2_Servlet
阅读原文时间:2023年07月08日阅读:1

一. 引言

1.1 C/S架构和B/S架构

  • C/S 和B/S是软件发展过程中出现的两种软件架构方式

1.2 C/S架构(Client/Server 客户端/服务器)

  • 特点: 必须在客户端安装特定软件

  • 优点: 图形效果显示较好(如: 3D游戏)

  • 缺点: 服务器的软件和功能进行升级, 客户端也必须升级

  • 常见的C/S程序: QQ, 微信等

    客 ---请求---> web服务器
    <---响应--- 户 ---请求---> 数据库服务器
    端 <---响应---

1.3 B/S架构(Browser/Server 浏览器/服务器)

  • 特点: 无需安装客户端, 任何浏览器都可直接访问

  • 优点: 设涉及到功能的升级, 只需要升级服务器端

  • 缺点: 图形显示效果不如C/S架构

  • 需要通过HTTP协议访问

    Browser ---请求---> Web 数据
    浏览器 <---响应--- 服务器 <---> 库

二. 服务器

2.1 概念

2.1.1 什么是Web
  • Web(World wide web)称为万维网, 简单理解就是网站, 它用来表示Internet主机上提供外界访问的资源
  • Internet上供外界访问的资源分为两大类
    • 静态资源: 指Web页面中供人们浏览的数据始终是不变的 (HTML, CSS)
    • 动态资源: 指Web页面中供人们浏览的数据是由程序产生的, 不同时间点, 甚至不同设备访问Web页面看到的内容各不相同 (JSP/Servlet)
    • 在Java中, 动态Web资源开发技术我们统称为Java Web
2.1.2 什么是Web服务器
  • Web服务器是运行及发布Web应用的容器, 只有将开发的Web项目放置到该容器中, 才能使网络中所有用户通过浏览器进行访问

2.2 常见服务器

  • 开源: OpenSource (1. 开放源代码 2. 免费)

    • Tomcat(主流Web服务器之一, 适合初学者)
    • jetty (淘宝, 运行效率比Tomcat高)
    • resin (新浪, 所有开源服务器软件中, 运行效率最高的)
    • 三者的用法从代码角度完全相同, 只有在开启, 关闭服务器软件时对应的命令稍有区别, 掌握一个即掌握所有
  • 收费

    • WebLogic (Oracle)
    • WebSphere (IBM)
    • 提供相应的服务与支持, 软件大, 耗资源

2.3 Tomcat服务器

  • Tomcat是Apache软件基金会 (Apache Software Foundation) 的Jakarta 项目中的一个核心项目, 免费开源, 并支持Servlet 和 JSP 规范. 目前Tomcat最新版本为9.0
  • Tomcat技术先进, 性能稳定, 深受Java爱好者喜爱并得到了部分软件开发商的认可, 成为目前比较流行的Web应用服务器

2.4 Tomcat安装

2.4.1 下载
2.4.2 解压安装
  • 将Tomcat解压到一个没有特殊字符的目录中 (一般纯英文即可)
  • 注意:
    • 不建议将服务器软件放在磁盘层次很多的文件夹
    • 不建议防在中文路径下
2.4.3 Tomcat目录说明
  • bin

    • 该目录下存放的是二进制可执行文件

      • startup.bat 启动Tomcat, shutdown.bat 停止Tomcat
  • conf

    • 这是一个非常重要的目录, 这个目录下有两个最为重要的文件server.xml和web.xml

      • server.xml: 配置整个服务器信息, 例如修改端口号, 编码格式等
      • web.xml: 项目部署描述符文件, 这个文件中注册了很多MIME类型, 即文档类型
  • lib

    • Tomcat的类库, 里面存放Tomcat运行所需要的jar文件
  • logs

    • 存放日志文件, 记录了Tomcat启动和关闭的信息, 如果启动Tomcat时有错误, 异常也会记录在日志文件中
  • temp

    • Tomcat的临时文件, 这个目录下的文件在停止Tomcat后删除
  • webapps

    • 存放web项目的目录, 其中每一个文件都是一个项目;
    • 其中ROOT是一个特殊的项目, 在地址栏中没有给出项目目录时, 对应的就是ROOT项目
  • work

    • 运行时生成的文件, 最终运行的文件都在这里

      • 当客户端用户访问一个JSP文件时, Tomcat会通过JSP生成Java文件, 然后再编译Java文件生成class文件, 生成的Java和class文件都会存放到这个目录下

2.5 Tomcat启动和停止

2.5.1 启动
  • 进入Tomcat安装目录bin下, 双击startup.bat启动程序
2.5.2 验证
  • 打开浏览器, 输入http://localhost:8080
  • 如果进入到Apache Tomcat版本信息页面证明启动成功
2.5.3 停止
  • 双击shutdown.bat即可关闭Tomcat启动窗口
2.5.4 修改端口号
  • Tomcat默认端口号为8080, 可以通过conf/server.xml文件修改

  • 注意: 修改端口号需要重新启动Tomcat才能生效

2.6 项目部署及访问静态资源

  • Tomcat是Web服务器, 我们的项目应用是部署在webapps下, 然后通过特定的URL访问
2.6.1 创建项目
  • 在webapps中建立文件夹(项目应用), 比如: myweb

    • 创建WEB-INF文件夹, 用于存放项目的核心内容

      • 创建classes, 用来存放.class文件
      • 创建lib, 用于存放jar文件
      • 创建web.xml, 项目配置文件 (到ROOT项目下的WEB-INF复制即可)
    • 把网页hello.html复制到myweb文件夹中, 与WEB-INF在同级目录

2.6.2 URL访问资源
2.6.3 Tomcat响应流程
Client(Browser)
http://localhost:8080/myweb/hello.html

|requests(请求)       |
|                    |
|                    |
|                    |response(响应)

                    Tomcat
                    webapps(存放所有项目)
                        MyWeb(项目)
                            WEB-INF(核心内容)
                                classes(.class文件)
                                lib(网站需要的jar包)
                            hello.html

2.7 常见错误

2.7.1 Tomcat控制台闪退
  • 闪退问题是由于JAVA_HOME配置导致的, 检查JAVA_HOME配置是否正确
2.7.2 404
  • 访问资源不存在, 出现404错误, 检查下输入的网址是否有有误, 有没有少加斜杠/

三. Servlet [重点]

3.1概念

  • Servlet: Server Applet的简称, 是服务器端的程序 (代码, 功能实现), 可交互式的处理客户端发送到服务器的请求, 并完成操作响应

  • 动态网页技术

  • JavaWeb程序开发的基础, JavaEE规范 (一套接口) 的一个组成部分

  • 用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:

  • 按照一种约定俗成的称呼习惯,通常我们把实现servlet接口或继承实现servlet接口的类的java程序,称之为Servlet

3.1.1 Servlet作用
  • 接收客户端请求, 完成操作
  • 动态生成网页 (页面数据可变)
  • 将包含操作结果的动态网页响应给客户端

3.2 Servlet开发步骤

3.2.1 搭建开发环境
  • 将Servlet相关jar包 (lib\servlet-api.jar) 配置到classpath中
3.2.2 编写Servlet
  • 实现javax.servlet.Servlet

  • 重写5个主要方法

  • 在核心的service() 方法中编写输出语句, 打印访问结果

    import javax.servlet.Servlet;
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.io.IOException;

    public class MyServlet implements Servlet {

    public void init(ServletConfig servletConfig) throws ServletException {
    
    }
    
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        System.out.println("My First Servlet");
    }
    
    public void destroy() {
    
    }
    
    public ServletConfig getServletConfig() {
        return null;
    }
    
    public String getServletInfo() {
        return null;
    }

    }

3.2.3 部署Servlet
  • 编译MyServlet后, 将生成的.class文件放在WEB-INF/classes文件中
3.2.4 配置Servlet
  • 编写WEB-INF下项目配置文件web.xml

    • Servlet-class: 如果有包名的话要写完整

      • 例: com.dz.servlet.MyServlet

      MyServlet MyServlet

      MyServlet /myservlet

  • 注意: url-pattern配置的内容就是浏览器地址栏输入的URL中项目名称后资源的内容

3.3 运行测试

3.4 常见错误

3.4.1 500错误
  • 服务端出现异常

四. IDEA创建Web项目

4.1 IDEA创建Web项目

  • 创建项目窗口点击Java Enterprise, 选择JavaEE7版本, 并勾选Web Application
  • 输入项目名称和保存位置, 点击Finish, 完成项目创建
  • web项目目录介绍
    • .idea: IDEA工程配置文件
    • src: 存放Java代码
    • web: 存放项目资源
      • WEB-INF: 存放项目配置文件(web.xml), Jar包(lib/servlet-api.jar), class文件(com.dz.servlet.MyServlet)
    • WebProject.iml: 模块配置文件
    • External Libraries: 外部Jar文件

4.2 IDEA开发Servlet

  • 使用开发工具编写Servlet, 仍要手工导入servlet-api.jar文件, 并与项目关联
  • 步骤:
    • 在WEB-INF下新建lib目录, 将servlet-api.jar复制到里面, 鼠标选中lib目录右键选择Add as Library
4.2.1 编写Servlet
  • 创建MyServlet, 实现Servlet接口, 覆盖5个方法
4.2.2 配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--1. 添加servlet节点-->
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.dz.servlet.MyServlet</servlet-class>
    </servlet>
    <!--2. 添加servlet-mapping节点-->
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>

</web-app>
4.2.3 部署Web项目
  • 在Tomcat的webapps目录下, 新建WebProject文件夹

  • 问题: 每当我们编写了新的Servlet或者重新编译, 都需要手工将新的.class部署到Tomcat中, 较为麻烦, 如何实现自动部署?

4.3 IDEA部署Web项目

  • 前面我们是在Tomcat的webapps目录下新建应用程序目录myweb, 然后把静态资源和Servlet复制到相关目录下. 若使用IDEA则不需要我们复制了, 可以通过IDEA集成Tomcat服务器, 实现自动部署
4.3.1 IDEA集成Tomcat
  1. 点击File选项, 选择Settings
  2. 选择Build, Execution, Deployment下的Application Servers
  3. 点击+号, 选择Tomcat Server
  4. 选择Tomcat安装目录, 点击OK即可
  5. 最后点击OK
4.3.2 项目部署Tomcat
  1. 点击Run下的 Edit Configuration
  2. 点击+号, 选择Tomcat Server, 选择Local
  3. 选择Deployment, 点击+号, 选择Artifact, 添加当前项目
  4. 点击运行按钮, 即可运行项目

4.4 其他操作

4.4.1 关联第三方jar包

方法一:

  1. 在WEB-INF 目录下新建lib目录
  2. 复制jar包到lib目录中
  3. 右击lib目录, 选择Add as Library
  4. 选择Project Library, 完成
    • Clobal Library 表示所有工程都可以使用
    • Project Library 表示当前工程中所有模块都可以使用
    • Module Library 表示当前模块可以使用

方法二[推荐]:

  1. 点击File下的Project Structure
  2. 点击Project Settings下的Libraries
  3. 点击+号, 选择java, 然后继续选择jar包所在的目录, 点击OK即可
4.4.2 如何导出war包
  • 项目完成后, 有时候需要打成war包方便部署. war包可以直接放入Tomcat的webapps目录中, 启动Tomcat后自动解压, 即可访问
  1. 点击项目结构Project Structure(IDEA图标那一排的倒数第三个图标, 或者File下可直接找到)
  2. 选择Artifacts, 点击+号
  3. 选择Web Application: Archive--->For…
  4. 点击Build下的Build Artifacts, 选择WebProject: war--->build构建项目
  5. 在out\artifacts\目录中, 查看生产的war包, 把war包放入Tomcat的webapps目录, 启动Tomcat自动解压即可访问

五. HTTP协议

5.1 什么是HTTP

  • 超文本传输协议 (HTTP, Hyper Text Transfer Protocol) 是互联网上应用最为广泛的一种网络协议, 是一个基于请求与响应模式的, 无状态的, 应用层的协议, 运行于TCP协议基础之上

5.2 HTTP协议特点

  • 支持客户端(浏览器) / 服务器模式

  • 简单快递: 客户端只向服务器发送请求方法和路径, 服务器即可响应数据, 因而通信速度很快. 请求方法常用的有GET, POST等

  • 灵活: HTTP允许传输任意类型的数据, 传输的数据类型由Content-Type标识

  • 无连接: 无连接指的是每次TCP连接只处理一个或多个请求, 服务器处理完客户的请求后, 即断开连接, 采用这种方式可以节省传输时间

    • HTTP1.0版本是一个请求响应之后, 直接就断开了, 称为短连接
    • HTTP1.1版本不是响应后直接断开, 而是等几秒钟, 这几秒中之内有新的请求, 那么还是通过之前的连接通道来收发消息, 如果过了这几秒钟用户没有发送新的请求, 就会断开连接, 称为长连接
  • 无状态: HTTP协议是无状态协议

    • 无状态指协议对于事务处理没有记忆能力

5.3 HTTP协议通信流程

  • 客户与服务器建立连接 (三次握手)
  • 客户向服务器发送请求
  • 服务器接收请求, 并根据请求返回相应的文件作为应答
  • 客户与服务器关闭连接 (四次挥手)

5.4 请求报文和响应报文 [了解]

5.4.1 HTTP请求报文
  • 当浏览器向web服务器发出请求时, 它向服务器传递了一个数据块, 也就是请求信息(请求报文), HTTP请求信息由4部分组成
5.4.2 HTTP响应报文
  • HTTP响应报文与HTTP请求报文相似, HTTP响应也由4个部分组成
5..4.3 常见状态码
  • 200 OK 客户端请求成功
  • 302 Found 临时重定向
  • 403 Forbidden 服务器收到请求, 但是拒绝提供服务. 服务器通常会在响应正文中给出不提供服务的原因
  • 404 Not Found 请求的资源不存在, 例如: 输入了错误的URL
  • 500 Internal Server Error 服务器发生不可预期的错误, 导致无法完成客户端的请求

六. Servlet详解[重点]

6.1 Servlet接口

  • 在Servlet API中最重要的是Servlet接口, 所有Servlet都会直接或间接的与该接口发生联系, 或是直接实现该接口, 或间接继承自实现了该接口的类, 该接口包括以下5个方法:

    • void init(ServletConfig config)
    • ServletConfig getServletConfig()
    • void service(ServletRequest req, ServletResponse res)
    • String getServletInfo()
    • void destroy()
6.1.2 GenericServlet抽象类
  • GenericServlet使编写Servlet变得更容易, 它提供声明周期方法 init 和 destroy 的简单实现, 要编写一般的Servlet, 只需重写抽象Service 方法即可

    public class GenServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    System.out.println("继承了GenericServlet抽象类!");
    }
    }

6.1.3 HttpServlet类
  • HttpServlet是继承GenericServlet的基础上进一步的扩展. 提供将要被子类化以创建适用于Web 站点的Http servlet 的抽象类
  • HttpServlet的子类必须至少重写一个方法, 该方法通常是以下这些方法之一:
    • doGet: 如果servlet 支持 HTTP GET请求
    • doPost: 用于HTTP POST请求
    • doPut: 用于HTTP PUT请求
    • doDelete: 用于HTTP DELETE请求

6.2 Servlet两种创建方式

6.2.1 实现接口Servlet
public class MyServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("My First Web Project!");
        System.out.println(new Date());
        System.out.println("成功导出war包!");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}
  • 该方式比较麻烦, 需要实现接口中所有方法
6.2.2 继承HttpServlet (推荐)
public class HttpsServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("这是get请求的内容!");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("这是post请求的内容!");
    }
}
6.2.3 常见错误
  • HTTP Status 404 资源找不到

    • 第一种情况:地址书写有误
    • 第二种情况: 地址没有问题, 把IDEA项目中out目录删除, 然后重新运行
  • Servlet地址配置重复

    • both mapped to the url-pattern [/helloservlet] which is not permitted
  • Servlet地址配置错误

    • 比如没有写/ Invalid[helloservlet2] in servlet mapping

6.3 Servlet两种配置方式

6.3.1 使用web.xml (Servlet2.5之前使用, 通用配置,各版本支持)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <!--Servlet配置-->
    <servlet>
        <!--名称-->
        <servlet-name>MyServlet</servlet-name>
        <!--Servlet的全程类名-->
        <servlet-class>com.dz.servlet.MyServlet</servlet-class>
        <!--启动的优先级, 数字越小越先起作用-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--映射配置-->
    <servlet-mapping>
        <!--名称-->
        <servlet-name>MyServlet</servlet-name>
        <!--资源的匹配规则:精确匹配-->
        <url-pattern>/ms</url-pattern>
    </servlet-mapping>

</web-app>
6.3.2 配置属性
url-pattern定义匹配规则, 取值说明:
精确匹配    /具体的名称  只有url路径是具体的名称时才会触发Servlet
后缀匹配    *.xxx   只要是以xxx结尾的就匹配触发Servlet
通配符匹配    /*  匹配所有请求, 包含服务器的所有资源
通配符匹配    /   匹配所有请求, 包含服务器的所有资源, 不包括.jsp

load-on-startup
1. 元素标记容器是否应该在web应用程序启动时就加载这个servlet
2. 它的值必须是一个整数, 表示servlet被加载的先后顺序
3. 如果该元素的值为负数或者没有设置, 则容器会认为Servlet被请求时再加载
4. 如果值为整数或者0时, 表示容器在应用启动时就加载并初始化这个servlet, 值越小, servlet的优先级越高, 就越先被加载, 值相同时, 容器就会自己选择顺序来加载
6.3.3 使用注解 (Servlet3.0后支持, 推荐)
@WebServlet(value = {"/bs", "/bss"},loadOnStartup = 0)
public class BasicServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("这是GET!");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("这是POST!");
    }
}
6.3.4 @WebServer注解常用属性
  • name: Servlet名字 (可选)
  • value: 配置url路径, 可以配置多个
  • urlPatterns: 配置url路径, 和value作用一样, 不能同时使用
  • loadOnStartup: 配置Servlet的创建时机, 如果是0或者正数则启动时创建, 如果是负数则访问时创建. 数字越小优先级越高

七. Servlet应用

7.1 request对象

  • 在Servlet中用来处理客户端请求需要用doGet或doPost方法的request对象
  • request对象, response对象作为参数传递给doGet, doPost方法
7.1.1 get和post的区别
  • get请求

    • get提交的数据会放在URL之后, 以?分隔URL和传输数据, 参数之间以&相连
    • get方式以明文传递 数据量小, 不安全
    • 效率高, 浏览器默认请求方式为GET请求
    • 对应的Servlet的方法是doGet
  • post请求

    • post方法是把提交的数据放在HTTP包的Body中
    • 密文传递数据, 数据量大, 安全
    • 效率相对没有GET高
    • 对应的Servlet的方法是doPost
7.1.2 request主要方法
  • String getParameter(String name)

    • 根据表单组件名称获取提交数据
  • void setCharacterEncoding(String charset)

    • 指定每个请求的编码
7.1.3 request应用
  • HTML页面



    注册页面


  • Servlet代码

    @WebServlet(value = {"/rs"})
    public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1. 获取用户请求发送的数据
    String username = req.getParameter("username");
    String password = req.getParameter("password");

        System.out.println("提交的数据: " + username + "\t" + password );
    }

    }

7.1.4 get请求收参问题
  • 产生乱码是因为服务器和客户端沟通的编码不一致造成的, 因此解决的办法是: 在客户端和服务器之间设置一个统一的编码, 之后就按照此编码进行数据的传输和接收
7.1.5 get中文乱码
  • 在Tomcat7及以下版本, 客户端以UTF-8的编码传输数据到服务器, 而服务器端的request对象使用的是ISO8859-1 这个字符编码来接收数据, 服务器和客户端沟通的编码不一致因此才会产生中文乱码的

    • 解决办法: 在接收到消息数据后, 先获取request对象以ISO8859-1 字符编码接收到的原始数据的字节数组, 然后通过字节数组以指定的编码构建字符串, 解决乱码问题
    • Tomcat8的版本中get方式不会出现乱码了, 因为服务器对url的编码格式可以进行自动转换

解决get中文乱码的代码

@WebServlet(value = {"/rs"})
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1. 获取用户请求发送的数据
        String username = req.getParameter("username");
        username = new String(username.getBytes("ISO8859-1"),"UTF-8");
        String password = req.getParameter("password");

        System.out.println("提交的数据: " + username + "\t" + password );
    }
}
7.1.6 post中文乱码
  • 由于客户端是以UTF-8字符编码将表单数据传输到服务器的, 因此服务器也需要设置以UTF-8字符编码进行接收

    • 解决方案: 使用从ServletRequest接口继承而来的setCharacterEncoding(charset)方法进行通一的编码设置

    @WebServlet(value = {"/rs"})
    public class RegisterServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //设置POST请求的统一编码格式为UTF-8
    req.setCharacterEncoding("UTF-8");
    //1. 获取用户请求发送的数据
    String username = req.getParameter("username");
    String password = req.getParameter("password");

        System.out.println("提交的数据: " + username + "\t" + password );
    }

    }

7.2 response对象

  • response对象用于响应客户请求并向客户端输出信息
7.2.1 response主要方法
  • setHeader(name, value)

    • 设置响应信息头
  • setContentType(String)

    • 设置响应文件类型, 响应式的编码格式
  • setCharacterEncoding(String)

    • 设置服务端响应内容编码格式
  • getWriter()

    • 获取字符输出流
7.2.2 response应用
//获取请求参数的代码此处省略
//获取输出流
PrintWriter printWriter = resp.getWriter();
printWriter.println("register success!!");
  • 如果输出内容包括中文, 则会出现乱码, 因为服务器默认采用ISO8859-1 编码响应内容
7.2.3 解决输出中文乱码
  • 设置服务器端响应的编码格式

  • 设置客户端响应内容的头内容的文件类型及编码格式

    //设置POST响应的统一编码格式为UTF-8
    resp.setCharacterEncoding("UTF-8");
    resp.setHeader("Content-type", "text/html;charset=UTF-8");

  • 不推荐

  • 同时设置服务端的编码格式和客户端响应的文件类型及响应时的编码格式

    resp.setContentType("text/html;charset=UTF-8");

  • 推荐

7.3 综合案例(Servlet + JDBC)

  • 要求:实现登陆功能, 展示所有用户功能

步骤:

  • MySQL数据库中

    • 创建用户表admin
  • Servlet项目中

    • 将所需的jar包导入到web---WEB-INF---lib中

      • mysql-connector-java-5.1.25-bin.jar
      • commons-dbutils-1.7.jar
      • druid-1.1.5.jar
      • servlet-api.jar
    • src下创建database.properties

      • 里面是连接MySQL数据库和Druid连接池的配置信息
    • src下创建com.dz.servletProject目录,在此目录下分别有以下操作

      • utils(工具类)目录下创建DbUtils工具类
      • entity(实体类)目录下创建Admin用户实体类
      • dao(数据库访问层)目录下创建AdminDao接口以及impl目录, impl目录下创建AdminDao接口的实现类AdminDaoImpl
      • service(逻辑业务层)目录下创建AdminService接口以及impl目录, impl目录下创建AdminService接口的实现类AdminServiceImpl
      • servlet(服务器端的java程序)目录下创建LoginServlet用户登陆类和ShowAllAdminServlet展示所有用户信息类
      • web目录下创建login.html文件

以下是部分代码

7.3.1 数据库
CREATE TABLE admin(
    username VARCHAR(20) PRIMARY KEY NOT NULL,
    PASSWORD VARCHAR(20) NOT NULL,
    phone VARCHAR(11) NOT NULL,
    address VARCHAR(20) NOT NULL
)CHARSET=utf8;

SELECT * FROM admin;

INSERT INTO admin(username,PASSWORD,phone,address)
VALUE('张三','123456','12345678901','地球');
INSERT INTO admin(username,PASSWORD,phone,address)
VALUE('李四','123456','12345678902','地球');
7.3.2 database.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=UTF-8
username=root
password=root
initialSize=10
maxActive=20
minIdle=5
maxWait=3000
7.3.3 DbUtils
public class DbUtils {
    private static DruidDataSource ds;
    private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();

    static {
        Properties properties = new Properties();
        InputStream inputStream = DbUtils.class.getResourceAsStream("/database.properties");
        try {
            properties.load(inputStream);
            ds = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection(){
        Connection conn = THREAD_LOCAL.get();
        try {
            if (conn == null) {
                conn = ds.getConnection();
                THREAD_LOCAL.set(conn);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return conn;
    }

    public static void begin() {
        Connection conn = null;
        try {
            conn = getConnection();
            conn.setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public static void commit() {
        Connection conn = null;
        try {
            conn = getConnection();
            conn.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            closeAll(conn,null,null);
        }
    }

    public static void rollback() {
        Connection conn = null;
        try {
            conn = getConnection();
            conn.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            closeAll(conn,null,null);
        }
    }

    public static void closeAll(Connection conn, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (conn != null) {
                conn.close();
                THREAD_LOCAL.remove();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

}
7.3.4 Admin
public class Admin {
    private String username;
    private String password;
    private String phone;
    private String address;

    public Admin() {
    }

    public Admin(String username, String password, String phone, String address) {
        this.username = username;
        this.password = password;
        this.phone = phone;
        this.address = address;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Admin{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", phone='" + phone + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
7.3.5 AdminDao
public interface AdminDao {
    int insert(Admin admin);
    int delete(String username);
    int update(Admin admin);
    Admin select(String username);
    List<Admin> selectAll();
}
7.3.6 AdminDaoImpl
public class AdminDaoImpl implements AdminDao {
    private QueryRunner queryRunner = new QueryRunner();
    @Override
    public int insert(Admin admin) {
        return 0;
    }

    @Override
    public int delete(String username) {
        return 0;
    }

    @Override
    public int update(Admin admin) {
        return 0;
    }

    @Override
    public Admin select(String username) {
        try {
            Admin admin = queryRunner.query(DbUtils.getConnection(), "select * from admin where username=?", new BeanHandler<Admin>(Admin.class), username);
            return admin;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    @Override
    public List<Admin> selectAll() {
        try {
            List<Admin> adminList = queryRunner.query(DbUtils.getConnection(), "select * from admin", new BeanListHandler<Admin>(Admin.class));
            return adminList;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
}
7.3.7 AdminService
public interface AdminService {
    Admin login(String username, String password);
    List<Admin> showALlAdmin();
}
7.3.8 AdminServiceImpl
public class AdminServiceImpl implements AdminService {
    private AdminDao adminDao = new AdminDaoImpl();
    @Override
    public Admin login(String username, String password) {
        Admin result = null;
        try {
            DbUtils.begin();
            Admin admin = adminDao.select(username);
            if (admin != null) {
                if (admin.getPassword().equals(password)) {
                    result = admin;
                }
            }
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public List<Admin> showALlAdmin() {
        List<Admin> adminList = null;
        try {
            DbUtils.begin();
            adminList = adminDao.selectAll();
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return adminList;
    }
}
7.3.9 LoginServlet
@WebServlet(value = "/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置统一编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        //1. 收参
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //2. 调用业务逻辑
        AdminService adminService = new AdminServiceImpl();
        Admin admin = adminService.login(username, password);
        //3. 处理结果
        PrintWriter printWriter = resp.getWriter();
        if (admin != null) {
            //响应给客户端一个结果页面, 显示登陆成功
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<meta charset='UTF-8'>");
            printWriter.println("<title>结果页面</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<h1>登陆成功!</h1>");
            printWriter.println("</body>");
            printWriter.println("</html>");
        }else {
            //响应给客户端一个结果页面, 显示登陆失败
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<meta charset='UTF-8'>");
            printWriter.println("<title>结果页面</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<h1>登陆失败!</h1>");
            printWriter.println("</body>");
            printWriter.println("</html>");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
7.3.10 ShowAllAdminServlet
@WebServlet(value = "/showAll")
public class ShowAllAdminServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        AdminService adminService = new AdminServiceImpl();
        List<Admin> adminList = adminService.showALlAdmin();

        PrintWriter printWriter = resp.getWriter();
        if (adminList != null) {
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<meta charset='UTF-8'>");
            printWriter.println("<title>展示所有用户</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<table border='1'>");
            printWriter.println("<tr>");
            printWriter.println("   <th>username</th>");
            printWriter.println("   <th>password</th>");
            printWriter.println("   <th>phone</th>");
            printWriter.println("   <th>address</th>");
            printWriter.println("</tr>");
            for (Admin admin : adminList) {
                printWriter.println("<tr>");
                printWriter.println("   <td>"+admin.getUsername()+"</td>");
                printWriter.println("   <td>"+admin.getPassword()+"</td>");
                printWriter.println("   <td>"+admin.getPhone()+"</td>");
                printWriter.println("   <td>"+admin.getAddress()+"</td>");
                printWriter.println("</tr>");
            }
            printWriter.println("</table>");
            printWriter.println("</body>");
            printWriter.println("</html>");
        }else {
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<meta charset='UTF-8'>");
            printWriter.println("<title>展示所有用户</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<h3>当前没有用户!</h3>");
            printWriter.println("</body>");
            printWriter.println("</html>");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
7.3.11 login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆页面</title>
</head>
<body>
<form action="/WebProject_war_exploded/login" method="post">
    <p>
        <label for="1">用户名:</label>
        <input type="text" name="username" id="1">
    </p>
    <p>
        <label for="2">密码:&nbsp;&nbsp;&nbsp;</label>
        <input type="password" name="password" id="2">
    </p>
    <p>
        <input type="submit" value="登陆">
    </p>
</form>
</body>
</html>

八. 转发与重定向

8.1 现有问题

  • 在之前案例中, 调用业务逻辑和显示结果页面都在同一个Servlet中, 就会产生设计问题

    • 不符合单一职能原则, 各司其职的思想
    • 不利于后期的维护
  • 应该将业务逻辑和显示结果分离开

    • XxxController extends HttpServlet{
      //调用业务逻辑
      }

    • XXXJSP extends HttpServlet{
      //显示页面结果
      }

8.1.1 业务, 显示分离
  • 问题: 业务逻辑和显示结果分离后, 如何跳转到显示结果的Servlet?
  • 业务逻辑得到的数据结果如何传递给显示结果的Servlet?

8.2 转发

  • 转发的作用在服务器端, 将请求发送给服务器上的其他资源, 以共同完成一次请求的处理
8.2.1 页面跳转
  • 调用业务逻辑的Server中, 编写以下代码

    • request.getRequestDispatcher("/目标URL-pattern").forward(request, response);
  • 使用forward跳转时, 是在服务器内部跳转, 地址栏不发生变化, 属于同一次请求

8.2.2 数据传递
  • forward表示一次请求, 是在服务器内部跳转, 可以共享同一次request作用域中的数据

    • request作用域: 拥有存储数据的空间, 作用范围是一次请求有效(一次请求可以经过多次转发)

      • 可以将数据存入request后, 在一次请求过程中的任何位置进行获取
      • 可传递任何数据(基本数据类型, 对象, 数组, 集合等)
    • 存数据: request.setAttribute(key, value);

      • 以键值对形式存储在request作用域中.key为String类型 , value为Object类型
    • 取数据: request.getAttribute(key);

      • 通过String类型的key访问Object类型的value

调用业务逻辑

@WebServlet(value = "/showAllController")
public class ShowAllAdminController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //只负责调用业务逻辑功能
        AdminService adminService = new AdminServiceImpl();
        List<Admin> adminList = adminService.showALlAdmin();
        //request作用域存储数据
        req.setAttribute("adminList", adminList);
        //通过转发, 跳转到显示结果的servlet
        req.getRequestDispatcher("/showAllJSP").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

显示

@WebServlet(value = "/showAllJSP")
public class ShowAllAdminJSP extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        List<Admin> adminList = (List)req.getAttribute("adminList");
        PrintWriter printWriter = resp.getWriter();
        if (adminList != null) {
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<meta charset='UTF-8'>");
            printWriter.println("<title>展示所有用户</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<table border='1'>");
            printWriter.println("<tr>");
            printWriter.println("   <th>username</th>");
            printWriter.println("   <th>password</th>");
            printWriter.println("   <th>phone</th>");
            printWriter.println("   <th>address</th>");
            printWriter.println("</tr>");
            for (Admin admin : adminList) {
                printWriter.println("<tr>");
                printWriter.println("   <td>"+admin.getUsername()+"</td>");
                printWriter.println("   <td>"+admin.getPassword()+"</td>");
                printWriter.println("   <td>"+admin.getPhone()+"</td>");
                printWriter.println("   <td>"+admin.getAddress()+"</td>");
                printWriter.println("</tr>");
            }
            printWriter.println("</table>");
            printWriter.println("</body>");
            printWriter.println("</html>");
        }else {
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<meta charset='UTF-8'>");
            printWriter.println("<title>展示所有用户</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<h3>当前没有用户!</h3>");
            printWriter.println("</body>");
            printWriter.println("</html>");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}
8.2.3 转发特点
  • 转发是服务器行为
  • 转发是浏览器只做了一次访问请求
  • 转发浏览器地址不变
  • 转发两次跳转之间存储的信息不会丢失, 所以可以通过request进行数据的传递
  • 转发只能将请求转发给同一个Web应用中的组件

8.3 重定向

  • 重定向作用在客户端, 客户端将请求发送给服务器后, 服务器响应给客户端一个新的请求地址, 客户端重新发送请求
8.3.1 页面跳转
  • 在调用业务逻辑的Servlet中, 编写以下代码

    • response.sendRedirect("目标URI");
  • URI统一资源标识符(Uniform Resource Identifier), 用来表示服务器中定位一个资源, 资源在web项目中的路径(/project/source)

  • 使用redirect跳转时, 是在客户端跳转, 地址栏发生变化, 属于多次请求

8.3.2 数据传递
  • sendRedirect跳转时, 地址栏改变, 代表客户端重新发送的请求. 属于两次请求

    • response没有作用域, 两次request中的数据无法共享
    • 传递数据:通过URL的拼接进行数据传递("/WebProject/b?username=tom");
    • 获取数据: request.getParameter("username");
8.3.3 重定向特点
  • 重定向是客户端行为
  • 重定向是浏览器做了至少两次的访问请求
  • 重定向浏览器地址改变
  • 重定向两次跳转之间传输的信息会丢失(request范围)
  • 重定向可以指向任何资源, 包括当前应用程序中的其他资源, 同一个站点上的其他应用程序中的资源, 其他站点的资源

8.4 转发, 重定向总结

  • 当两个Servlet需要传递数据时, 选择forward转发, 不建议使用sendRedirect进行传递

九. Servlet生命周期

9.1.1 实例化
  • 当用户第一次访问Servlet时, 由容器(Tomcat服务器)调用Servlet的构造器创建具体的Servlet对象. 也可以在容器启动之后立刻创建实例. 使用如下代码可以设置Servlet是否在服务器启动时就创建

    • < load-on-startup>1< /load-on-startup>

      • 数字为0或者正数: 容器启动时创建,数字越小, 优先级越高
      • 数字为负数或者不写这条语句: 被访问时创建
    • 注意: 只执行一次

9.1.2 初始化
  • 在初始化阶段, init()方法会被调用, 这个方法在javax.servlet.Servlet接口中定义. 其中, 方法以一个ServletConfig类型的对象作为参数
  • 注意: init()方法只被执行一次
9.1.3 服务
  • 当客户端有一个请求时, 容器就会将请求ServletRequest与响应ServletResponse对象转给Servlet, 以参数的形式传给service 方法

    • 此方法会执行多次
9.1.4 销毁
  • 当Servlet容器(Tomcat服务器)停止或重新启动都会引起销毁Servlet对象并调用destroy方法

    • destroy方法只执行一次
9.1.5 Servlet执行流程
  1. 浏览器发送请求
  2. 容器(Tomcat服务器)解析请求
  3. 创建Servlet实例
  4. 调用init()
  5. 调用service()
  6. 输出响应
  7. 容器向浏览器发送响应
  8. 容器调用destroy()

十. Servlet特性

10.1 线程安全问题

  • Servlet在访问之后, 会执行实例化操作. 创建一个Servlet对象, 而我们Tomcat容器可以同时多个线程并发访问同一个Servlet, 如果在方法中对成员变量做修改操作, 就会有线程安全的问题

10.2 如何保证线程安全

  • synchronized

    • 将存在线程安全问题的代码放到同步代码块中
  • 实现SingleThreadModel接口

    • servlet实现SingleThreadModel接口后, 每个线程都会创建servlet实例, 这样每个客户端请求就不存在共享资源的问题, 但是servlet响应客户端请求的效率太低, 所以已经淘汰
  • 尽可能使用局部变量

    @WebServlet(value = "/ss")
    public class SafeServlet extends HttpsServlet {
    //private String message = "";
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //假设1. 接收参数
    //假设2. 调用业务逻辑, 得到登陆结果

        //尽量使用局部变量可以避免线程安全问题
        String message = "登陆成功!";//登陆失败!
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter printWriter = resp.getWriter();
        printWriter.println(message);
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    }

十一. 状态管理

11.1 现有问题

  • HTTP协议是无状态的, 不能保存每次提交的信息
  • 如果用户发来一个新的请求, 服务器无法知道它是否与上次的请求有联系
  • 对于那些需要多次提交数据才能完成的Web操作, 比如登陆来说, 就成问题了

11.2 概念

  • 将浏览器与web服务器之间多次交互当做一个整体来处理, 并且将多次交互所涉及的数据(即状态) 保存下来

11.3 状态管理分类

  • 客户端状态管理技术: 将状态保存在客户端. 代表性的是Cookie技术
  • 服务器状态管理技术: 将状态保存在服务器端,. 代表性的是session技术 (服务器传递sessionID时需要使用Cookie的方式)

十二. Cookie的使用

12.1 什么是Cookie

  • Cookie是在浏览器访问Web服务器的某个资源时, 由Web服务器在HTTP响应消息头中附带传送给浏览器的一小段数据

  • 一旦Web浏览器保存了某个Cookie, 那么它在以后每次访问该该Web服务器时, 都应在HTTP请求头中将这个Cookie回传给Web服务器

  • 一个Cookie主要由标识该信息的名称(name) 和 值(value)组成

    Client

    //1. 访问登陆Servlet
    http://localhost:8080/web/ls
    //2. 接收ls响应的cookie, 存储
    //3. 访问显示Servlet, 携带Cookie

    Server

    Client首次请求Server
    LoginServlet extends HttpServlet{
    //创建Cookie对象(键值对)
    //通过response将Cookie响应给Client
    }
    Client再次请求Server
    ShowServlet extends HttpServlet{
    //从请求中查找Cookie, 存在就获取
    }

12.2 创建Cookie

@WebServlet(value = "/cs")
public class CookieServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1. 服务端创建Cookie对象
        Cookie cookie = new Cookie("username","dz");

        //1.1. 设置Cookie的路径
        cookie.setPath("/WebProject_war_exploded/get");
        //1.2 设置Cookie的有效期
        cookie.setMaxAge(60*60);//内存存储, 取值有3种, >0有效期: 单位秒, =0: 浏览器关闭, <0内存存储: 默认-1
        //2. 将Cookie响应给客户端
        resp.addCookie(cookie);
        //注意: 有效路径: 当前访问资源的上一级目录, 不带主机名
    }
}

12.3 获取Cookie

@WebServlet(value = "/get")
public class GetServlet extends HttpsServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1. 通过request获取所有的cookie
        Cookie[] cookies = req.getCookies();
        //2. 通过循环遍历Cookie
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                //检索出自己的Cookie
                if (cookie.getName().equals("username")) {
                    //记录Cookie的值
                    String value = cookie.getValue();
                    System.out.println(cookie.getName() + ":" + value);
                    break;
                }
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

12.4 修改Cookie

  • 只需要保证Cookie的名称和路径一致即可修改

        Cookie cookie2 = new Cookie("username","dz2");
    cookie2.setPath("/WebProject_war_exploded/get");
    
    cookie2.setMaxAge(60*60*24*7);
    
    resp.addCookie(cookie2);</code></pre></li>
  • 注意: 如果改变Cookie的name和有效路径会新建Cookie, 而改变Cookie的值,和有效期会覆盖原有Cookie

12.5 Cookie编码与解码

  • Cookie默认不支持中文, 只能包含ASCII字符, 所以Cookie需要对Unicode字符进行编码, 否则会出现乱码

    • 编码可以使用java.net.URLEncoder类的encode(String str, String encoding) 方法
    • 解码使用java.net.URLDecoder类的decode(String str, String encoding) 方法
12.5.1 创建带中文Cookie
        //使用中文的Cookie  name和value都使用UTF-8编码
        Cookie cookie = new Cookie(URLEncoder.encode("姓名","UTF-8"),URLEncoder.encode("张三","UTF-8"));
        cookie.setPath("/WebProject_war_exploded/get");
        cookie.setMaxAge(60*60);
        //发送到客户端
        resp.addCookie(cookie);
12.5.2 读取带中文Cookie
        //1. 通过request获取所有的cookie
        Cookie[] cookies = req.getCookies();
        //2. 通过循环遍历Cookie
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                String cookieName = URLDecoder.decode(cookie.getName(),"UTF-8");
                String cookieValue = URLDecoder.decode(cookie.getValue(),"UTF-8");
                System.out.println(cookieName + ":" + cookieValue);
            }
        }

12.6 Cookie优点和缺点

12.6.1 优点
  • 可配置到期规则
  • 简单性: Cookie是一种基于文本的轻量结构, 包含简单的键值对
  • 数据持久性: Cookie默认在过期之前是可以一直存在客户端浏览器上的
12.6.2 缺点
  • 大小受到限制: 大多数浏览器对Cookie的大小有4K, 8K字节的限制
  • 用户配置为禁用: 有些用户禁用了对浏览器或客户端设备接收Cookie的能力, 因此限制了这一功能
  • 潜在的安全风险: Cookie可能会被篡改. 会对安全性造成潜在危险或者导致依赖于Cookie的应用程序失败

十三. Session对象[重点]

13.1 Session概述

  • Session用于记录用户的状态. Session指的是在一段时间内, 单个客户端与Web服务器的一连串相关的交互过程
  • 在一个Session中, 客户可能会多次请求访问同一个资源, 也有可能请求访问各种不同的服务器资源

13.2 Session原理

  • 服务器会给每一次会话分配一个Session对象
  • 同一个浏览器发起的多次请求, 同属于一次会话(Session)
  • 首次使用到Session时, 服务器会自动创建Session, 并创建Cookie存储SessionId发送回客户端
  • 注意: session是服务端创建的

13.3 Session使用

  • Session作用域: 拥有存储数据的空间, 作用范围是一次会话有效

    • 一次会话是使用同一浏览器发送的多次请求. 一旦浏览器关闭, 则结束会话
    • 可以将数据存入Session中, 在一次会话的任意位置进行获取
    • 可传递任何数据(基本数据类型, 数组, 对象, 集合)
13.3.1 获取Session
  • session是服务器自动创建的, 通过request对象获取

    //获取Session
    HTTPSession session = request.getSession();
    System.out.println("Id: " + session.getId());//唯一标记

13.3.2 Session保存数据
  • setAttribute(属性名,Object) 保存数据到session中

    session.setAttribute("key", value);//以键值对形式存储在Session中

13.3.3 Session获取数据
  • getAttribute(属性名); 获取session中的数据

    session.getAttribute("key");//通过String类型的key访问Object类型的value

13.3.4 Session移除数据
  • removeAttribute(属性名); 从session中删除数据

    session.removeAttribute("key");//通过键移除session作用域中的值

13.4 Session与Request应用区别

  • request是一次请求有效, 请求改变, 则request改变
  • session是一次会话有效, 浏览器改变, 则session改变
13.4.1 Session应用
@WebServlet(name = "SessionServlet", value = "/sss")
public class SessionServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 通过request对象获取Session对象
        HttpSession session = request.getSession();

        //2. 使用session保存数据
        session.setAttribute("username","dz");
        //request是一次请求有效, 在重定向后属于第二次请求, 就失效了
        request.setAttribute("password","1234");

        response.sendRedirect("/WebProject_war_exploded/getValue");//重定向跳转到getValue下
        System.out.println(session.getId());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
13.4.2 GetValueServlet.java
@WebServlet(name = "GetValueServlet",value = "/getValue")
public class GetValueServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 通过request获取session对象
        HttpSession session = request.getSession();

        String s = (String)session.getAttribute("username");
        String password = (String)request.getAttribute("password");

        System.out.println("从session中获得了: " + s);//从session中获得了: dz
        System.out.println("从request中获得了: " + password);//从request中获得了: null
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

13.5 Session的生命周期

  • 开始: 第一次使用到Session的请求产生, 则创建Session
  • 结束:
    • 浏览器关闭, 则失效
    • Session超时, 则失效
      • session.setMaxInactiveInterval(seconds);//设置最大有效时间(单位: 秒)
    • 手工销毁, 则失效
      • session.invalidate();//登陆退出, 注销
13.5.1 Session失效
session.setMaxInactiveInterval(60*60);//设置session最大有效期为一小时

session.invalidate();//手工销毁

13.6 浏览器禁用Cookie解决方案[了解]

13.6.1 浏览器禁用Cookie的后果
  • 服务器在默认情况下, 会使用Cookie的方式将sessionID发送给浏览器, 如果用户禁止Cookie, 则sessionID不会被浏览器保存, 此时, 服务器可以使用如URL重写这样的方式来发送sessionID
13.6.2 URL重写
  • 浏览器在访问服务器上的某个地址时, 不再使用原来的那个地址, 而是使用改写过的地址 (即在原来的地址后面加上了sessionID)
13.6.3 实现URL重写
  • response.encodeRedirectURL(String url)生成重写的URL

    HttpSession session = request.getSession();
    //重写URL追加sessionID
    String newUrl = response.encodeRedirectURL("/WebProject_war_exploded/gets");
    response.sendRedirect(newUrl);

13.7 Session实现权限验证

  • 在7.3的Servlet+JDBC综合案例的基础下进行升级
  • 现在要求:
    • 增加管理员表, 只有管理员通过登陆才能查看所有用户信息
    • 若在网址栏中直接访问用户信息网址, 则跳转到管理员登陆界面
    • 管理员在登陆成功后, 跳转到用户信息页面, 且在页面最上方显示"欢迎您,xxx管理员"字样
  • 提示: 需要把管理员Mgr和用户列表adminList放入session中存储, 这样可以在程序任何地方通过session.getAttribute方法进行调用
13.7.1 创建管理员表
CREATE TABLE manager(
    username VARCHAR(20) PRIMARY KEY,
    PASSWORD VARCHAR(20) NOT NULL
)CHARSET=utf8;
  • 根据管理员表创建实体类Admin(代码此处省略)
  • 在dao目录下编写ManagerDao接口和ManagerDaoImpl实现类

ManagerDao接口

public interface ManagerDao {
    Manager select(String username);
}

ManagerDaoImpl实现类

public class ManagerDaoImpl implements ManagerDao {
    private QueryRunner queryRunner = new QueryRunner();

    @Override
    public Manager select(String username) {
        try {
            Manager manager = queryRunner.query(DbUtils.getConnection(), "select * from manager where username=?", new BeanHandler<Manager>(Manager.class),username);
            return manager;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
}
  • service下编写ManagerService接口和ManagerServiceImpl实现类

ManagerService接口

public interface ManagerService {
   Manager login(String username,String password);
}

ManagerServiceImpl实现类

public class ManagerServiceImpl implements ManagerService {
    private ManagerDao managerDao = new ManagerDaoImpl();
    @Override
    public Manager login(String username, String password) {
        Manager manager = null;
        try {
            DbUtils.begin();
            Manager temp = managerDao.select(username);
            if (temp != null) {
                if (temp.getPassword().equals(password)) {
                    manager = temp;
                }
            }
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return manager;
    }
}
13.7.2 登陆页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>管理员登录</title>
</head>
<body>
<form action="/WebProject_war_exploded/loginMgr" method="post">
    <p>
        <label for="1">用户名:</label>
        <input type="text" name="username" id="1">
    </p>
    <p>
        <label for="2">密码:&nbsp;&nbsp;&nbsp;</label>
        <input type="password" name="password" id="2">
    </p>
    <p>
        <input type="submit" value="登陆">
    </p>
</form>
</body>
</html>
13.7.3 LoginMgrController
  • servlet下的controller目录下, 负责调用业务逻辑(管理员登陆)

    @WebServlet(name = "LoginMgrController",value = "/loginMgr")
    public class LoginMgrController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //1. 处理乱码
    request.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");
    //2. 收参
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    //3. 调用业务方法
    ManagerService managerService = new ManagerServiceImpl();
    Manager mgr = managerService.login(username, password);
    //4. 处理结果, 流程跳转
    if (mgr != null) {
    //登陆成功
    //将管理员信息mgr存储在Session中
    HttpSession session = request.getSession();
    session.setAttribute("mgr",mgr);
    //跳转,目标,方式(重定向)
    response.sendRedirect("/WebProject_war_exploded/showAllController");
    }else {
    //如果管理员信息为空则跳转到管理员登陆界面
    response.sendRedirect("/WebProject_war_exploded/loginMgr.html");
    }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

    }

13.7.4 ShowAllAdminController
  • servlet下的controller目录下, 负责调用业务逻辑(查所有用户信息)

    @WebServlet(value = "/showAllController")
    public class ShowAllAdminController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1. 通过HttpSession完成权限控制
    HttpSession session = req.getSession();
    Manager mgr = (Manager)session.getAttribute("mgr");
    if (mgr != null) {
    //2. 调用业务逻辑
    AdminService adminService = new AdminServiceImpl();
    //查用户列表
    List adminList = adminService.showALlAdmin();
    //session作用域存储数据(存储adminList到session中)
    session.setAttribute("adminList", adminList);
    //通过重定向, 跳转到显示结果的servlet
    resp.sendRedirect("/WebProject_war_exploded/showAllJSP");
    }else {
    //如果管理员信息为空则跳转到管理员登陆界面
    resp.sendRedirect("/WebProject_war_exploded/loginMgr.html");
    }

    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    }

13.7.5 ShowAllAdminJSP
  • servlet下的jsp目录下, 负责展示最终结果

    @WebServlet(value = "/showAllJSP")
    public class ShowAllAdminJSP extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1. 通过HttpSession完成权限控制
    HttpSession session = req.getSession();
    //3. 从session中取出管理员信息
    Manager mgr = (Manager)session.getAttribute("mgr");
    //3. 从session中取出用户列表信息
    List adminList = (List)session.getAttribute("adminList");

        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter printWriter = resp.getWriter();
        if (adminList != null) {
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<meta charset='UTF-8'>");
            printWriter.println("<title>展示所有用户</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<h1>欢迎您: "+ mgr.getUsername() +"</h1>");
            printWriter.println("<table border='1'>");
            printWriter.println("<tr>");
            printWriter.println("   <th>username</th>");
            printWriter.println("   <th>password</th>");
            printWriter.println("   <th>phone</th>");
            printWriter.println("   <th>address</th>");
            printWriter.println("</tr>");
            for (Admin admin : adminList) {
                printWriter.println("<tr>");
                printWriter.println("   <td>"+admin.getUsername()+"</td>");
                printWriter.println("   <td>"+admin.getPassword()+"</td>");
                printWriter.println("   <td>"+admin.getPhone()+"</td>");
                printWriter.println("   <td>"+admin.getAddress()+"</td>");
                printWriter.println("</tr>");
            }
            printWriter.println("</table>");
            printWriter.println("</body>");
            printWriter.println("</html>");
        }else {
            printWriter.println("<html>");
            printWriter.println("<head>");
            printWriter.println("<meta charset='UTF-8'>");
            printWriter.println("<title>展示所有用户</title>");
            printWriter.println("</head>");
            printWriter.println("<body>");
            printWriter.println("<h3>当前没有用户!</h3>");
            printWriter.println("</body>");
            printWriter.println("</html>");
        }
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    }

13.8 Session实战保存验证码

13.8.1 创建验证码
  • 导入ValidateCode.jar

  • 创建生成验证码的Servlet

    @WebServlet(name = "CreateCodeController",value = "/createCode")
    public class CreateCodeController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //1. 创建验证码图片
    ValidateCode code = new ValidateCode(200, 30, 4, 20);
    String codes = code.getCode();
    HttpSession session = request.getSession();
    session.setAttribute("codes",codes);
    //2. 验证码图片响应给客户端
    code.write(response.getOutputStream());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

    }

13.8.2 登陆页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>管理员登录</title>
</head>
<body>
<form action="/WebProject_war_exploded/loginMgr" method="post">
    <p>
        <label for="1">用户名:</label>
        <input type="text" name="username" id="1">
    </p>
    <p>
        <label for="2">密码:&nbsp;&nbsp;&nbsp;</label>
        <input type="password" name="password" id="2">
    </p>
    <p>
        <label for="3">验证码:</label>
        <input type="text" name="inputVcode" id="3">
        <img src="/WebProject_war_exploded/createCode" alt="验证码">
    </p>
    <p>
        <input type="submit" value="登陆">
    </p>
</form>
</body>
</html>
13.8.3 LoginMgrController
@WebServlet(name = "LoginMgrController",value = "/loginMgr")
public class LoginMgrController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 处理乱码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        //2. 收参
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String inputVcode = request.getParameter("inputVcode");

        String codes = (String)request.getSession().getAttribute("codes");
        if (!inputVcode.isEmpty() && inputVcode.equalsIgnoreCase(codes)) {
            //3. 调用业务方法
            ManagerService managerService = new ManagerServiceImpl();
            Manager mgr = managerService.login(username, password);
            //4. 处理结果, 流程跳转
            if (mgr != null) {
                //登陆成功
                //将管理员信息mgr存储在Session中
                HttpSession session = request.getSession();
                session.setAttribute("mgr",mgr);
                //跳转,目标,方式
                response.sendRedirect("/WebProject_war_exploded/showAllController");
            }else {
                //如果管理员信息为空则跳转到管理员登陆界面
                response.sendRedirect("/WebProject_war_exploded/loginMgr.html");
            }
        }else {
            //如果验证码为空或者不正确则跳转到登陆页面
            response.sendRedirect("/WebProject_war_exploded/loginMgr.html");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

十四. ServletContext对象[重点]

14.1 ServletContext概述

  • 全局对象, 也拥有作用域, 对应一个Tomcat中的Web应用
  • 当Web服务器启动时, 会为每一个Web应用程序创建一块共享的存储区域(ServletContext)
  • ServletContext在Web服务器启动时创建, 服务器关闭时销毁

14.2 获取ServletContext对象

  • GenericServlet提供了getServletContext方法. (推荐) this.getServletContext();
  • HttpServletRequest提供了getServletContext()方法.(推荐)
  • HttpSession提供了getServletContext()方法

14.3 ServletContext作用

14.3.1 获取项目真实路径
  • 获取当前项目在服务器发布的真实路径

    String realpath=servletContext.getRealPath("/");

14.3.2 获取项目上下文路径
  • 获取当前项目上下文路径(应用程序名称)

    System.out.println(servletContext.getContextPath());//上下文路径(应用程序名称)
    System.out.println(request.getContextPath());

14.3.3 全局容器
  • ServletContext拥有作用域, 可以存储数据到全局容器中

    • 存储数据: servletContext.setAttribute("name", value);
    • 获取数据: servletContext.getAttribute("name");
    • 移除数据: servletContext.removeAttribute("name");

14.4 ServletContext特点

  • 唯一性: 一个应用对应一个ServletContext
  • 生命周期: 只要容器不关闭或者应用不卸载, ServletContext就一直存在

14.5 ServletContext应用场景

  • ServletContext统计当前项目访问次数

    @WebServlet(name = "ShowCounterController",value = "/showCounterController")
    public class ShowCounterController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //1. 获取servletContext对象
    ServletContext servletContext = request.getServletContext();
    //2. 获取计数器
    Integer counter = (Integer) servletContext.getAttribute("counter");

        if (counter == null) {
            counter = 1;
        }else {
            counter += 1;
        }
        servletContext.setAttribute("counter",counter);
        System.out.println("Servlet被访问次数: " + counter);
    }
    
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

    }

14.6 作用域总结

  • HttpServletRequest: 一次请求, 请求响应之间有效
  • HttpSession: 一次会话开始, 浏览器不关闭或不超时之前有效
  • ServletContext: 服务器启动开始, 服务器停止之前有效

十五. 过滤器[重点]

15.1 现有问题

  • 在以往的Servlet中, 有没有冗余的代码, 多个Servlet都要进行编写

15.2 概念

  • 过滤器(Filter) 是处于客户端与服务器目标资源之间的一道过滤技术

15.3 过滤器作用

  • 执行地位在Servlet之前. 客户端发送请求时, 会先经过Filter, 再到达目标Servlet中; 响应时, 会根据执行流程再次反向执行Filter
  • 可以解决多个Servlet共性代码的冗余问题(例如: 乱码处理, 登陆验证)

15.4 编写过滤器

  • Servlet API中提供了一个Filter接口, 开发人员编写一个Java类实现这个接口即可, 那么这个类就被称之为过滤器(Filter)
15.4.1实现过程
  • 编写Java类实现Filter接口
  • 在doFilter方法中编写拦截逻辑
  • 设置拦截路径

过滤器

@WebFilter(value = "/t")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("---My Filter---");
        //让请求继续
        filterChain.doFilter(servletRequest,servletResponse);

        System.out.println("---end---");
    }

    @Override
    public void destroy() {

    }
}

Servlet

@WebServlet(name = "TargetServlet",value = "/t")
public class TargetServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("---Target---");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

15.5 过滤器配置

15.5.1 注解配置(推荐)
  • 在自定义的Filter类上使用注解@WebFilter(value="/过滤目标资源")
15.5.2 xml配置
    <!--过滤器的xml配置-->
    <filter>
        <!--名称-->
        <filter-name>Xml</filter-name>
        <!--过滤器类全程-->
        <filter-class>com.dz.filter.XmlFilter</filter-class>
    </filter>
    <!--映射路径配置-->
    <filter-mapping>
        <!--名称-->
        <filter-name>Xml</filter-name>
        <!--过滤的url匹配规则和Servlet类似-->
        <url-pattern>/test</url-pattern>
    </filter-mapping>
15.5.3 过滤器路径
过滤器的过滤路径通常有三种形式

精确过滤匹配 如: /index.jsp,    /myservlet1

后缀过滤匹配 如: *.jsp,    *.html,     *.jpg

通配符过滤匹配/*, 表示拦截所有. 注意过滤器不能使用/匹配. /aaa/bbb/*允许

15.6 过滤器链和优先级

15.6.1 过滤器链
  • 客户端对服务器请求之后, 服务器调用Servlet之前会执行一组过滤器(多个过滤器), 那么这组过滤器就称作一条过滤器链
  • 每个过滤器实现某个特定的功能, 当第一个Filter的doFilter方法被调用时, Web服务器会创建一个代表Filter链的FilterChain对象传递给该方法, 在doFilter方法中, 开发人员如果调用了FilterChain对象的doFilter方法, 则Web服务器会检查FilterChain对象中是否还有filter, 如果有, 则调用第二个filter, 如果没有, 则调用目标资源(XxxServlet)
15.6.2 过滤器优先级
  • 在一个Web应用中, 可以开发编写多个Filter, 这些Filter组合起来称之为一个Filter链. 优先级:

    • 如果为注解的话, 是按照类全名称的字符串顺序决定作用顺序
    • 如果是web.xml, 则按照filter-mapping 注册顺序, 从上往下
    • web.xml配置高于注解方式
    • 如果注解和web.xml同时配置, 会创建多个过滤器对象, 造成过滤多次

15.7 过滤器经典应用

15.7.1 过滤器解决编码
@WebFilter(filterName = "EncodingFilter",value = "/*")
public class EncodingFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //设置统一编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");

        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }

}
15.7.2 权限验证
  • showAllAdminController

    @WebServlet(value = "/showAllController")
    public class ShowAllAdminController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1. 调用业务逻辑
    AdminService adminService = new AdminServiceImpl();
    //查用户列表
    List adminList = adminService.showALlAdmin();
    //session作用域存储数据(存储adminList到session中)
    req.getSession().setAttribute("adminList", adminList);
    //通过重定向, 跳转到显示结果的servlet
    resp.sendRedirect("/WebProject_war_exploded/showAllJSP");

    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    }

  • CheckFilter

    @WebFilter(filterName = "CheckFilter",value = "/showAllController")
    public class CheckFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //权限验证, 验证管理员是否登陆
        //先向下转型 拆箱
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)resp;
        HttpSession session = request.getSession();
        Manager mgr = (Manager)session.getAttribute("mgr");
        if (mgr != null) {//证明登陆过,验证通过
            chain.doFilter(request, response);
        }else {//验证未通过,重新登陆
            response.sendRedirect(request.getContextPath() + "/loginMgr.html");
        }
    }
    
    public void init(FilterConfig config) throws ServletException {
    
    }

    }

十六. 综合案例 (EmpProject)

16.1 数据库环境搭建

  • 该案例是EmpProject员工管理系统, 使用了两张表

    • emp员工信息表
    • empManager管理员表
16.1.1 创建数据库
CREATE DATABASE emp;
USE emp;
16.1.2 创建数据库表
CREATE TABLE emp(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(20) NOT NULL,
    salary DOUBLE NOT NULL,
    age INT NOT NULL
)CHARSET=utf8;

CREATE TABLE empManager(
    username VARCHAR(20) NOT NULL,
    PASSWORD VARCHAR(20) NOT NULL
)CHARSET=utf8;
  • 项目中的entity实体层代码此处未给出, 根据数据库表自行创建即可

16.2 创建Web项目

  • 创建Web项目, 导入相关jar包

    • mysql-connector-java-5.1.25-bin.jar
    • druid-1.1.5.jar
    • commons-dbutils-1.7.jar
    • ValidateCode.jar

16.3 基础环境搭建

  • 项目下创建包目录结构

    • com.dz.emp.controller 调用业务逻辑的Servlet
    • com.dz.emp.dao 数据访问层
      • com.dz.emp.dao.impl 数据访问层实现类
    • com.dz.emp.entity 实体类
    • com.dz.emp.filter 过滤器
    • com.dz.emp.jsp 打印显示页面的Servlet
    • com.dz.emp.service 业务逻辑层
      • com.dz.emp.service.impl 业务逻辑层实现类
    • com.dz.emp.utils 工具类
    • database.properties 数据库连接及连接池配置文件
  • web下创建empLogin.html

16.4 database.properties配置文件

#<!-- 连接设置 -->
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/emp?useUnicode=true&characterEncoding=UTF-8
username=root
password=root
#<!-- 初始化连接 -->
initialSize=10
#<!-- 最大连接数量 -->
maxActive=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 60000毫秒/1000等于60秒 -->
maxWait=3000

16.5 DbUtils工具类

public class DbUtils {
    //声明连接池对象
    private static DruidDataSource ds;
    //一个线程共享同一个ThreadLocal, 在整个流程中任一环节可以存值或取值
    private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();

    static {
        Properties properties = new Properties();
        try {
            InputStream inputStream = DbUtils.class.getResourceAsStream("/database.properties");
            properties.load(inputStream);
            ds = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        Connection conn = THREAD_LOCAL.get();
        try {
            if (conn == null) {
                conn = ds.getConnection();
                THREAD_LOCAL.set(conn);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return conn;
    }

    public static void begin() {
        Connection conn = null;
        try {
            conn = getConnection();
            conn.setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public static void commit() {
        Connection conn = null;
        try {
            conn = getConnection();
            conn.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            closeAll(conn,null,null);
        }
    }

    public static void rollback() {
        Connection conn = null;
        try {
            conn = getConnection();
            conn.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            closeAll(conn,null,null);
        }
    }

    public static void closeAll(Connection conn, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (conn != null) {
                conn.close();
                THREAD_LOCAL.remove();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

}

16.6 dao数据访问层

16.6.1 EmpManagerDao接口
public interface EmpManagerDao {
    EmpManager select(String username);
}
16.6.2 EmpDao接口
public interface EmpDao {
    int insert(Emp emp);
    int delete(int id);
    int update(Emp emp);
    Emp select(int id);
    List<Emp> selectAll();
}
16.6.3 EmpManagerDao接口实现类
public class EmpManagerDaoImpl implements EmpManagerDao {
    private QueryRunner queryRunner = new QueryRunner();
    @Override
    public EmpManager select(String username) {
        try {
            EmpManager empManager = queryRunner.query(DbUtils.getConnection(),"select * from empManager where username=?", new BeanHandler<EmpManager>(EmpManager.class), username);
            return empManager;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
}
16.6.4 EmpDao接口实现类
public class EmpDaoImpl implements EmpDao {
    private QueryRunner queryRunner = new QueryRunner();

    @Override
    public int insert(Emp emp) {
        try {
            int result = queryRunner.update(DbUtils.getConnection(), "insert into emp(name,salary,age) values(?,?,?)", emp.getName(), emp.getSalary(), emp.getAge());
            return result;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return 0;
    }

    @Override
    public int delete(int id) {
        try {
            int result = queryRunner.update(DbUtils.getConnection(),"delete from emp where id = ?",id);
            return result;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return 0;
    }

    @Override
    public int update(Emp emp) {
        try {
            int result = queryRunner.update(DbUtils.getConnection(), "update emp set name=?,salary=?,age=? where id=?", emp.getName(), emp.getSalary(), emp.getAge(), emp.getId());
            return result;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return 0;
    }

    @Override
    public Emp select(int id) {
        try {
            Emp emp = queryRunner.query(DbUtils.getConnection(), "select * from emp where id=?", new BeanHandler<Emp>(Emp.class), id);
            return emp;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    @Override
    public List<Emp> selectAll() {
        try {
            List<Emp> empList = queryRunner.query(DbUtils.getConnection(), "select * from emp", new BeanListHandler<Emp>(Emp.class));
            return empList;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
}

16.7 service业务逻辑层

16.7.1 EmpManagerService接口
public interface EmpManagerService {
    EmpManager login(String username,String password);
}
16.7.2 EmpService接口
public interface EmpService {
    int addEmp(Emp emp);
    int removeEmp(int id);
    int modify(Emp emp);
    Emp showEmp(int id);
    List<Emp> showAllEmp();
}
16.7.3 EmpManagerService接口实现类
public class EmpManagerServiceImpl implements EmpManagerService {
    private EmpManagerDao empManagerDao = new EmpManagerDaoImpl();
    @Override
    public EmpManager login(String username, String password) {
        EmpManager empManager = null;
        try {
            DbUtils.begin();
            EmpManager temp = empManagerDao.select(username);
            if (temp != null) {
                if (temp.getPassword().equals(password)) {
                    empManager = temp;
                }
            }
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return empManager;
    }
}
16.7.4 EmpService接口实现类
public class EmpServiceImpl implements EmpService {
    private EmpDao empDao = new EmpDaoImpl();

    @Override
    public int addEmp(Emp emp) {
        int result = 0;
        try {
            DbUtils.begin();
            result = empDao.insert(emp);
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public int removeEmp(int id) {
        int result = 0;
        try {
            DbUtils.begin();
            result = empDao.delete(id);
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public int modify(Emp emp) {
        int result = 0;
        try {
            DbUtils.begin();
            result = empDao.update(emp);
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public Emp showEmp(int id) {
        Emp emp = null;
        try {
            DbUtils.begin();
            emp = empDao.select(id);
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return emp;
    }

    @Override
    public List<Emp> showAllEmp() {
        List<Emp> empList = new ArrayList<>();
        try {
            DbUtils.begin();
            List<Emp> temp = empDao.selectAll();
            if (temp != null) {
                empList = temp;
            }
            DbUtils.commit();
        } catch (Exception e) {
            DbUtils.rollback();
            e.printStackTrace();
        }
        return empList;
    }
}

16.8 controller(调用业务逻辑的Servlet)

16.8.1 创建验证码
@WebServlet(name = "CreateCodeController",value = "/createCode")
public class CreateCodeController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //创建验证码对象
        ValidateCode validateCode = new ValidateCode(200, 30, 4, 20);
        //获取验证码
        String codes = validateCode.getCode();
        //将验证码存入session
        HttpSession session = request.getSession();
        session.setAttribute("codes",codes);
        //响应到客户端
        validateCode.write(response.getOutputStream());
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
16.8.2 管理员登陆
@WebServlet(name = "EmpManagerLoginController",value = "/manager/EmpManagerLoginController")
public class EmpManagerLoginController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //统一编码格式放在过滤器里, 此处不必再写
        //1. 收参
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String inputVcode = request.getParameter("inputVcode");

        //2. 校验验证码
        String codes = (String)request.getSession().getAttribute("codes");
        if (!inputVcode.isEmpty() && inputVcode.equalsIgnoreCase(codes)) {
            //调用业务逻辑实现登陆
            EmpManagerService empManagerService = new EmpManagerServiceImpl();
            EmpManager empManager = empManagerService.login(username,password);
            if (empManager != null) {
                //登陆成功
                //存储到session作用域
                HttpSession session = request.getSession();
                session.setAttribute("empManager",empManager);
                //跳转到查询所有的Controller
                response.sendRedirect(request.getContextPath() + "/manager/safe/showAllEmpController");
            }else {
                response.sendRedirect( request.getContextPath() + "/empLogin.html");
            }

        }else {
            //如果验证码错误则跳转到登陆界面
            response.sendRedirect( request.getContextPath() + "/empLogin.html");
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
16.8.3 查询所有员工信息
@WebServlet(name = "ShowAllEmpController",value = "/manager/safe/showAllEmpController")
public class ShowAllEmpController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //权限验证放在过滤器里, 此处不必再写
        EmpService empService = new EmpServiceImpl();
        List<Emp> empList = empService.showAllEmp();
        if (empList != null) {
//            HttpSession session = request.getSession();
//            session.setAttribute("empList",empList);//存到session作用域中,本次会话有效
//            response.sendRedirect(request.getContextPath() + "/manager/safe/showAllEmpJSP");
            request.setAttribute("empList",empList);//存到request作用域中,临时存储
            request.getRequestDispatcher("/manager/safe/showAllEmpJSP").forward(request,response);
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
16.8.4 查询单个员工信息
@WebServlet(name = "ShowEmpController",value = "/manager/safe/showEmpController")
public class ShowEmpController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Integer id = Integer.valueOf(request.getParameter("id"));
        EmpService empService = new EmpServiceImpl();
        //根据传过来的id查询数据库得到最新的数据
        Emp emp = empService.showEmp(id);

        request.setAttribute("emp",emp);
        request.getRequestDispatcher("/manager/safe/showUpdateEmpInfoJSP").forward(request,response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
16.8.5 新增员工信息
@WebServlet(name = "InsertEmpController",value = "/manager/safe/insertEmpController")
public class InsertEmpController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        Double salary = Double.valueOf(request.getParameter("salary"));
        Integer age = Integer.valueOf(request.getParameter("age"));

        Emp emp = new Emp(name,salary,age);

        EmpService empService = new EmpServiceImpl();
        empService.addEmp(emp);

        response.sendRedirect(request.getContextPath() + "/manager/safe/showAllEmpController");

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
16.8.6 删除单个员工信息
@WebServlet(name = "RemoveEmpController",value = "/manager/safe/removeEmpController")
public class RemoveEmpController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //装箱: 基本数据类型转为引用类型,把拿到的字符串类型的id转为Integer类型
        Integer id = Integer.valueOf(request.getParameter("id"));

        EmpService empService = new EmpServiceImpl();
        empService.removeEmp(id);

        response.sendRedirect(request.getContextPath() + "/manager/safe/showAllEmpController");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
16.8.7 修改单个员工信息
@WebServlet(name = "UpdateEmpController",value = "/manager/safe/updateEmpController")
public class UpdateEmpController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 收参
        Integer id = Integer.valueOf(request.getParameter("id"));
        String name = request.getParameter("name");
        Double salary = Double.valueOf(request.getParameter("salary"));
        Integer age = Integer.valueOf(request.getParameter("age"));

        Emp emp = new Emp(id, name, salary, age);

        EmpService empService = new EmpServiceImpl();
        empService.modify(emp);

        response.sendRedirect(request.getContextPath() + "/manager/safe/showAllEmpController");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

16.9 filter过滤器

16.9.1 字符编码过滤器
@WebFilter(filterName = "EncodingFilter",value = "/manager/*")//manager目录下的所有文件都生效
public class EncodingFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {

    }
}
16.9.2 权限验证过滤器
@WebFilter(filterName = "CheckFilter",value = "/manager/safe/*")//manager下的safe目录下的文件都生效,管理员登陆操作和增删改查操作都在此目录下
public class CheckFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)resp;

        HttpSession session = request.getSession();
        EmpManager empManager = (EmpManager) session.getAttribute("empManager");
        if (empManager != null) {//证明登陆过
            chain.doFilter(request, response);
        }else {
            response.sendRedirect(request.getContextPath() + "/empLogin.html");
        }

    }

    public void init(FilterConfig config) throws ServletException {

    }
}

16.10 jsp(打印显示页面的Servlet)

16.10.1 展示所有用户信息的页面
@WebServlet(name = "showAllEmpJSP",value = "/manager/safe/showAllEmpJSP")
public class showAllEmpJSP extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 获取集合数据
//        List<Emp> empList = (List<Emp>)request.getSession().getAttribute("empList");//从session中拿数据
        List<Emp> empList = (List<Emp>)request.getAttribute("empList");//从request中拿数据
        PrintWriter printWriter = response.getWriter();
        printWriter.println("<html>");
        printWriter.println("   <head>");
        printWriter.println("       <meta charset='UTF-8'>");
        printWriter.println("       <title>查询所有员工页面</title>");
        printWriter.println("   </head>");
        printWriter.println("       <body>");
        printWriter.println("           <form action='/EmpProject/manager/safe/showInsertEmpJSP'>");
        printWriter.println("               <p><input type='submit' value='新增'></p>");
        printWriter.println("           </form>");
        printWriter.println("           <table border='1'>");
        printWriter.println("               <tr>");
        printWriter.println("                   <td>编号</td>");
        printWriter.println("                   <td>姓名</td>");
        printWriter.println("                   <td>工资</td>");
        printWriter.println("                   <td>年龄</td>");
        printWriter.println("                   <td colspan='2'>操作</td>");
        printWriter.println("               </tr>");
        for (Emp emp : empList) {
            printWriter.println("               <tr>");
            printWriter.println("                   <td>"+emp.getId()+"</td>");
            printWriter.println("                   <td>"+emp.getName()+"</td>");
            printWriter.println("                   <td>"+emp.getSalary()+"</td>");
            printWriter.println("                   <td>"+emp.getAge()+"</td>");
            printWriter.println("                   <td><a href='"+request.getContextPath() + "/manager/safe/removeEmpController?id="+emp.getId()+"" +"'>删除</a></td>");
            printWriter.println("                   <td><a href = "+request.getContextPath() + "/manager/safe/showEmpController?id="+emp.getId()+"" +">修改</a></td>");
            printWriter.println("               </tr>");
        }
        printWriter.println("           </table>");
        printWriter.println("       </body>");
        printWriter.println("</html>");

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
16.10.2 展示新增员工操作时的页面
@WebServlet(name = "ShowInsertEmpJSP",value = "/manager/safe/showInsertEmpJSP")
public class ShowInsertEmpJSP extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter printWriter = response.getWriter();
        printWriter.println("<html>");
        printWriter.println("   <head>");
        printWriter.println("       <meta charset='UTF-8'>");
        printWriter.println("       <title>新增员工信息页面</title>");
        printWriter.println("   </head>");
        printWriter.println("   <body>");
        printWriter.println("       <form action='/EmpProject/manager/safe/insertEmpController' method='post'>");
        printWriter.println("           <p>姓名:<input type='text' name='name'></p>");
        printWriter.println("           <p>工资:<input type='text' name='salary'></p>");
        printWriter.println("           <p>年龄:<input type='text' name='age'></p>");
        printWriter.println("           <p><input type='submit' value='提交'></p>");
        printWriter.println("       </form>");
        printWriter.println("   </body>");
        printWriter.println("</html>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}
16.10.3 展示修改员工信息时的页面
@WebServlet(name = "ShowUpdateEmpInfoJSP",value = "/manager/safe/showUpdateEmpInfoJSP")
public class ShowUpdateEmpInfoJSP extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Emp emp = (Emp) request.getAttribute("emp");

        PrintWriter printWriter = response.getWriter();
        printWriter.println("<html>");
        printWriter.println("   <head>");
        printWriter.println("       <meta charset='UTF-8'>");
        printWriter.println("       <title>修改员工信息页面</title>");
        printWriter.println("   </head>");
        printWriter.println("   <body>");
        printWriter.println("       <form action='/EmpProject/manager/safe/updateEmpController' method='post'>");
        printWriter.println("           <p>编号:<input type='text' name='id' value='"+emp.getId()+"' readonly></p>");
        printWriter.println("           <p>姓名:<input type='text' name='name' value='"+emp.getName()+"'></p>");
        printWriter.println("           <p>工资:<input type='text' name='salary' value='"+emp.getSalary()+"'></p>");
        printWriter.println("           <p>年龄:<input type='text' name='age' value='"+emp.getAge()+"'></p>");
        printWriter.println("           <p><input type='submit' value='修改'></p>");
        printWriter.println("       </form>");
        printWriter.println("   </body>");
        printWriter.println("</html>");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

16.11 empLogin.html

  • 管理员登陆界面



    管理员登录

    验证码


手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章