Java Web总结

文章目录

      • 一 web引言
        • 1.1 生活中的上网方式
        • 1.2 Web服务器
        • 1.3 Web相关概念
      • 二 Tomcat
        • 2.1 Tomcat安装与配置
      • 三 Web项目
        • 3.1Web项目分类
        • 3.2 tomcat部署项目
        • 3.3 IDEA配置Tomcat
        • 3.4 idea中的web项目的内容部署到tomcat中
      • 四 HTTP协议
        • 4.1 使用抓包来查看http协议信息
        • 4.2 请求的执行流程
        • 4.3 http请求
        • 4.4 Http响应
      • 五 Servlet
        • 5.1 Servlet介绍
        • 5.2 服务器编译环境设置
        • 5.3 Servlet的入门
          • 5.3.1 入门案例
          • 5.3.2 执行流程
          • 5.3.3 注意事项
        • 5.4 Servlet详解
          • 5.4.1 Servlet处理请求
          • 5.4.2 创建Servlet的三种方式
        • 5.5 Servlet生命周期
        • 5.6 load-on-startup配置
        • 5.7 Servlet配置
          • 5.7.1 自定义Servlet
          • 5.7.2 < url-parttern >书写规则
        • 5.8 缺省Servlet
        • 5.9 服务器中路径
        • 5.10 ServletConfig对象
        • 5.11 ServletContext对象
          • 5.11.1 介绍
          • 5.11.2 ServletContext对象作用
        • 5.12 ServletContext综合案例
        • 5.13 Servlet3.0
      • 六 request与response对象
        • 6.1 介绍
        • 6.2 response对象操作响应行、响应头
        • 6.3 response操作重定向
        • 6.4 response操作定时跳转
        • 6.5 response操作响应正文
        • 6.6 request操作请求行
        • 6.7 request操作请求头
        • 6.8 request操作请求参数
        • 6.9 请求参数中文乱码
          • 6.9.1 post
          • 6.9.2 get
          • 6.9.3 终极解决方案
        • 6.10 request操作请求转发
        • 6.11 请求转发和重定向
        • 6.12 request作为域对象
      • 七 会话技术
      • 八 Cookie
        • 8.1 Cookie介绍
        • 8.2 Cookie的基本使用
        • 8.3 Cookie的相关设置
        • 8.4 Cookie案例
          • 8.4.1 记录上一次访问时间
          • 8.4.2 商品浏览记录
        • 8.5 CookieUtils工具类
      • 九 Session
        • 9.1 Session基本使用
        • 9.2 Session相关配置
        • 9.3 session的基本使用
        • 9.4 session和request的区别
        • 9.5 session和cookie的区别
        • 9.6 session案例
          • 9.6.1 登录
          • 9.6.2 随机验证码
      • 十 过滤器
        • 10.1 过滤器的概念
        • 10.2 过滤器的基本使用
        • 10.3 过滤器的相关配置
        • 10.4 过滤器的注解开发
        • 10.5 过滤器案例
          • 10.5.1 中文乱码
          • 10.5.2 自动登录
          • 10.5.3 敏感词屏蔽
      • 十一 监听器
        • 11.1 监听器的介绍
        • 11.2 监听器的分类
          • 11.2.1 一类监听器
          • 11.2.2 二类监听器
          • 11.2.3 三类监听器
        • 11.3 监听器注解开发
        • 11.4 监听器综合案例
          • 11.4.1 记录登录人数
      • 十二 JavaWeb综合案例
        • 12.1 用户管理与DML操作
          • 12.1.1 用户列表
          • 12.1.2 删除用户
          • 12.1.3 批量删除
          • 12.1.4 添加用户
          • 12.1.5 修改用户
        • 12.2 BaseServlet优化案例
          • 12.2.1 BaseServlet 的作用
          • 12.2.2 实现原理
          • 12.2.3 BaseServlet的应用
          • 12.2.4 BaseServlet的优化
        • 12.3 jsp优化登录案例
        • 12.4 三层结构优化
          • 12.4.1 准备工作
          • 12.4.2 登录
          • 12.4.3 注销登录
          • 12.4.4 用户列表
          • 12.4.5 登录校验
          • 12.4.6 删除用户
          • 12.4.7 修改用户
      • 十三 分页查询
        • 13.1 数据库的物理分页
        • 13.2 分页查询流程分析
        • 13.3 分页代码实现
          • 13.3.1 后台代码
          • 13.3.2 前端代码
        • 13.4 另一版本
      • 十四 文件上传
        • 14.1 概念及原理
        • 14.2 页面实现
        • 14.3 编写服务器程序
          • 14.3.1 文件上传
          • 13.3.2 文件下载
          • 13.3.3 多文件上传
      • 十五 BaseServlet

一 web引言

1.1 生活中的上网方式

两种方式:可以通过浏览器(browser)进行上网,也可以通过客户端(client)进行上网

两种结构
BS结构 browser server 浏览器服务器
CS结构 client server 客户端服务器

BS结构 browser server 浏览器服务器
- 不需要安装客户端,只要能连上网,就能随时随地使用
- 开发人员只需要对服务器端程序进行开发、维护,降低开发维护难度和开发维护成本
- 浏览器主要负责用户界面的动态展示,只处理一些简单的逻辑功能
- 所有具体业务逻辑的处理都由服务器端程序完成,所以程序负载几乎都转移给服务器
端。
- 但是随着服务器负载的增加,可以平滑地增加服务器的个数并建立集群服务器系统,然
后在各个服务器之间做负载均衡。
CS结构 client server 客户端服务器
- 将应用程序分为客户端和服务器端两层,客户端程序用于展示功能,为用户提供操作界
面,同时也可以进行业务逻辑的处理;而服务器端程序负责操作数据库完成数据处理等
核心业务
- 由此可见通过C/S开发模型开发的应用程序,客户端程序可以承担一部分业务逻辑处
理,特别是数据的预处理工作,减轻了服务器端程序的压力
BS优缺点:
优点:实时地更新数据(新功能的增加只需要在服务端完成, 浏览器刷新就好了),
缺点:将负载给了服务器

CS优缺点:
优点:客户端也分担了一部分负载,
缺点:如果有新的功能要增加必须要重新下载客户端

------------------------------

1.2 Web服务器

作用:能够让本地电脑中的资源可以被其他的电脑访问

常见的javaweb服务器
    weblogic
        它是oracle公司的,已经被oracle收购,它是全面支持javaee规范,收费的大型的web
    服务器,它是企业中主流的服务器,在网络上学习资料比较多。
    
websphere
        它是ibm公司的一个大型的收费的全面支持javaee规范的javaee容器。
        
tomcat
        它是开源的免费的servlet规范的服务器。 它是apache一个web服务器。

jboss
        hibernate公司产品,不是开源免费的,是javaee规范的容器。

ngix
        Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理服务器

------------------------------

1.3 Web相关概念

什么是web?
Web指的就是网页,我们所说的web指的是internet主机(服务器)上的供外界访问的资源
web资源可以分为两种:静态web资源、动态web资源

静态web资源
指web页面上供人们浏览的数据,它们始终不变。例如html
    
优点:
静态网站开发简易,只需要掌握HTML、CSS和JS就可以开发
静态访问速度快,因为静态网页不需要和任何程序进行交互,更不需要对数据进行处理

缺点:
静态网站内容无法实时更新,因为网站由一个个的静态HTML网页构成,新增内容只能
通过开发人员修改代码
网站内容过多时,每个页面都需要单独制作,需要不断编写和维护HTML页面,增加了
网站开发人员的工作量,提高了运营费用。
动态web资源
指web页面中内容是由程序产生的,供人们浏览,并且在不同的时间点,数据不一样,并且
还可以实现人与人之间的交互。用到Servlet和JS等技术.

优点
维护方便、可以根据用户需求实现各种功能
查询信息方便,能存储大量数据,需要时能立即查询
网站内容可以实时动态更新
与用户交互性强,提高用户粘性

缺点
技术要求高

二 Tomcat

2.1 Tomcat安装与配置

  • 测试是否安装成功:
    在tomcat的安装目录下有一个bin目录 ,在目录 中有一个startup.bat文件执行它。打开浏览器,在浏览器的地址栏上输入 http://localhost:8080

  • 配置

    • JAVA_HOME配置
      在tomcat的安装目录bin文件夹下的catalina.bat中使用了JAVA_HOME,所以,安装
      tomcat必须要求系统配置中有JAVA_HOME,如果没有配置,执行startup.bat文件时会出现闪退效果
    • 端口号配置
      tomcat默认使用的8080端口,可以进行端口号的修改,修改tomcat的端口号,在
      tomcat/conf/server.xml文件, 可以添加80端口,80是http协议默认的端口。

三 Web项目

3.1Web项目分类

 - Web静态项目
 	包含都是静态资源:html、js、css、图片、音频等等
 	
 - Web动态项目: 不是必须包含动态资源
 	包含都是静态资源:html、js、css、图片、音频等等
​		那么和静态项目的区别在哪里?可以有动态资源及WEB-INF文件夹
​		通过http://localhost:8080/    访问的资源是来自于tomcat服务器的动态web项目(内置),
​		而在tomcat的一个安装目录中,是由一个webapps的文件夹专门用来部署web项目

------------------------------

3.2 tomcat部署项目

  • 方式一:直接将web应用程序放置到webapps目录
    直接将一个web应用程序放置在tomcat/webapps目录下。这时web应用程序目录名称就是我们访问tomcat下的这个应用程序的名称

  • 方式二:虚拟目录初级版
    将一个不在tomcat下的web应用程序部署加载。可以在tomcat/conf/server.xml文件中配置,在server.xml文件中的标签中添加一段配置

<Context docBase="磁盘路径" path="/hello" />
	经过以上配置后,在浏览器上可以输入http://localhost/hello来访问
	但是,在tomcat6以后已经不建议使用了。
  • 方式三:虚拟目录优化版
    在tomcat/conf/Catalina/localhost下创建任意名称的一个xml文件,例如创建一个good.xml文件,在good.xml中书写
<Context docBase="磁盘路径" />
	这种方案配置,xml文件的名称就是访问路径,在浏览器中访问http://localhost/good

------------------------------

3.3 IDEA配置Tomcat

  • 1 选择Edit Configurations
    1
  • 2 点击加号 ->tomcat server -> local
    Java Web总结_第1张图片
  • 3 点击Configure -> 点击加号 -> 选择tomcat
    3
    4Java Web总结_第2张图片

------------------------------

3.4 idea中的web项目的内容部署到tomcat中

只有资源来到了day50_war_exploded文件夹中,才意味着部署成功

src文件夹:
​		可以部署上去!部署到了项目中的\WEB-INF\classes文件夹中

​web文件夹:
​		可以部署上去!部署到了项目目录中!

​day50项目下:
​		不可以部署上去

四 HTTP协议

协议:两个设备进行数据交换的约定!
Http协议:超文本传输协议(hypertext transfer protocl)
超文本:字符、音频、视频、图片等等
基于tcp协议。tomcat服务器底层实现本质上就是TCP(Socket)

4.1 使用抓包来查看http协议信息

经过演示发现,浏览器和服务器,它们之间进行交互,是一个请求-响应模型!!!

请求:
​	请求行
​	请求头
​	请求正文

响应:
​	响应行
​	响应头
​	响应正文
通过抓包可以看到如下信息
	General:请求行,响应行
	Request Headers:请求头
	Response Headers:响应头
	响应正文:将显示内容携带到浏览器
	请求正文:用来接收请求的参数.

------------------------------

4.2 请求的执行流程

  • 发起请求
  • 域名解析
    本地域名解析器(C:\Windows\System32\drivers\etc\host),
    ​ 如果本地解析器无法解析,那么就交给互联网上的DNS解析器
    ​ 得到IP
  • 根据ip和端口,可以得到一个Socket对象,执行请求
    携带请求行、请求头、请求正文
  • 服务器响应浏览器
    携带响应行、响应头、响应正文

------------------------------

4.3 http请求

请求组成:请求行、请求头、请求正文

  • 请求行
Request URL : 请求路径,告诉服务器要请求的资源路径
​Request Method : 请求方式 , GET/POST
​protocol : http协议版本

---------------

  • GET请求和POST请求
get请求只能携带小数据、get请求下的请求参数会直接拼接到Request URL(请求网址)后面,QueryStringParameters

​post请求可以携带大数据、post请求下的请求参数会存放到请求正文

​	请求参数:比如,表单中的输入框中的值.

​	如果我们要做文件上传,需要用到post请求,文件比较大

---------------

  • 请求头
Content-Type:浏览器告诉服务器,请求正文的数据类型
​User-Agent:浏览器告诉服务器,我是个什么样的浏览器

---------------

  • 请求正文
请求正文,只有当请求方式为post,且有请求参数时才会有请求正文

------------------------------

4.4 Http响应

Http响应组成:响应行、响应头、响应正文

  • 响应行
Status Code : 响应状态码

​	常见的有:
​		200:服务器响应成功
​		302: 告诉浏览器,进行重定向
​		304: 页面上的内容没有发生改变,不需要重新请求服务器
​		404: 没有对应的服务器资源
​		500:服务器内部错误!

---------------

  • 响应头
Location:告诉浏览器重定向的资源路径,需要结合响应状态码302使用

​Content-Type:服务器告诉浏览器,响应正文的数据类型
​		Content-Type:text/html;charset=utf-8; 服务器告诉浏览器,响应正文是文本和html标签;告诉浏览器,应该以utf-8的形式进行解码!浏览器就会以html标签及utf-8的形式对响应正文进行渲染显示!!!

​refresh:定时跳转

​Content-Disposition:文件下载

---------------

  • 响应正文
浏览器显示的内容

五 Servlet

5.1 Servlet介绍

Servlet是运算在服务器上的一个java程序,简单说,它就是一个java类。我们要使用servlet,需要导入servlet的api.

------------------------------

5.2 服务器编译环境设置

Java Web总结_第3张图片
Java Web总结_第4张图片
Java Web总结_第5张图片
result

------------------------------

5.3 Servlet的入门

5.3.1 入门案例

package servlet;

//1,自定义类继承HttpServlet
public class Demo01Servlet extends HttpServlet {
     

    //2,重写doGet和doPost方法
        //doGet:处理get请求
        //doPost:处理post请求
        //在doGet方法中调用doPost方法,不管是 get请求 or post请求,都交给doGet方法处理
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        //处理get请求
        System.out.println("Demo01Servlet doGet");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        //处理post请求
        doGet(req, resp);
    }
}

web/WEB-INF/web.xml


<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_4_0.xsd"
         version="4.0">

    
    
    <servlet>
        
        <servlet-name>Demo01Servletservlet-name>
        
        <servlet-class>com.qfedu.servlet.Demo01Servletservlet-class>
    servlet>
    
    <servlet-mapping>
        
        <servlet-name>Demo01Servletservlet-name>
        
        <url-pattern>/demo01url-pattern>
    servlet-mapping>
web-app>
5.3.2 执行流程
浏览器发起请求: http://localhost:8080/day50/demo01
​	就会在服务器中找访问名称为demo01的Servlet -> Demo01Servlet
​	请求的处理就交给了Demo01Servlet的实例,根据请求方式get/post,决定是给doGet还是doPost方法处理
5.3.3 注意事项
不管是get请求还是post请求,对于服务器来说,没差别

​get请求将请求参数放到请求网址

​post请求将请求参数放到请求正文

​服务器最终无非就要获取请求参数。getParameter()方法

------------------------------

5.4 Servlet详解

HttpServlet继承于GenericServlet、GenericServlet实现于Servlet,也就是说Servlet是顶层接口

5.4.1 Servlet处理请求
  • Servlet接口下有一个实现类叫GenericServlet,GenericServlet有一个子类HttpServlet.
  • 在Servlet接口中定义了一个方法service,它的主要作用是处理来自浏览器的请求操作。在service方法的重载的方法中,对请求方式进行判断,如果是GET就会调用doGet方法,如果是POST就会调用doPost方法。
5.4.2 创建Servlet的三种方式
  • 方式一:实现Servlet接口
在servlet接口中,没有doGet和doPost方法,处理请求是service方法(抽象的)
  • 方式二:继承GenericServlet类
在GenericServlet类中,没有doGet和doPost方法,处理请求是service方法(抽象的)
  • 方式三:继承HttpServlet类
HttpServlet类中重写service方法。

​根据源码,发现重写service方法中,
	有将ServletRequest强转为HttpServletRequest, 
​	将ServletResponse强转为HttpServletResponse
​	以上强转是因为,ServletRequest和ServletResponse并没有针对Http协议做优化!!!无法专门针对http协议调用方法!!

​	HttpServletRequest和HttpServletResponse有针对http协议做优化!!!

在开发中,一般应用比较多的是使用extends HttpServlet,优点是它是与http协议相关
的,封装了http协议相关的操作。

------------------------------

5.5 Servlet生命周期

在javax.servlet.Servlet接口中定义了三个方法init service destroy它们就是servlet的生命周期
方法

public interface Servlet {
     
	//监听Servlet的初始化
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();
    
	//处理请求
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

	//监听Servlet的销毁
    void destroy();
}
  • 调用构造方法创建Sevlet对象(Servlet是单例的,构造方法只执行一次)
  • 调用init方法,进行初始化操作(只执行一次)
  • 当有请求访问Servlet时,会执行service方法(每次请求都执行),在父类HttpServlet中,重写service方法,会把请求分发给doPost()和doGet()方法
  • 当容器关闭时,会执行destroy方法,销毁对象,释放资源

------------------------------

5.6 load-on-startup配置

根据Servlet生命周期,可知,servlet默认不会随着服务器的启动而初始化

load on startup可以让servlet随着服务器的启动而初始化

对于load-on-startup它的可以配置的值有10个,1代表优先级最高,数值越大,优先级越低

------------------------------

5.7 Servlet配置

5.7.1 自定义Servlet

对于servlet,我们需要在web.xml文件中对其进行配置

  • 在web.xml中声明Servlet
<servlet>
        <servlet-name>Demo01Servletservlet-name>
        <servlet-class>servlet.Demo01Servletservlet-class>
    servlet>

---------------

  • 在web.xml中给Servlet映射访问路径
<servlet-mapping>
        <servlet-name>Demo01Servletservlet-name>
        <url-pattern>/demo01url-pattern>
    servlet-mapping>
一个可有多个与其对应
5.7.2 < url-parttern >书写规则
  • 完全匹配
要求网址上的访问名称完全和一致

​必须以"/"开头,否则会报错:IllegalArgumentException : Invalid 

---------------

  • 目录匹配
要求网址上的访问名称中的目录和一致

​必须以"/"开头,以"*"结尾

​比如:/a/b/* , 目录必须是/a/b,后面随便写

---------------

  • 后缀名匹配
要求网址上的访问名称中的后缀和一致

​不能以"/"开头,以"*"开头,后缀名根据业务写

​比如:*.xyz。后缀名必须是xyz,其他随意写!!!

------------------------------

5.8 缺省Servlet

创建一个servlet时,如果它的url-pattern的配置值为”/”这时这个servlet就是一个缺省的
servlet,tomcat服务器中默认就有缺省Servlet

  • 缺省Servlet的作用
凡是在web.xml文件总找不到匹配的元素的URL,它们的请求都将交给
缺省Servlet处理。也就是说,缺省的servlet用于处理其他Servlet处理不了的请求

当访问tomcat服务中的静态资源(html、图片等等)时,实际上是在访问这个缺省的servlet
  • 自定义缺省Servlet
在当前工程中自定义Servlet,将url-parttern设置为"/",就覆盖了tomcat容器中的缺省Servlet
  • 应用
 SpringMVC框架中,用于放行静态资源

------------------------------

5.9 服务器中路径

  • 带协议的绝对路径
http://localhost:8080/day51/img/girl.jpg
  • 不带协议的绝对路径
/day51/img/girl.jpg
  • 相对路径
当前目录:./ ,可以省略

上一级目录:../

------------------------------

5.10 ServletConfig对象

ServletConfig是javax.servlet.包下的一个接口。ServletConfig它是Servlet的一个配置对象

ServletConfig对象是由服务器创建的,它是通过Servlet的init方法传递到Servlet中

  • 作用
获取Servlet名称 getServletName

获取Servlet初始化参数 getInitParameter getInitParameterNames

获取ServletContext对象

getInitParameter(String parameterName):根据参数名称获取指定的参数值

getInitParameterNames():获取所有的参数名称

获取域对象:ServletContext
public class Demo11Servlet extends HttpServlet {
     
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        ServletConfig servletConfig = getServletConfig();
        String servletName = servletConfig.getServletName();
        System.out.println(servletName+"正在运行...");
        System.out.println("--------------------");
        String username = servletConfig.getInitParameter("username");
        System.out.println(username);
        String password = servletConfig.getInitParameter("password");
        System.out.println("--------------------");
        Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
        while (parameterNames.hasMoreElements()){
     
            String parameterName = parameterNames.nextElement();
            String parameterValue = servletConfig.getInitParameter(parameterName);
            System.out.println("name"+parameterName+"password"+parameterValue);
        }

        ServletContext servletContext = getServletContext();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        doGet(req,resp);
    }
}
<servlet>
        <servlet-name>Demo11Servletservlet-name>
        <servlet-class>servlet.Demo11Servletservlet-class>
        <init-param>
            <param-name>usernameparam-name>
            <param-value>rootparam-value>
        init-param>
        <init-param>
            <param-name>passwordparam-name>
            <param-value>root123param-value>
        init-param>
    servlet>

    <servlet-mapping>
        <servlet-name>Demo11Servletservlet-name>
        <url-pattern>/demo11url-pattern>
    servlet-mapping>

------------------------------

5.11 ServletContext对象

相当于是整个应用程序对象

5.11.1 介绍
  • ServletContext它是javax.servlet包下的一个接口
  • 当服务器启动时,会为服务器中的每一个web应用程序创建一个ServletContext对象,一个ServletContext对象对应的就是一个web应用程序
  • 对于ServletContext,我们叫它上下文对象,ServletConfig对象中维护了ServletContext对象,也就是说,我们可以通过ServletConfig对象来获取ServletContext对象
  • 在web应用中的servlet要想实现资源的共享,可以通过ServletContext来完成,
    ServletContext也叫做域对象
5.11.2 ServletContext对象作用
  • 实现Servlet资源共享

ServletContext是一个域对象,可以用来存储数据.可以将它想像成一个Map,可以通过它实现Servlet资源共享

在应用程序中的任何位置都能够访问
    * getAttribute(String parameterName) : 获取ServletContext域中指定名称的参数值
    * setAttribute(String paramterName,Object parameterValue):存储参数到ServletContext域中
    * removeAttribute(String parameterNam):将ServletContext域中指定名称的参数移除

---------------

  • 获取全局初始化参数

在web.xml中配置的全局初始化参数,可以通过ServletContext对象获取

public class Demo15Servlet extends HttpServlet {
     
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        ServletContext servletContext = getServletContext();
        Enumeration<String> parameterNames = servletContext.getInitParameterNames();
        while (parameterNames.hasMoreElements()){
     
            String parameterName = parameterNames.nextElement();
            String parameterValue = servletContext.getInitParameter(parameterName);
            System.out.println("name:"+parameterName+"value:"+parameterValue);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        doGet(req, resp);
    }
}
<context-param>
        <param-name>usernameparam-name>
        <param-value>rootparam-value>
    context-param>
    <servlet>
        <servlet-name>Demo15Servletservlet-name>
        <servlet-class>servlet.Demo15Servletservlet-class>
    servlet>

    <servlet-mapping>
        <servlet-name>Demo15Servletservlet-name>
        <url-pattern>/demo15url-pattern>
    servlet-mapping>

---------------

  • 获取资源在服务器上的真实磁盘路径

getRealPath:依据当前项目去生成真实磁盘路径

servletContext.getRealPath("upload"):当前项目的服务器磁盘路径/upload

servletContext.getRealPath("upload/img"):当前项目的服务器磁盘路径/upload/img
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        ServletContext servletContext = getServletContext();
        String upload = servletContext.getRealPath("upload");
        System.out.println(upload);
    }

------------------------------

5.12 ServletContext综合案例

  • 需求:统计站点访问次数
public class Demo17Servlet extends HttpServlet {
     
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        //1 获取ServletContext对象
        ServletContext servletContext = getServletContext();
        //2 判断是否第一次
        Integer count = (Integer) servletContext.getAttribute("count");
        if (count == null) {
     
            //2.1 第一次访问
            count = 1;
            System.out.println(count);
            servletContext.setAttribute("count", count);
        } else {
     
            //2.2 非第一次访问
            count++;
            System.out.println(count);
            servletContext.setAttribute("count", count);
        }
    }

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

------------------------------

5.13 Servlet3.0

支持注解开发,由注解配置来代替web.xml配置

  • 在Servlet类上直接使用@WebServlet注解
@WebServlet(name = "Demo01Servlet", urlPatterns = {
     "/demo01", "/mydemo01"},
        initParams = {
     @WebInitParam(name = "username", value = "root"),
                @WebInitParam(name = "password", value = "123456")})

---------------

  • 常用属性
name:String:设置Servlet名称

urlPatterns:String[]:设置Servlet的访问路径

loadOnStartup:int:设置Servlet的load-on-startup属性

initParams:WebInitParam[]:设置Servlet的初始化参数

六 request与response对象

6.1 介绍

当浏览器发起请求后,服务器会创建一个请求对象、一个响应对象,通过service方法传入给Serlvet

request与response的作用
	* request对象就可以操作http请求信息
	* response对象就可以操作http响应信息
ServletRequest与HttpServletRequest及ServletResponse与HttpServletResponse关系
	* ServletRequest与ServletResponse它们是HttpServletRequest与HttpServletResponse的
父接口

------------------------------

6.2 response对象操作响应行、响应头

操作响应行
	* setStatus(int status):操作正常响应状态码,比如:200、302
 	* ssendError(int status):操作错误响应状态码,比如: 404
操作响应头
	* public void addHeader(String name, String value):直接覆盖响应头原有值
  	* public void setHeader(String name, String value):在响应头原有值的后面追加

------------------------------

6.3 response操作重定向

通过各种方法将各种网络请求重新定个方向转到其它位置
通过操作状态码302及响应头location来实现

重定向的流程
  * 当浏览器访问一个资源Demo03Servlet,访问名称为“/demo03”
  * Demo03Servlet进行重定向
    * 操作响应状态码302
    * 操作响应头Location,服务器告诉浏览器,重定向的资源的访问路径
  * 浏览器进行一个新的请求,完成重定向
  • 方式一
  response.setStatus(302);
  response.setHeader("Location","demo04");

---------------

  • 方式二
response.sendRedirect("demo04");

------------------------------

6.4 response操作定时跳转

一个资源定时一段时间之后,跳转到另外一个资源
操作响应头refresh

resp.setHeader("refresh","5;url=demo07");

------------------------------

6.5 response操作响应正文

响应正文:就是浏览器显示的主体内容

response.getWriter().write():操作响应正文

响应正文中文乱码

    * 本质原因,服务器的编码和浏览器的解码,格式不同!
    * setCharacterEncoding("utf-8"):
      * 告诉服务器,应该以utf-8格式编码响应正文
    * setHeader("Content-Type","text/html;charset=utf-8"):
      * 告诉浏览器 , 响应正文是文本+html标签,应该以utf-8格式解码响应正文

    * setContentType("text/html;charset=utf-8")
      * 告诉服务器,应该以utf-8格式编码响应正文
      * 告诉浏览器 , 响应正文是文本+html标签,应该以utf-8格式解码响应正文
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //告诉服务器,编码格式为utf-8
//        response.setCharacterEncoding("utf-8");
        //单单只告诉服务器的编码格式,不合适的!浏览器的编码格式不确定!
        //告诉浏览器,编码格式也应该是utf-8
//        response.setHeader("Content-Type","text/html;charset=utf-8");

        response.setContentType("text/html;charset=utf-8");

        String msg1 = "hello world 老邱";
        PrintWriter writer = response.getWriter();
        //操作响应正文
        writer.write(msg1);

        //操作响应正文中文乱码
        //不仅要告诉服务器编码格式,还要告诉浏览器编码格式! 它们应该是统一的!
        //有没有一个简单,就是既告诉服务器的编码,同时也告诉浏览器的编码
}

------------------------------

6.6 request操作请求行

getRequestURI	获取请求路径
  
getMethod	获取请求方式
 
getRemoteAddr	获取请求ip
  
getLocalPort	获取请求端口
 
getQueryString	请求网址"?"后面的路径
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        System.out.println("Demo02Servlet");
        //获取请求路径
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        
        //获取请求方式
        String method = request.getMethod();
        System.out.println(method);
        
        //获取请求ip
        String remoteAddr = request.getRemoteAddr();
        System.out.println(remoteAddr);
        
        //获取请求端口
        int port = request.getLocalPort();
        System.out.println(port);
        
        //获取请求网址后的请求参数
        String queryString = request.getQueryString();
        //在网址后的请求参数,如果要使用,需要做字符串的拆分!!
        System.out.println(queryString);
        //SpringMVC:前后端交互使用的是json字符串,json解析 --> java对象!!
    }

------------------------------

6.7 request操作请求头

getHeader()	获取指定请求头的值
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        System.out.println("Demo03Servlet");
        //获取User-Agent请求头:判定请求是由哪种浏览器发起
        String userAgent = request.getHeader("User-Agent");
        System.out.println(userAgent);
    }

------------------------------

6.8 request操作请求参数

请求正文:post+请求参数
请求参数:不管是get请求 还是 post请求

getParameter	获取指定请求参数值

getParameterNames	获取所有请求参数名称

getParameterValues(String parameterName)	获取指定请求参数所有值

getParameterMap	键,获取所有请求参数名称 , 相当于getParameterNames方法
				值,获取指定请求参数所有值,相当于getParameterValues方法

getParameter	能拿到表单中的参数、URL中的参数
getAttribute	拿的是通过setAttribute存入request的属性
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //获取指定参数值
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        System.out.println("username : " + username + " , password : " + password);

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

        //获取所有请求参数名称
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
     
            String parameterName = parameterNames.nextElement();
            String parameterValue = request.getParameter(parameterName);
            System.out.println("name : " + parameterName + " , value : " + parameterValue);
        }
        System.out.println("---------------");
        //获取指定请求参数所有值
        String[] usernames = request.getParameterValues("username");
        System.out.println(usernames[0]);
        String[] hobbys = request.getParameterValues("hobbys");
        for (String hobby : hobbys) {
     
            System.out.println(hobby);
        }

        System.out.println("---------------");
        //获取请求参数对应的map :
        //getParameterMap() -> Map(String,String[])
        //键:请求参数名称  相当于 getParameterNames
        //值:一组请求参数值 相当于 getParameterValues
        Map<String, String[]> parameterMap = request.getParameterMap();
        //双列集合:获取到所有的实体对象(键值对象)
        Set<Map.Entry<String, String[]>> entrySet = parameterMap.entrySet();
        for (Map.Entry<String, String[]> entry : entrySet) {
     
            //键 - 请求参数名称
            String parameterName = entry.getKey();

            //值 - 一组请求参数值
            String[] values = entry.getValue();
            StringBuffer valueStr = new StringBuffer();
            for (String value : values) {
     
                valueStr.append(value + " ");
            }
            System.out.println("参数名称 : "+ parameterName + " , 参数值 : " + valueStr);
        }
    }

------------------------------

6.9 请求参数中文乱码

6.9.1 post

本质:请求正文中的中文参数乱码

解决方案
  * request.setCharacterEncoding("utf-8")
   		 * 告诉服务器应该以utf-8解码请求正文

  * 逆向,先编码在解码
   		 * 先将乱码字符串以iso8859-1编码成字节
  	 	 * 将字节以utf-8解码成字符串
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //不管是get请求 还是 post请求,都存在请求参数中文乱码问题
        //post请求 告诉服务器,应该以utf-8来解码请求正文
        request.setCharacterEncoding("utf-8");
        String username = request.getParameter("username");
        username = new String(username.getBytes("iso8859-1"),"utf-8");
        System.out.println(username);
        String password = request.getParameter("password");
        System.out.println(password);
    

将tomcat容器的URIEncoding=“utf-8”,对Query String中的请求参数有效,对请求正文中无效,对post请求下的中文乱码无效

---------------

6.9.2 get

本质:Query String中的中文参数乱码

解决方案
  * 逆向,先编码在解码
   		 * 先将乱码字符串以iso8859-1编码成字节
	  	 * 将字节以utf-8解码成字符串
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //不管是get请求 还是 post请求,都存在请求参数中文乱码问题
        //告诉服务器,应该以utf-8对请求正文进行解码
        //get请求中,有请求正文么? 没有!
//        request.setCharacterEncoding("utf-8");
        String username = request.getParameter("username");
        //将乱码的字符串编码(iso8859-1)成字节
//        byte[] bytes = username.getBytes("iso8859-1");
        //将字节解码(utf-8)成字符串
//        String newUsername = new String(bytes,"utf-8");
        username = new String(username.getBytes("iso8859-1"),"utf-8");
        System.out.println(username);

        String password = request.getParameter("password");
        System.out.println(password);
    }

修改tomcat容器的URIEncoding=“utf-8”

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               URIEncoding="utf-8"
               redirectPort="8443" />

request.setCharacterEncoding(“utf-8”)对get请求无效,告诉服务器应该以utf-8来解码请求正文,跟Query String 没有关系

---------------

6.9.3 终极解决方案
方案
  * tomcat8.5 
    * 相当于是tomcat7.0修改了URIEncoding="utf-8"
    * 就解决了get请求参数中文乱码问题
  *  request.setCharacterEncoding("utf-8")
    * 就解决了post请求参数中文乱码问题
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        request.setCharacterEncoding("utf-8");
        String username = request.getParameter("username");
        System.out.println(username);
        String password = request.getParameter("password");
        System.out.println(password);
    }

------------------------------

6.10 request操作请求转发

  • .request的生命周期
    • Request对象是在浏览器向服务器发送请求时,服务器会创建request对象,当服务器产生了响应时,request对象就销毁。简单说就是请求时创建request,响应时销毁request。

  • 请求转发
    • 请求转发指的就是服务器内的资源的跳转,请求转发时,就会形成一个请求链,它们共享同一个request与response对象。

  • 请求转发的实现
    • request.getRequestDispathcher(“路径”).forward(request,response);
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        System.out.println("Demo08Servlet");
        //转发到Demo09Servlet , 重定向 : 操作响应状态码302,操作响应头location  都是通过response
        //获取一个转发器对象,传入一个转发地址
        RequestDispatcher dispatcher = request.getRequestDispatcher("demo09");
        //开始转发
        dispatcher.forward(request,response);
    }

转发只有一次请求

------------------------------

6.11 请求转发和重定向

转发forward:服务器内部进行,客户端不知道。
			转发可以共享request中的数据。
			转发只能在当前工程内部进行。
重定向:redirect:由服务器返回给客户端地址,再由客户端发起第二次请求。
			重定向不能共享request中的数据。
			重定向可以访问其他工程。
请求次数
  * 重定向有2次请求
  * 转发有1次请求

跳转区别
  * 重定向既可以站内资源进行跳转,站外资源也可以进行跳转
  * 转发只能站内资源进行跳转

路径区别
  * 要转发的资源的相对路径无区别
  * 要转发的资源的绝对路径有区别
    * 重定向,是从先从项目开始找,再找资源
    * 转发,是直接从项目中找资源

不能重定向两次

重定向不能拿到上一个请求中的参数,但是可以在重定向的url中拼接参数

/**
 * 重定向和转发的区别之站内资源、站外资源
 */
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //重定向到站外资源
        response.sendRedirect("http://www.baidu.com/");
        //转发到站外资源
        request.getRequestDispatcher("http://www.baidu.com/").forward(request,response);
    }
/**
 * 重定向和转发的区别之路径
 */
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //重定向和转发,使用相对路径是没有区别的!
        //使用重定向跳转到index.html(相对路径)
        response.sendRedirect("index.html");
        
        //使用转发跳转到index.html(相对路径)
        request.getRequestDispatcher("index.html").forward(request,response);
        
        //使用重定向跳转到index.html(绝对路径)
        response.sendRedirect("/day55/index.html");
        
        //使用转发跳转到index.html(绝对路径)
        request.getRequestDispatcher("/index.html").forward(request,response);
    }

------------------------------

6.12 request作为域对象

域对象:可以存储数据的对象

ServletContext域对象的作用范围:不同设备、当前项目中的任意一个资源都可以访问ServletContext域中的数据

request域对象的作用范围
  	* request对象的生命周期
    	* 发起请求时,request初始化
    	* 响应时,request销毁
  	* request域对象的作用范围在一次请求中!

request在重定向和转发中使用!
  	* 重定向中不能使用request域对象
  	* 转发中可以使用request域对象
/**
 * request域对象在重定向和转发中的使用
 */
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //request域操作数据的方法和ServletContext域是一样!
        //setAttribute、getAttribute、removeAttribute
        //往request域中存储一个变量
        String msg = "hello servlet";
        request.setAttribute("msg",msg);
        //重定向到Demo13Servlet  request.getContextPath() 获取项目名称
        response.sendRedirect(request.getContextPath()+"/demo13");
        request.getRequestDispatcher("/demo13").forward(request,response);
    }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        request.removeAttribute("msg");
        System.out.println("Demo13Servlet");
        String msg = (String) request.getAttribute("msg");
        System.out.println(msg);
    }

七 会话技术

打开浏览器,访问服务器中资源,关闭浏览器;这个过程就是会话

  • 分类
  * Cookie会话技术;浏览器会话技术
  * Session会话技术;服务器会话技术
  • 作用
解决ServletContext域对象、Request域对象存储数据所存在的问题


八 Cookie

8.1 Cookie介绍

网景公司发明。是浏览器的会话技术

  • Cookie的流程
  * 浏览器请求服务器,请求Demo01Servlet,创建一个Cookie对象,名称为cookie1
  
* 可以通过响应头Set-Cookie,携带cookie给浏览器进行保存
  * 浏览器再次请求服务器,请求Demo02Servlet,获取cookie1对象

------------------------------

8.2 Cookie的基本使用

  • 设置Cookie
//方式一(不推荐)
response.addHeader("set-cookie","msg=hellocoolie");
//方式二
Cookie cookie = new Cookie("msg","hellocookie");
response.addCookie(cookie);
  • 获取Cookie
开发步骤
    * 通过request对象获取所有的Cookie对象,存储到一个数组中
    * 遍历该数组,匹配Cookie名称
    * 如果匹配上,就知道了指定的Cookie对象
    * 如果匹配不上,就没有指定的Cookie对象
Cookie[] cookies = request.getCookies();
Cookie cookie = null;
for(Cookie sonCookie : cookies){
     
    if("msg".equals(sonCookie.getName())){
     
        cookie = sonCookie;
    }
}
if(null != cookie){
     
    System.out.println("name : "+msgCookie.getName() + " , value : "+ msgCookie.getValue());
}

------------------------------

8.3 Cookie的相关设置

  • 持久化设置
cookie的生命周期
    * 默认是随着浏览器的关闭而销毁
    
setMaxAge
    * 设置cookie的存活时长,cookie就可以不随着会话的关闭而销毁
    * 取值有三种:>0有效期,单位秒;=0浏览器关闭;<0内存存储,默认-1
  • 路径设置
默认情况下,Cookie对象会随着任何一个请求携带到服务器

setPath	设置Cookie的访问路径
	Cookie cookie = new Cookie("msg","helloworld");
    cookie.setPath("/day56/demo04");
    response.addCookie(cookie);

cookie对象只有访问路径包含"/day56/demo04",才会跟随请求携带到服务器

------------------------------

8.4 Cookie案例

8.4.1 记录上一次访问时间
需求:
   第一次访问,就直接打印当前时间
   不是第一次访问,就打印上一次的访问时间

开发步骤:
	获取对应的Cookie对象
    判断是否是第一次访问
    如果是第一次访问
      打印当前时间
      将当前时间存储到Cookie中
    如果不是第一次访问
      打印上一次访问时间
      将当前时间存储到Cookie中
  //判断是否是一次请求
  Cookie[] cookies = request.getCookies();
  Cookie cookie = null;//记录上一次的访问时间
  if (cookies != null && cookies.length != 0 ) {
     
      for (Cookie sonCookie : cookies) {
     
          if ("lastTime".equals(sonCookie.getName())) {
     
              cookie = sonCookie;
          }
      }
  }
  SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
  if (null == cookie) {
     
      //第一次访问 ,打印当前时间,并将创建Cookie对象,存储当前时间
  
      Date currentDate = new Date();
      System.out.println("第一次访问,时间为" + format.format(currentDate));
      cookie = new Cookie("lastTime",currentDate.getTime()+"");
  } else {
     
      //不是第一次访问,从cookie取出上一次的访问时间,并打印。获取当前时间,并存储cookie对象中
      long lastDateMills = Long.parseLong(cookie.getValue());
      Date lastDate = new Date(lastDateMills);
      //获取到了上一次的访问时间
      String lastTimeStr = format.format(lastDate);
      System.out.println("上一次访问,时间为" + lastTimeStr);
      //获取当前时间,并存储cookie对象中
      Date currentDate = new Date();
      //            cookie.setValue(currentDate.getTime()+"");
      cookie = new Cookie("lastTime",currentDate.getTime()+"");
  }  
  response.addCookie(cookie);
8.4.2 商品浏览记录
需求:
	浏览商品,将商品的浏览的记录起来,并显示!
  
  <a href="/day56/history?id=0">西游记a><br>
  <a href="/day56/history?id=1">红楼梦a><br>
  <a href="/day56/history?id=2">水浒传a><br>
  <a href="/day56/history?id=3">三国志a><br>
/* * 获取history的Cookie对象
 * * 判断商品浏览记录是否为空
 * * 如果浏览记录没有
 * * * 创建Cookie,并将当前的商品记录到Cookie中
 * * 如果浏览记录有
 * * * 有当前的商品,不做任何处理
 * * * 没有当前商品,就需要将当前的商品拼接到已有记录中
*/
 @WebServlet(name = "HistoryServlet", urlPatterns = "/history")
public class HistoryServlet extends HttpServlet {
     
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     

    String id = request.getParameter("id");
    Cookie cookie = null;
    Cookie[] cookies = request.getCookies();
    if (null != cookies && 0 != cookies.length){
     
        for (Cookie sonCookie : cookies) {
     
            if ("history".equals(sonCookie.getName())) {
     
                cookie = sonCookie;
            }
        }
    }
  
    if (null == cookie) {
     
        //之前没有任何浏览记录 ,创建Cookie对象 ,并存储浏览记录(id)
        cookie = new Cookie("history",id);
    } else {
     
        //之前有一些浏览记录
        String historyStr = cookie.getValue();
        if (!historyStr.contains(id)) {
     
            //有一些记录,但是不包含当前浏览的商品;
            //将浏览商品拼接到已有浏览记录中
            //120
            //1-2-0
            historyStr += "-"+id;
            cookie.setValue(historyStr);
  
        } else {
     
            //有一些记录,包含当前浏览的商品 ,不做任何处理
        }
    }
    response.addCookie(cookie);
    //上述代码,已经完成了商品浏览记录功能,剩下就是要显示商品浏览记录
    response.sendRedirect(request.getContextPath()+ File.separator+"showHistory");
  }
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        doPost(request, response);
    }
}
  /*
  * 显示商品浏览记录
  * * 获取history对应的Cookie对象
  * * 获取对应的商品浏览记录
  * * 判断是否有浏览记录
  * * 如果没有,就显示“没有浏览记录”
  * * 如果有,就显示处理浏览记录字符串
  */
@WebServlet(name = "ShowHistoryServlet", urlPatterns = "/show")
public class ShowHistoryServlet extends HttpServlet {
     
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
		Cookie cookie = null;
  		Cookie[] cookies = request.getCookies();
  		if (null != cookies && 0 !=  cookies.length) {
     
      		for (Cookie sonCookie : cookies) {
     
          		if ("history".equals(sonCookie.getName())) {
     
             		 cookie = sonCookie;
          		}
     		 }
  		}
  		StringBuffer responseContent = new StringBuffer();//记录响应正文
  		if (null == cookie) {
     
		      //没有浏览记录
		      responseContent.append("没有浏览记录,");
		      responseContent.append("浏览商品");
		  } else {
     
		      //有浏览记录
		      //获取浏览记录 0-1-2-3
		      String[] bookNames = {
     "西游记","红楼梦","水浒传","三国志"};
		      String historyStr = cookie.getValue();
		      String[] historys = historyStr.split("-");
		      responseContent.append("您的浏览记录如下:
"
); for (String history : historys) { //history : 0 / 1 / 2 /3 String bookName = bookNames[Integer.parseInt(history)]; responseContent.append(bookName+"
"
); } } response.setContentType("text/html;charset=utf-8"); response.getWriter().write(responseContent.toString()); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }

------------------------------

8.5 CookieUtils工具类

获取指定名称的Cookie对象

public class CookieUtils {
     
    public static Cookie getCookie(Cookie[] cookies,String cookisName){
     
        if(cookies!=null&&cookies.length!=0){
     
            for(Cookie sonCookie:cookies){
     
                if(cookisName.equals(sonCookie.getName())){
     
                    return sonCookie;
                }
            }
        }
        return null;
    }
}


九 Session

9.1 Session基本使用

Cookie之所以叫做浏览器会话,原因是Cookie的数据存储到浏览器


Session之所以叫做服务器会话,原因是Session的数据存储到服务器

  • 执行流程
第一次请求Demo01Servlet时,根据request.getSession方法, 新建一个session对象
当第一次响应时,会将该session对象的id作为cookie头响应给浏览器保存
	set-cookie:JSESSIONID=4741C65CC84788A204E87EB870196EB0

第二次请求Demo01Servlet时,根据request.getSession方法,请求中会有cookie头
	Cookie:JSESSIONID=4741C65CC84788A204E87EB870196EB0

会根据该JSESSIONID去服务器中找有没有对应的session对象,如果有就直接用,没有就新建

------------------------------

9.2 Session相关配置

生命周期:session默认是有30分钟的存活时间,参考tomcat中的web.xml

      <session-config>
          <session-timeout>30session-timeout>
      session-config>
session和cookie是相关联的,cookie中存储了jsessionid,request.getSession方法会根据
	jsessionid去选择,到底是新建session对象,还是引用原来的session对象;

如果,将浏览器关闭了,就意味着cookie中存储的jsessionid就会销毁,
	对应request.getSession就会新建一个session对象,但是原来的session对象还存在
session只有两种情况会销毁
	调用了invalidate方法
	过了30分钟

------------------------------

9.3 session的基本使用

setAttribute	往session域对象中存储数据

getAttribute	从session域对象中获取数据

removeAttribute		把session域对象中的数据移除

------------------------------

9.4 session和request的区别

request是一次请求,session是一次会话,一次会话中包含多次请求

request中的数据(setAttritube)在重定向后失效,不同的request不能共享数据。
session中的数据无论是重定向还是转发,都能拿到。关闭浏览器失效

session默认有效期30分钟(打开浏览器,超过30分钟无操作,session失效)
重启Tomcat清空所有session

------------------------------

9.5 session和cookie的区别

session存在于服务端,可以存放任意类型的数据,依赖于cookie中的JSESSIONID
cookie存在于客户端,只能存放字符串
用户可能禁用浏览器的cookie,导致sessionid不能存储在浏览器cookie中
服务端就不能获取用户session信息
encodeRedirect方法,将用户的sessionid拼接到url后面
String url = resp.encodeRedirectURL("/admin/list");
resp.sendRedirect(url);

------------------------------

9.6 session案例

9.6.1 登录
  • UserDao
public class UserDaoImpl implements UserDao {
     
      @Override
      public User login(User inputUser) throws SQLException {
     
  //        ComboPooledDataSource就是连接池,连接池包含很多连接对象
  //        连接池作用就是减少连接的创建次数!
  //        第一个用户,登录,创建一个连接池,创建3个连接
  //        第二个用户,登录,创建一个连接池,创建3个连接
  //        应该只让连接池创建一次!!后面复用就OK了!!
          QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
          User existUser = queryRunner.query(
                  "select * from tb_user where username = ? and password = ?",
                  new BeanHandler<User>(User.class),
                  inputUser.getUsername(),
                  inputUser.getPassword());`在这里插入代码片`  
          return existUser;
      }
  }
  • LoginServlet
@WebServlet(name = "LoginServlet",urlPatterns = "/login")
  public class LoginServlet extends HttpServlet {
     
  
      private UserDao userDao = new UserDaoImpl();
  
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          String username = request.getParameter("username");
          String password = request.getParameter("password");
          User inputUser = new User();
          inputUser.setUsername(username);
          inputUser.setPassword(password);
          try {
     
              User existUser = userDao.login(inputUser);
              System.out.println(existUser);
              //判断登录成功
              if (null == existUser) {
     
                  //登录失败,请求转发,跳转到登录页面
                  request.getRequestDispatcher("/login.html").forward(request,response);
              } else {
     
                  //登录成功,重定向,跳转到显示用户信息
                  //存储existUser
                  //request : 跳转到首页,使用了重定向,会有一个新的请求
                  //servletContext : 如果存储到ServletContext,就意味着所有人都可以拿到你的用户信息!
                  //cookie : 如果存储到cookie中,就是存储到浏览器 , 不安全! cookie中是无法存储中文及一些特殊符号!!
                  //session : 数据存储到服务器!!
                  request.getSession().setAttribute("existUser",existUser);
                  response.sendRedirect("/day57/showIndex");
  
              }
  
          } catch (SQLException throwables) {
     
              throwables.printStackTrace();
          }
      }
  
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          doPost(request,response);
      }
  }
  • ShowIndexServlet
  @WebServlet(name = "ShowIndexServlet" ,urlPatterns = "/showIndex")
  public class ShowIndexServlet extends HttpServlet {
     
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          response.setContentType("text/html;charset=utf-8");
          User existUser = (User) request.getSession().getAttribute("existUser");
          if (null != existUser) {
     
              //在登录状态
              response.getWriter().write("欢迎回来,"+existUser.getUsername());
          } else {
     
              //不在登录状态
              //方式一:提示下,未登录
  //            response.getWriter().write("您还没有登录,请登录");
              //方式二:跳转到登录页面
              response.sendRedirect("/day57/login.html");
              //看需求,选择方式一还是方式二
              //登录状态权限管理!!
          }
      }
  
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          doPost(request, response);
      }
  }

第三方jar包,必须放到WEB-INF文件夹中
登录失败使用请求转发、登录成功使用重定向

---------------

9.6.2 随机验证码
  • 显示验证码
	创建图片对象
	画背景
	画边框
  	画干扰线
  	产生四位随机数,存储到session
  	画四位随机数
  	将图片响应到浏览器
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          int width = 60;//定义图片宽度
          int height = 32;//定义图片高度
          //创建图片对象
          BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
          //创建画笔对象
          Graphics g = image.getGraphics();
          //设置背景颜色
          g.setColor(new Color(0xDCDCDC));
          g.fillRect(0, 0, width, height);//实心矩形
          //设置边框
          g.setColor(Color.black);
          g.drawRect(0, 0, width - 1, height - 1);//空心矩形
  
          Random rdm = new Random();
          //画干扰椭圆
          for (int i = 0; i < 50; i++) {
     
              int x = rdm.nextInt(width);
              int y = rdm.nextInt(height);
              g.drawOval(x, y, 0, 0);
          }
          //产生随机字符串
          String hash1 = Integer.toHexString(rdm.nextInt());
          //生成四位随机验证码
          String capstr = hash1.substring(0, 4);
          //将产生的验证码存储到session域中,方便以后进行验证码校验!
          request.getSession().setAttribute("existCode", capstr);
          System.out.println(capstr);
          g.setColor(new Color(0, 100, 0));
          g.setFont(new Font("Candara", Font.BOLD, 24));
          g.drawString(capstr, 8, 24);
          g.dispose();
          //将图片响应到浏览器
          response.setContentType("image/jpeg");
          OutputStream strm = response.getOutputStream();
          ImageIO.write(image, "jpeg", strm);
          strm.close();
      }
  • 校验验证码
  //获取输入的验证码
  String validateCode = request.getParameter("validateCode");
  //将输入的验证码和产生的随机验证码进行校验
  String existCode = (String) request.getSession().getAttribute("existCode");
  if (validateCode.equals(existCode)) {
     
      //校验通过,完成登录功能
  } else {
     
      //校验不通过,跳转到登录页面
  
  }

十 过滤器

10.1 过滤器的概念

滤器就是一个用于在请求之前处理资源的组件

生命周期
	随着服务器启动而初始化
  	随着请求的发出而过滤
  	随着服务器关闭而销毁

执行流程
  	浏览器发起请求
  	服务器会根据这个请求,创建request对象及response对象
  	过滤器会持有request对象及response对象
  	只有当过滤器放行之后,request对象及response对象才会传给Serlvet

过滤器链
  	根据配置顺序,遵从"先过滤,后放行"的原则

------------------------------

10.2 过滤器的基本使用

开发步骤
  	自定义类实现Filter接口
  	重写init、doFilter、destroy方法
  	在web.xml中配置过滤器
    	声明过滤器
    	过滤器配置过滤路径
  • 过滤器
  public class Demo01Filter implements Filter {
     
      @Override
      public void init(FilterConfig filterConfig) throws ServletException {
     
          //Demo01Filter过滤器的初始化
          System.out.println("Demo01Filter初始化");
      }
  
      @Override
      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
     
          //Demo01Filter过滤器处理请求
          System.out.println("Demo01Filter放行之前");
          filterChain.doFilter(servletRequest,servletResponse);
          System.out.println("Demo01Filter放行之后");
      }
  
      @Override
      public void destroy() {
     
          //Demo01Filter过滤器的销毁
          System.out.println("Demo01Filter销毁");
      }
  }
  • web.xml
  
  <filter>
      <filter-name>Demo01Filterfilter-name>
      <filter-class>com.qfedu.filter.Demo01Filterfilter-class>
  filter>
  
  
  <filter-mapping>
      <filter-name>Demo01Filterfilter-name>
      <url-pattern>/*url-pattern>
  filter-mapping>

------------------------------

10.3 过滤器的相关配置

  • 初始化参数
    • Filter配置初始化参数
	<filter>
          <filter-name>Demo03Filterfilter-name>
          <filter-class>com.qfedu.filter.Demo03Filterfilter-class>
          
          <init-param>
              <param-name>usernameparam-name>
              <param-value>rootparam-value>
          init-param>
          <init-param>
              <param-name>passwordparam-name>
              <param-value>root123param-value>
          init-param>
  
      filter>
  • 初始化参数
    • Filter获取初始化参数
  public class Demo03Filter implements Filter {
     
  
      public void init(FilterConfig config) throws ServletException {
     
          Enumeration<String> parameterNames = config.getInitParameterNames();
          while (parameterNames.hasMoreElements()) {
     
              //获取初始化参数名称
              String parameterName = parameterNames.nextElement();
              //获取初始化参数值
              String parameterValue = config.getInitParameter(parameterName);
              System.out.println("name : " + parameterName + " , value : " + parameterValue);
          }
      }
      
      ......
      
  }
  • Filter的过滤路径
    • 针对< servlet-name > ,以为Filter仅针对Demo01Servlet进行过滤
	<filter-mapping>
        <filter-name>Demo03Filterfilter-name>
        <servlet-name>Demo01Servletservlet-name>
    filter-mapping>

---------------

  • Filter的过滤路径
    • 针对< url-pattern >
完全匹配:必须以"/"开头
      <filter-mapping>
          <filter-name>Demo03Filterfilter-name>
          <url-pattern>/aaurl-pattern>
      filter-mapping>

过滤器只过滤访问路径完全匹配"/aa"的资源

目录匹配:必须以"/"开头,以"*"结尾
      <filter-mapping>
          <filter-name>Demo03Filterfilter-name>
          <url-pattern>/aa/bb/*url-pattern>
      filter-mapping>

过滤器只过滤访问路径目录匹配到“/aa/bb“的资源

后缀名匹配:必须以"*"开头,以后缀名结尾
      <filter-mapping>
          <filter-name>Demo03Filterfilter-name>
          <url-pattern>*.jspurl-pattern>
      filter-mapping>

过滤器只过滤后缀名为jsp的资源

------------------------------

10.4 过滤器的注解开发

@WebFilter注解

	WebInitParam[] initParams() default {};		配置初始化参数

	String filterName() default "";		配置过滤器名称

	String[] servletNames() default {};		配置过滤的Servlet

	String[] urlPatterns() default {};		配置过滤路径
  • 基本使用
    @WebFilter(filterName = "Demo04Filter" ,
            urlPatterns = "/demo01",
            servletNames = "Demo01Servlet" ,
            initParams = {
     
                    @WebInitParam(name = "username",value = "root"),
                    @WebInitParam(name = "password",value = "root123")
            })
    public class Demo04Filter implements Filter {
     
    
    
        public void init(FilterConfig config) throws ServletException {
     
            Enumeration<String> parameterNames = config.getInitParameterNames();
            while (parameterNames.hasMoreElements()) {
     
                String parameterName = parameterNames.nextElement();
                String parameterValue = config.getInitParameter(parameterName);
                System.out.println(parameterName + " , " + parameterValue);
            }
        }
        
        ......
    
    }
执行顺序
    	按照过滤器的类名的字典顺序决定谁先过滤,谁先放行
    		比如AFilter、BFilter,那么AFilter就会先过滤,BFilter会先放行

------------------------------

10.5 过滤器案例

10.5.1 中文乱码
    public class EncodingFilter implements Filter {
     
    
        private String encoding = null;
    
        public void init(FilterConfig config) throws ServletException {
     
            encoding = config.getInitParameter("encoding");
        }
    
        public void destroy() {
     
        }
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
     
            //处理响应中文乱码
            resp.setContentType("text/html;charset="+encoding);
            //处理请求中文乱码
            req.setCharacterEncoding(encoding);
            chain.doFilter(req, resp);//放行
        }
    }
  • web.xml
    <filter>
        <filter-name>EncodingFilterfilter-name>
        <filter-class>com.qfedu.filter.EncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>utf-8param-value>
        init-param>
    
    filter>
    
    <filter-mapping>
        <filter-name>EncodingFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
10.5.2 自动登录
实现步骤
	登录账户后,根据是否勾选了自动登录选项框,
	判断是否访问和登录相关资源
		如果是,直接放行
		如果不是,判断是否已经在登录状态
			如果是,直接放行
			如果不是,需要从cookie中取出存储的用户信息,进行登录操作
				如果登录成功,直接放行
				如果登录失败,就跳转到登录页面
  • 登录功能
@WebServlet(name = "LoginServlet" ,urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
     
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if ("root".equals(username) && "root".equals(password)) {
     

            String autoLogin = request.getParameter("autoLogin");
            System.out.println(autoLogin);
            if ("autoLogin".equals(autoLogin)) {
     
                //进行自动登录,无非就是将用户信息(账户和密码(加密))保存起来!!!
                //request、servletContext、cookie、session
                Cookie cookie = new Cookie("autoLogin",username+"-"+password);
                cookie.setMaxAge(7 * 24 * 60 * 60);
                response.addCookie(cookie);
            }


            //登录成功,转发到一个页面,用来显示用户信息
            User existUser = new User();
            existUser.setUsername(username);
            existUser.setPassword(password);
            request.getSession().setAttribute("existUser",existUser);
            request.getRequestDispatcher("/showIndex").forward(request,response);
        } else {
     
            //登录失败,转到登录页面
            request.getRequestDispatcher("/index.jsp").forward(request,response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        doPost(request, response);
    }
}
  • LoginFilter自动登录
public class AutoLoginFilter implements Filter {
     
    public void destroy() {
     
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
     
        HttpServletRequest request = (HttpServletRequest) req;
        //获取请求路径
        String requestURI = request.getRequestURI();
        //1,判断访问的资源是否和登录相关
        if (requestURI.contains("login")) {
     
            //和登录相关的资源,直接放行
            chain.doFilter(request, resp);
        } else {
     
            //不是登录相关的资源
            //2,判断是否在登录状态
            User existUser = (User) request.getSession().getAttribute("existUser");
            if (null == existUser) {
     
                //不在登录状态 , 进行自动登录
                //获取Cookie
                Cookie cookie = CookieUtils.getCookie(request.getCookies(), "autoLogin");
                //判断cookie是否为空 , 存在浏览器清理缓存
                if (null == cookie) {
     
                    //浏览器清理缓存 , 相当于自动登录失败!! 跳转到登录页面,进行手动登录
                    request.getRequestDispatcher("/login.jsp").forward(request,resp);
                } else {
     
                    //还有缓存,进行自动登录
                    //获取用户信息 root-root
                    String infoStr = cookie.getValue();
                    String[] infos = infoStr.split("-");
                    String username = infos[0];
                    String password = infos[1];
                    if ("root".equals(username) && "root".equals(password)) {
     
                        //自动登录成功  ,修改登录状态, 直接放行  ,意味着,还不在登录状态!!!
                        existUser = new User();
                        existUser.setUsername(username);
                        existUser.setPassword(password);
                        request.getSession().setAttribute("existUser",existUser);
                        chain.doFilter(req,resp);
                    } else {
     
                        //自动登录失败 (修改了密码) ,跳转到登录页面,进行手动登录
                        request.getRequestDispatcher("/login.jsp").forward(request,resp);
                    }
                }
            } else {
     
                //在登录状态 , 直接放行
                chain.doFilter(request,resp);
            }
        }
    }

    public void init(FilterConfig config) throws ServletException {
     

    }

}

10.5.3 敏感词屏蔽
对request对象进行增强。增强获取参数相关方法
放行。传递增强的请求方法
  @WebFilter(
          filterName = "SensitiveWordsFilter" ,
          urlPatterns = "/*",
          initParams = {
     
                  @WebInitParam(name = "word1",value = "笨蛋"),
                  @WebInitParam(name = "word2" ,value = "傻瓜"),
                  @WebInitParam(name = "word3" ,value = "尼玛"),
                  @WebInitParam(name = "word4",value = "靠")
          })
  public class SensitiveWordsFilter implements Filter {
     
  
      //敏感词
      List<String> sensitiveWords = new ArrayList<>();
  
      public void init(FilterConfig config) throws ServletException {
     
          Enumeration<String> parameterNames = config.getInitParameterNames();
          while (parameterNames.hasMoreElements()) {
     
              String sensitiveWord = config.getInitParameter(parameterNames.nextElement());
              sensitiveWords.add(sensitiveWord);
          }
      }
  
      public void destroy() {
     
      }
  
  
  
      public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
     
          System.out.println("SensitiveWordsFilter doFilter");
          HttpServletRequest request = (HttpServletRequest) req;
          //增强request下的getParameter方法
          HttpServletRequest requestPrxoy = (HttpServletRequest) Proxy.newProxyInstance(
                  request.getClass().getClassLoader(),
                  request.getClass().getInterfaces(),
                  new InvocationHandler() {
     
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
                          //增强getParameter方法
                          Object returnValue = null;
                          String methodName = method.getName();
                          if ("getParameter".equals(methodName)) {
     
                              //returnValue就是getParameter方法的返回值,可能会存在敏感词
                              String returnValue1 = (String)method.invoke(request, args);
                              //开始处理敏感词
                              for (String sensitiveWord : sensitiveWords) {
     
                                  if (returnValue1.contains(sensitiveWord)) {
     
                                      //getParameter方法的返回值包含敏感词
                                      returnValue1 = returnValue1.replace(sensitiveWord,"***");
                                  }
                              }
                              return returnValue1;
                          } else {
     
                              returnValue = method.invoke(request, args);
                          }
                          return returnValue;
                      }
                  });
          chain.doFilter(requestPrxoy, resp);
      }  
  }

十一 监听器

11.1 监听器的介绍

监听器概念
	事件源:事件发生的源头
	监听器:监听事件发生
  	绑定:将监听器绑定到事件源
  	事件:能够触发监听器的事
Servlet监听器
	事件源:request域对象、session域对象、ServletContext域对象
	监听器:Servlet三种监听器
  	绑定:配置web.xml
  	事件:域对象发生改变

------------------------------

11.2 监听器的分类

一类监听器		监听域对象的创建、销毁

二类监听器		监听域对象中的属性变更(属性设置、属性替换、属性移除)

三类监听器		监听域对象的java对象的绑定
11.2.1 一类监听器
ServletRequestListener : 监听ServletRequest域对象的创建、销毁

HttpSessionListener :监听HttpSession域对象的创建、销毁

ServletContextListener : 监听ServletContext域对象的创建、销毁
开发步骤
	自定义类实现一类监听器
	重写监听器中的方法
	配置web.xml
  • 监听器
  public class MyListener01 implements ServletContextListener {
     
  
      @Override
      public void contextInitialized(ServletContextEvent sce) {
     
          //监听ServletContext域的初始化,随着服务器的启动
          System.out.println("ServletContext初始化");
      }
  
      @Override
      public void contextDestroyed(ServletContextEvent sce) {
     
          //监听ServletContext域的销毁,随着服务器的关闭
          System.out.println("ServletContext销毁");
      }
  }
  • web.xml(绑定)
  <listener>
      <listener-class>com.qfedu.listener.MyListener01listener-class>
  listener>
事件源: ServletContext域对象
监听器:ServletContextListener
绑定:  web.xml配置
事件 : ServletContext域对象发生了创建、发生了销毁
11.2.2 二类监听器
ServletRequestAttributeListener :监听ServletRequest域对象中属性变更

HttpSessionAttributeListener : 监听HttpSession域对象中属性变更

ServletContextAttributeListener : 监听ServletContext域对象中属性变更
  • 监听器
  public class MyServletContextAttributeListener implements ServletContextAttributeListener {
     
  
      @Override
      public void attributeAdded(ServletContextAttributeEvent scae) {
     
          //监听ServletContext域对象中属性添加
          System.out.println("ServletContext added");
      }
  
      @Override
      public void attributeReplaced(ServletContextAttributeEvent scae) {
     
          //监听ServletContext域对象中属性值被替换
          System.out.println("ServletContext replaced");
  
      }
  
      @Override
      public void attributeRemoved(ServletContextAttributeEvent scae) {
     
          //监听ServletContext域对象中属性值移除
          System.out.println("ServletContext removed");
  
      }
  }
  • web.xml(绑定)
  <listener>
      <listener-class>com.qfedu.listener.MyServletContextAttributeListenerlistener-class>
  listener>
11.2.3 三类监听器
HttpSessionBindingListener

	监听session域中的java对象的状态(绑定和解绑)
		绑定:将java对象存储到session域对象
		解绑:将java对象从session域对象移除
监听器组成
		事件源:java对象
		监听器:HttpSessionBindingListener
		绑定:java对象实现HttpSessionBindingListener接口
		事件:java对象在session中状态发生改变
  public class User implements HttpSessionBindingListener {
     
  
  
      @Override
      public void valueBound(HttpSessionBindingEvent event) {
     
          System.out.println("User绑定");
      }
  
      @Override
      public void valueUnbound(HttpSessionBindingEvent event) {
     
          System.out.println("User解绑");
      }
      
      ......
      
  }

HttpSessionBindingListener监听不需要在web.xml配置

------------------------------

11.3 监听器注解开发

@WebListener 相当于在web.xml绑定了监听器

  @WebListener
  public class MyServletContextLIstener implements ServletContextListener {
     
  
      @Override
      public void contextInitialized(ServletContextEvent sce) {
     
          System.out.println("ServletContext创建");
      }
  
  
      @Override
      public void contextDestroyed(ServletContextEvent sce) {
     
          System.out.println("ServletContext销毁");
      }
  }

------------------------------

11.4 监听器综合案例

11.4.1 记录登录人数
  • 开发步骤
	登录功能
    	登录失败,转发到登录页面
    	登录成功,记录登录状态,重定向到首页(显示用户名、注销登录、在线人数)
  	
  	注销登录
    	注销登录成功;正常来说,应该转发到登录页面;转发到首页
  	
  	在线人数
    	使用HttpSessionBindingListener监听器
    	使用ServletContext域对象,存储在线人数count
  • login.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录title>
head>
<body>
<form action="/day59/login" method="post">
    账户:<input type="text" name="username"/><br>
    密码:<input type="text" name="password"/><br>
    <button type="submit">登录button>
form>
body>
html>
  • bean.User
public class User implements HttpSessionBindingListener {
     
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
     
        //有人登录成功 , 在线人数(count)加1
        //判断是否是第一个登录成功的人
        //获取ServletContext
        ServletContext servletContext = event.getSession().getServletContext();
        Integer count = (Integer) servletContext.getAttribute("count");
        if (null == count) {
     
            //就是第一个登录成功的人
            count = 1;
        } else {
     
            //不是第一个登录成功的人
            count++;
        }
        servletContext.setAttribute("count", count);
    }
    //在同一个浏览器,意味着是同一个session
    //第一次登录 ,session.setAttribute("existUser","root") , valueBound  +1
    //第二次登录 ,session.setAttribute("existUser","root1") , valueBound +1  -> valueUnbound -1


    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
     
        //有人注销登录 ,在线人数(count)减1
        System.out.println("User解绑");
        ServletContext servletContext = event.getSession().getServletContext();
        Integer count = (Integer) servletContext.getAttribute("count");
        count--;
        servletContext.setAttribute("count", count);
    }

    private Integer id;
    private String username;
    private String password;

    public User() {
     
    }

    public User(Integer id, String username, String password) {
     
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public Integer getId() {
     
        return id;
    }

    public void setId(Integer id) {
     
        this.id = id;
    }

    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;
    }

    @Override
    public String toString() {
     
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

在线人数:在同一个浏览器,操作多次登录,意味着是同一个session

  • LoginServlet
@WebServlet(name = "LoginServlet", urlPatterns = "/login")
public class LoginServlet extends HttpServlet {
     
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        if ("root".equals(username) && "root".equals(password)) {
     
            //登录成功 , 修改登录状态 ,跳转到ShowIndexServlet
            User existUser = new User();
            existUser.setUsername(username);
            existUser.setPassword(password);
            request.getSession().setAttribute("existUser", existUser);
//            request.getRequestDispatcher("/showIndex").forward(request,response);
            response.sendRedirect("/day59/showIndex");
        } else {
     
            //登录失败,转发到登录页面,重新登录
            request.getRequestDispatcher("/login.html").forward(request, response);
        }

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        doPost(request, response);
    }
}
  • LogoutServlet
@WebServlet(name = "LogoutServlet", urlPatterns = "/logout")
public class LogoutServlet extends HttpServlet {
     
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //注销登录 , 将existUser变量从session域中移除!
//        request.getSession().removeAttribute("existUser");
        //注销登录,将session销毁 -> 将existUser变量从session域中移除!
        request.getSession().invalidate();
        //注销成功
        request.getRequestDispatcher("/showIndex").forward(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        doPost(request, response);
    }
}
  • ShowIndexServlet
@WebServlet(name = "ShowIndexServlet", urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
     
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        User existUser = (User) request.getSession().getAttribute("existUser");
        StringBuffer responseBody = new StringBuffer();
        if (null == existUser) {
     
            //不在登录状态,提示
            responseBody.append("您还没有登录;请登录
"
); } else { //在登录状态,显示信息 responseBody.append("欢迎回来," + existUser.getUsername() + " 注销
"
); } ServletContext servletContext = getServletContext(); //获取在线人数count Integer count = (Integer) servletContext.getAttribute("count"); System.out.println("在线人数 " + count); //count变量为null,在没有任何人登录状态下,访问了ShowIndexServlet if (null == count) { //没有任何人在登录状态 ,在线人数处理成0人 count = 0; } else { //有人在登录状态 ,直接输出在线人数count人 } responseBody.append("在线人数 : " + count); response.setContentType("text/html;charset=utf-8"); response.getWriter().write(responseBody.toString()); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }

十二 JavaWeb综合案例

12.1 用户管理与DML操作

12.1.1 用户列表
开发步骤
	获取用户列表放到首页完成
    	登录状态才需要获取用户列表
  • UserDao
      @Override
      public List<User> selectUserList() throws Exception {
     
          return new QueryRunner(JDBCUtil.getDataSource())
                  .query("select * from tb_user",
                          new BeanListHandler<User>(User.class));
      }
  • ShowIndexSerlvet
  @WebServlet(name = "ShowIndexServlet" ,urlPatterns = "/showIndex")
  public class ShowIndexServlet extends HttpServlet {
     
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          User existUser = (User) request.getSession().getAttribute("existUser");
          StringBuffer responseBody = new StringBuffer();
          if (null != existUser) {
     
              //已经在登录状态
              responseBody.append("欢迎回来~ " + existUser.getUsername());
              responseBody.append(" 注销
"
); //获取用户列表 UserDao userDao = new UserDaoImpl(); try { List<User> userList = userDao.selectUserList(); System.out.println(userList); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append("");for(User user : userList){ //遍历一个User对象,对应就应该有一个tr responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append("");} responseBody.append("
ID账户密码
"+user.getId()+""+user.getUsername()+""+user.getPassword()+"
"
); } catch (Exception e) { e.printStackTrace(); } } else { //不在登录状态 responseBody.append("您还没有登录,请登录
"
); } response.setContentType("text/html;charset=utf-8"); response.getWriter().write(responseBody.toString()); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); } }
12.1.2 删除用户
开发步骤
  	首页,给每一个记录后面添加一个删除按钮
  	用户列表不要显示当前登录用户
  • UserDao
      @Override
      public void deleteUserById(Integer id) throws Exception {
     
          new QueryRunner(JDBCUtil.getDataSource())
                  .update("delete from tb_user where id = ?",id);
      }
  • ShowIndexServlet
  responseBody.append("");
  responseBody.append("");
  responseBody.append("");
  responseBody.append("");
  responseBody.append("");
  responseBody.append("");
  responseBody.append("");for(User user : userList){
     if( user.getId()!= existUser.getId()){
     //遍历一个User对象,对应就应该有一个tr
          responseBody.append("");
          responseBody.append("");
          responseBody.append("");
          responseBody.append("");
          responseBody.append("");
          responseBody.append("");}}
  
  responseBody.append("
ID账户密码操作
"+user.getId()+""+user.getUsername()+""+user.getPassword()+"删除
"
);
  • DeleteUserServlet
  @WebServlet(name = "DeleteUserServlet" ,urlPatterns = "/deleteUser")
  public class DeleteUserServlet extends HttpServlet {
     
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          String idStr = request.getParameter("id");
          Integer id = Integer.parseInt(idStr);
          UserDao userDao = new UserDaoImpl();
          try {
     
              userDao.deleteUserById(id);
          } catch (Exception e) {
     
              e.printStackTrace();
          }
          response.sendRedirect("/day60/showIndex");
      }
  
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          doPost(request, response);
      }
  }
12.1.3 批量删除
开发流程
  	在首页需要给每一个记录新加多选框
  	当点击批量删除按钮之后,完成批量删除操作
  • UserDao
      public void deleteUsersByIds(List<Integer> idList) throws Exception {
     
          for (Integer id : idList) {
     
              deleteUserById(id);
          }
      }
  • ShowIndexServlet
  responseBody.append("
"); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append(""); responseBody.append("");for(User user : userList){ if( user.getId()!= existUser.getId()){ //遍历一个User对象,对应就应该有一个tr responseBody.append(""); responseBody.append("");...... responseBody.append("");}} responseBody.append("
ID账户密码操作
"
); responseBody.append(""); responseBody.append(""
);
12.1.4 添加用户
开发步骤
  	首页加入一个添加用户按钮
  	点击添加用户按钮进入到登录页面
  • UserDao
      public void addUser(User inputUser) throws Exception {
     
          new QueryRunner(JDBCUtil.getDataSource())
                  .update("insert into tb_user(username ,password) values(?,?)",
                          inputUser.getUsername(),
                          inputUser.getPassword());
      }
  • ShowIndexServlet
  //已经在登录状态
  responseBody.append("欢迎回来~ " + existUser.getUsername());
  responseBody.append(" 注销
"
); responseBody.append("添加用户
"
);
  • AddUserServlet
  @WebServlet(name = "AddUserServlet" ,urlPatterns = "/addUser")
  public class AddUserServlet extends HttpServlet {
     
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          String username = request.getParameter("username");
          String password = request.getParameter("password");
          User inputUser = new User();
          inputUser.setUsername(username);
          inputUser.setPassword(password);
          //方式一:在添加之前,校验下用户名是否存在
          //方式二:让username字段有唯一约束!
          UserDao userDao = new UserDaoImpl();
          try {
     
              userDao.addUser(inputUser);
              //添加成功,跳转到首页
              response.sendRedirect("/day60/showIndex");
  
          } catch (Exception e) {
     
              e.printStackTrace();
              //添加失败,跳转到添加用户页面
              response.sendRedirect("/day60/add.html");
          }
  
      }
  
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          doPost(request, response);
      }
  }
12.1.5 修改用户
开发步骤
	首页加入修改按钮
  • UserDao
      @Override
      public void updateUser(User user) throws Exception {
     
          new QueryRunner(JDBCUtil.getDataSource())
                  .update("update tb_user set username = ? , password = ? where id = ?",
                          user.getUsername(),
                          user.getPassword(),
                          user.getId());
      }
  • ShowIndexServlet
  for (User user : userList) {
     
      if ( user.getId() != existUser.getId()) {
     
          //遍历一个User对象,对应就应该有一个tr
          responseBody.append("");
          responseBody.append("");
          responseBody.append(""+user.getId()+"");
          responseBody.append(""+user.getUsername()+"");
          responseBody.append(""+user.getPassword()+"");
          responseBody.append("" +
                              "删除 " +
                              "修改"+
                              "");
          responseBody.append("");
      }
  }
  • ShowUpdateUserServlet 显示修改用户页面
  @WebServlet(name = "ShowUpdateUserServlet" , urlPatterns = "/showUpdateUser")
  public class ShowUpdateUserServlet extends HttpServlet {
     
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          Integer id = Integer.parseInt(request.getParameter("id"));
          //根据id查询用户
          UserDao userDao = new UserDaoImpl();
          StringBuffer responseBody = new StringBuffer();
          try {
     
              User user = userDao.selectUserById(id);
              System.out.println(user);
              responseBody.append("
"); responseBody.append(""); responseBody.append("账户:
"
); responseBody.append("密码:
"
); responseBody.append(""); responseBody.append(""
); } catch (Exception e) { e.printStackTrace(); } response.setContentType("text/html;charset=utf-8"); response.getWriter().write(responseBody.toString()); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
  • UpdateUserServlet 修改用户
  @WebServlet(name = "UpdateUserServlet" ,urlPatterns = "/updateUser")
  public class UpdateUserServlet extends HttpServlet {
     
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          Integer id = Integer.parseInt(request.getParameter("id"));
          String username = request.getParameter("username");
          String password = request.getParameter("password");
          System.out.println("username : " + username + ", password : " + password);
          //要修改的内容:username、password
          User user = new User(id,username,password);
          UserDao userDao = new UserDaoImpl();
          try {
     
              userDao.updateUser(user);
              //修改成功,跳转到首页
              response.sendRedirect("/day60/showIndex");
          } catch (Exception e) {
     
              e.printStackTrace();
              //修改失败,跳转到修改页面
              response.sendRedirect("/day60/showUpdateUser?id="+id);
  
          }
      }
  
  
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          doPost(request, response);
      }
  }

完整版案例

12.2 BaseServlet优化案例

12.2.1 BaseServlet 的作用

让一个Servlet可以处理多种不同的请求,不同的请求调用Servlet的不同方法。

12.2.2 实现原理
客户端发送请求时, 必须多给出一个参数, 用来说明要调用的方法。这样,BaseServlet 通过该参数来
调用目标方法。

请求处理方法的签名必须与 service 相同, 即方法的返回值和参数,以及声明的异常都相同。
12.2.3 BaseServlet的应用
  @WebServlet(name = "UserServlet",urlPatterns = "/user")
  public class UserServlet extends BaseServlet {
     
  
  
  
      public void login(HttpServletRequest request,HttpServletResponse response){
     
          String username = request.getParameter("username");
          String password = request.getParameter("password");
          User inputUser = new User();
          inputUser.setUsername(username);
          inputUser.setPassword(password);
          UserDao userDao = new UserDaoImpl();
          try {
     
              User existUser = userDao.login(inputUser);
              if (null == existUser) {
     
                  //登录失败
                  request.getRequestDispatcher("/login.html").forward(request,response);
              } else {
     
                  //登录成功
                  request.getSession().setAttribute("existUser",existUser);
                  response.sendRedirect("/day61/user?methodName=showIndex");
              }
          } catch (Exception e) {
     
              e.printStackTrace();
          }
      }
  
      public void showIndex(HttpServletRequest request , HttpServletResponse response) throws Exception {
     
          System.out.println("showIndex");
          User existUser = (User) request.getSession().getAttribute("existUser");
          StringBuffer responseBody = new StringBuffer();
          if (null != existUser) {
     
              //在登录状态
              responseBody.append("欢迎回来~~~"+existUser.getUsername());
              responseBody.append("注销登录");
          } else {
     
              //不在登录状态
              responseBody.append("您还没有登录;");
              responseBody.append("请登录");
          }
          response.setContentType("text/html;charset=utf-8");
          response.getWriter().write(responseBody.toString());
      }
  
      public void logout(HttpServletRequest request , HttpServletResponse response) throws Exception {
     
          request.getSession().invalidate();
          response.sendRedirect("/day61/login.html");
      }
  
  
  }
12.2.4 BaseServlet的优化
资源跳转问题
响应正文中文乱码
  @WebServlet(name = "BaseServlet",urlPatterns = "/base")
  public class BaseServlet extends HttpServlet {
     
      protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          response.setContentType("text/html;charset=utf-8");
          String methodName = request.getParameter("methodName");
          try {
     
              Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
              if (null != method) {
     
                  //处理返回值,为了资源跳转
                  String returnValue = (String) method.invoke(this,request,response);
                  System.out.println(returnValue);
                  //有的方法有返回值
                      //登录成功:重定向 redirect:/day61/user?methodName=showIndex
                      //登录失败: 请求转发 forward:/login.html
                  //有的方法没有返回值
                      //null,不用做任何处理
                  if (returnValue != null){
     
                      //有返回值,实现资源跳转,需要资源的路径
                      if (returnValue.lastIndexOf(":") != -1) {
     
                          String path = returnValue.split(":")[1];
                          System.out.println("path : "+path);
                          //有":"
                          //实现资源跳转:重定向、请求转发
                          if (returnValue.startsWith("redirect")) {
     
                              //重定向
                              response.sendRedirect(request.getContextPath()+path);
                          } else if(returnValue.startsWith("forward")){
     
                              //请求转发
                              request.getRequestDispatcher(path).forward(request,response);
                          }
                      } else {
     
                          //没有":",默认就是转发  /login.html
                          request.getRequestDispatcher(returnValue).forward(request,response);
                      }
                  } else {
     
                      //不做任何处理
                  }
  
              }
          } catch (Exception e) {
     
              e.printStackTrace();
          }
      }
  
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
          doPost(request, response);
      }
  }

12.3 jsp优化登录案例

之前方案
  	login.html
    	登录页面
  	LoginServlet
    	处理登录请求
    	业务处理
    	调用dao,操作数据
  	ShowIndexServlet(java代码)
    	显示用户名
    	
  	存在的问题
    	login.html
      	无法和java后台进行交互,无法显示错误信息
    	ShowIndexServlet
      	html无法和java后台进行交互,java代码应该专注于后台代码
现在方案
  	login.jsp
    	登录页面
    	显示错误信息
  	LoginServlet
    	处理登录请求
    	业务处理
    	调用dao,操作数据
  	index.jsp
   		显示用户名
  • login.jsp
  <%--显示错误信息--%>
  <%="" + (request.getAttribute("errorMsg") == null ? "" : request.getAttribute("errorMsg") ) + ""%>
  <form action="/day62/user" method="post">
      <input type="hidden" name="methodName" value="login"/>
      账户:<input type="text" name="username"/><br>
      密码:<input type="text" name="password"/><br>
      <button type="submit">登录</button>
  </form>
  • index.jsp
  <%
      String username = (String) session.getAttribute("username");
      if (null == username) {
     
          //不在登录状态
          out.write("您还没有登录;");
          out.write("请登录");
      } else {
     
          //在登录状态
          out.write("欢迎回来~~~"+username);
      }
  %>
存在的问题
  	jsp页面还存在java代码(后台代码)和html代码(前端代码)融合在一起的问题!
  	一定程度解决,可能需要jstl标签库。
  	彻底解决,得使用vue + html

12.4 三层结构优化

12.4.1 准备工作
环境准备
	tomcat8.5
  	idea2020.1
  	jdk1.8
导入jar包
  BeanUtils
  DbUtils
  c3p0
  mysql驱动
  jstl
12.4.2 登录
 开发流程
  	UserServlet
    	login方法	调用UserService的login方法
  	
  	UserService
    	login方法,返回User对象		调用UserDao的login方法
  
  	UserDao
    	login方法,返回User对象		操作数据库,并将记录封装到User对象
  	
  	登录成功
   		修改登录状态,重定向到首页
  	
  	登录失败
    	记录错误信息,请求转发到登录页面
  • UserDao
      @Override
      public User login(User inputUser) throws Exception {
     
          return new QueryRunner(JDBCUtils.getDataSource())
                  .query("select * from tb_user where username = ? and password = ?",
                          new BeanHandler<User>(User.class),
                          inputUser.getUsername(),
                          inputUser.getPassword());
      }
  • UserService
      @Override
      public User login(User inputUser) throws Exception {
     
          UserDao userDao = new UserDaoImpl();
          return userDao.login(inputUser);
      }
  • UserServlet类的login方法
      private UserService userService = new UserServiceImpl();
  
      public String login(HttpServletRequest request,HttpServletResponse response){
     
          User inputUser = new User();
          //将请求参数封装到inputUser中
          try {
     
              BeanUtils.populate(inputUser,request.getParameterMap());
              User existUser = userService.login(inputUser);
              //没有异常有返回值
              if (null != existUser) {
     
                  //登录成功 , 修改登录状态,跳转到首页
                  request.getSession().setAttribute("existUser",existUser);
                  return "redirect:/index.jsp";
              } else {
     
                  //登录失败 ,记录错误信息,跳转到登录页面
                  request.setAttribute("errorMsg","账户或密码错误");
                  return "/login.jsp";
              }
          } catch (Exception e) {
     
              e.printStackTrace();
          }
  
          //有异常,应该有返回值 。意味着登录失败,返回登录页面,重新登录
          return "/login.jsp";
  
      }
  • login.jsp
  <body>
  
  <font color="red">${errorMsg}font>
  
  <form action="${pageContext.request.contextPath}/user" method="post">
      <input type="hidden" name="methodName" value="login"/>
      账户:<input type="text" name="username"/><br>
      密码:<input type="text" name="password"/><br>
      <button type="submit">登录button>
  form>
  
  body>
  • index.jsp
  <body>
    <%--
    在登录状态
      显示用户名
    --%>
      <c:if test="${existUser != null}">
          欢迎回来~~~  ${existUser.username}  <a href="${pageContext.request.contextPath}/user?methodName=logout">注销a>
      c:if>
  
    <%--
    不在登录状态
       提示登录
    --%>
      <c:if test="${existUser == null}">
          您还没有登录,<a href="${pageContext.request.contextPath}/login.jsp">请登录a>
      c:if>
  
  body>
12.4.3 注销登录
开发步骤
  	login.jsp上需要加一个“注销”的按钮
  	
  	注销成功	跳转到登录页面
  
  	注销失败	跳转到首页,重新进行手动注销
  • UserServlet
    public String logout(HttpServletRequest request,HttpServletResponse response){
     
        try {
     
            request.getSession().invalidate();
            return "redirect:/login.jsp";
        } catch (Exception e) {
     
            e.printStackTrace();
            return "/index.jsp";
        }
    }
12.4.4 用户列表
开发步骤
  	登录成功,重定向到UserServlet中的selectUserList方法
  	在selectUserList方法中获取用户列表,并将其保存
  	再请求转发到index.jsp
  • UserDao
      @Override
      public List<User> selectUserList() throws Exception {
     
          return new QueryRunner(JDBCUtils.getDataSource())
                  .query("select * from tb_user",
                          new BeanListHandler<User>(User.class));
      }
  • UserServlet类的selectUserList方法
      public String selectUserList(HttpServletRequest request,HttpServletResponse response) throws Exception {
     
          List<User> userList = userService.selectUserList();
          request.getSession().setAttribute("userList",userList);
          return "/index.jsp";
      }
  • index.jsp
  <body>
  
    <%--
    在登录状态
      显示用户名
    --%>
      <c:if test="${existUser != null}">
          欢迎回来~~~  ${existUser.username}  <a href="${pageContext.request.contextPath}/user?methodName=logout">注销a>
          <%--显示用户列表--%>
          <table border="1px" cellspacing="0px" cellpadding="15px" width="500px" height="200px">
              <tr>
                  <td>IDtd>
                  <td>账户td>
                  <td>密码td>
              tr>
  
              <c:forEach items="${userList}" var="user">
                  <tr>
                      <td>${user.id}td>
                      <td>${user.username}td>
                      <td>${user.password}td>
                  tr>
              c:forEach>
  
  
          table>
      c:if>
  
    <%--
    不在登录状态
       提示登录
    --%>
      <c:if test="${existUser == null}">
          您还没有登录,<a href="${pageContext.request.contextPath}/login.jsp">请登录a>
      c:if>
  
  body>
12.4.5 登录校验
需求:只要不在登录状态就自动跳转到登录页面
开发步骤
  	定义过滤器  
  	判断是否和登录相关
  		和登录相关,直接放行
    		访问登录页面
    		处理登录,请求参数methodName="login"
  		和登录无关,进行登录状态判断
    		不在登录状态,跳转到login.jsp
    		在登录状态,直接放行
  • filter.LoginFilter
  @WebFilter(filterName = "LoginFilter" ,urlPatterns = "/*")
  public class LoginFilter implements Filter {
     
      public void destroy() {
     
      }
  
      public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
     
          System.out.println("LoginFilter doFilter");
          HttpServletRequest request = (HttpServletRequest) req;
          HttpServletResponse response = (HttpServletResponse) resp;
          //登录校验
          String requestURI = request.getRequestURI();
          String methodName = request.getParameter("methodName");
          if (requestURI.contains("login") || ( methodName != null && methodName.equals("login"))) {
     
              //和登录相关的资源,直接放行
              chain.doFilter(req, resp);
          } else {
     
              //和登录无关的资源
              Object existUser = request.getSession().getAttribute("existUser");
              if (null == existUser) {
     
                  //不在登录状态,跳转到login.jsp
                  response.sendRedirect("/day64/login.jsp");
              }  else {
     
                  //在登录状态,直接放行
                  chain.doFilter(req, resp);
              }
          }
      }
  
      public void init(FilterConfig config) throws ServletException {
     
  
      }
  }
12.4.6 删除用户
开发步骤
	给table每一行加删除按钮
  	不管是删除成功,还是删除失败,都应该重新获取用户列表并显示
    	转发到UserServlet类的selectUserList方法
  • index.jsp
  ......
  
  <c:forEach items="${userList}" var="user">
      <tr>
          <td>${user.id}td>
          <td>${user.username}td>
          <td>${user.password}td>
          <td>
              <a href="${pageContext.request.contextPath}/user?methodName=deleteUserById&id=${user.id}">删除a>
          td>
      tr>
  c:forEach>
  
  ......
  • UserDao
      @Override
      public int deleteUserById(Integer id) throws Exception {
     
          return new QueryRunner(JDBCUtils.getDataSource())
                  .update("delete from tb_user where id = ?",
                          id);
      }
  • UserService
      @Override
      public boolean deleteUserById(Integer id) throws Exception {
     
          return userDao.deleteUserById(id) == 1;
      }
  • UserServlet的deleteUserById方法
      public String deleteUserById(HttpServletRequest request,HttpServletResponse response){
     
          Integer id = Integer.parseInt(request.getParameter("id"));
          try {
     
              boolean flag = userService.deleteUserById(id);
          } catch (Exception e) {
     
              e.printStackTrace();
          }
          //获取用户列表
          return "/user?methodName=selectUserList";
      }
12.4.7 修改用户
开发步骤
  	在table的每一行中加修改按钮
  	当点击table中修改按钮,跳转到UserServlet类中的toUpdateUserById方法
  	UserServlet类中的toUpdateUserById方法
    	获取用户信息
    	跳转到修改用户页面
  	当点击修改页面中的修改按钮,开始修改
    	修改成功,跳转到UserServlet类中的selectUserList方法
    	修改失败,跳转到toUpdateUserById方法,让它重新进行修改用户
  • UserDao
      @Override
      public void updateUserById(User inputUser) throws Exception {
     
          new QueryRunner(JDBCUtils.getDataSource())
                  .update("update tb_user set username = ? , password = ? where id = ?",
                          inputUser.getUsername(),
                          inputUser.getPassword(),
                          inputUser.getId());
      }
  • UserServlet类toUpdateUserById方法
      public String toUpdateUserById(HttpServletRequest request,HttpServletResponse response) {
     
          Integer id = Integer.parseInt(request.getParameter("id"));
          User user = null;
          try {
     
              user = userService.selectUserById(id);
          } catch (Exception e) {
     
              e.printStackTrace();
          }
          request.setAttribute("user",user);
          return "/updateUser.jsp";
  
      }
  • updateUser.jsp
  <body>
  <font color="red">${
     errorMsg}</font>
  <form action="${pageContext.request.contextPath}/user" method="post">
  
      <input type="hidden" name="methodName" value="updateUserById"/>
      <input type="hidden" name="id" value="${user.id}">
      账户:<input type="text" name="username" value="${user.username}"><br>
      密码:<input type="text" name="password" value="${user.password}"><br>
      <button type="submit">修改</button>
  </form>
  
  </body>
  • UserServlet类updateUserById方法
      public String updateUserById(HttpServletRequest request,HttpServletResponse response){
     
          User inputUser = new User();
          try {
     
  
              BeanUtils.populate(inputUser,request.getParameterMap());
              userService.updateUserById(inputUser);
              //修改成功
              return "/user?methodName=selectUserList";
          } catch (Exception e) {
     
              e.printStackTrace();
          }
          request.setAttribute("errorMsg","修改失败");
          //修改失败
          return "/user?methodName=toUpdateUserById&id="+inputUser.getId();
      }

十三 分页查询

分页查询概念在MySQL知识点总结10.8

13.1 数据库的物理分页

select * from 表名 limit ? , ?
#第一个?:开始查询脚标(从0开始)
#第二个?:查询多少个
# 需求:每页数据有2条
  -- 第一页 1、2;开始脚标为0
  -- 从脚标为0的记录开始,往后查2个!
  SELECT * FROM tb_user LIMIT 0 , 2;
  
  -- 第二页 3、5;开始脚标为2
  SELECT * FROM tb_user LIMIT 2 , 2;
  
  -- 第三页 7、8;开始脚标为4
  SELECT * FROM tb_user LIMIT 4 , 2;
  
  -- 第四页 12;开始脚标为6
  SELECT * FROM tb_user LIMIT 6 , 2;

13.2 分页查询流程分析

问题一:当浏览器发起分页请求时,浏览器需要传递什么参数给服务器?
答:当前页数

问题二:当服务器响应浏览器时,服务器需要传递什么参数给浏览器?
答:都是浏览器需要的数据;如下:
    	当前页数(currentPage)
    	总页数(totalPage)
    	总记录数(totalSize)
    	每页记录数(pageSize)
    	当前页数据(list)
	
问题三:当服务器向浏览器响应上面的五个参数时,是将五个参数作为整体传递还是独立传递?
答:服务器向浏览器传递参数,无非是通过域!
  	如果是独立传递,那么就需要在域中存储五个参数(currentPage、totalPage、totalSize、pageSize、list)
  	如果是整体传递,那么只需要在域中存储一个参数(PageBean)
总结:将五个参数作为整体

问题四:分页对象PageBean中的五个参数是在哪一层代码中确定的?
答:当前页数(currentPage):controller层
  	总页数(totalPage):service层
  	总记录数(totalSize):获取是在dao层,调用是在service层
  	每页记录数(pageSize):service层
  	当前页数据(list):获取是在dao层
总结:以上五个参数封装成分页对象(PageBean),是在service层进行的

13.3 分页代码实现

步骤:
		确定每页显示数据数量。
		确定分页显示所需总页数
		编写SQL查询语句,实现数据查询
		在JSP页面中进行分页显示设置
pageSize = 10 //每页10条数据
total = 100; //一共100条数据 分10页
total = 105; // 一共105条数据 分11页

if(total % pageSize == 0){
     
	num = total / pageSize; //页数
}
if(total % pageSize != 0){
     
	num = total / pageSize + 1; //页数
}

int pageNum ; //当前页码
limit (pageNum-1)*pageSize,pageSize //sql语句
13.3.1 后台代码
  • UserServlet
处理当前页数
获取分页对象PageBean
页面跳转
      public String selectUserListByPage(HttpServletRequest request, HttpServletResponse response){
     
          String currentPageStr = request.getParameter("currentPage");
          Integer currentPage = getCurrentPage(currentPageStr);
          //调用UserService,传递currentPage
          try {
     
              PageBean<User> pageBean = userService.selectUserListByPage(currentPage);
              System.out.println(pageBean);
              request.setAttribute("pageBean",pageBean);
              //转发到用户列表页面
              return "/userList.jsp";
          } catch (Exception e) {
     
              e.printStackTrace();
          }
          return "/index.jsp";
  
      }
  • UserService
设置当前页数(currentPage)
设置总记录数(totalSize)
设置每页记录数(pageSize)
设置总页数(totalPage)	总页数 = (总记录数 % 每页记录数 == 0) ? 总记录数/ 每页记录数 : 总记录数/ 每页记录数 + 1;
设置当前页数据(list)	begin = (currentPage - 1) * pageSize
      public PageBean<User> selectUserListByPage(Integer currentPage) throws Exception {
     
          PageBean<User> pageBean = new PageBean<>();
          //    currentPage;当前页数
          pageBean.setCurrentPage(currentPage);
          //    totalSize;总记录数
          UserDao userDao = new UserDaoImpl();
          Integer totalSize = userDao.selectTotalSize();
          pageBean.setTotalSize(totalSize);
          //    pageSize;每页记录数
          Integer pageSize = 3;
          pageBean.setPageSize(pageSize);
          //    totalPage:总页数  = 总记录数 / 每页记录数
          Integer totalPage = ( totalSize % pageSize == 0 ) ? ( totalSize / pageSize ) : (totalSize / pageSize + 1 ) ;
          pageBean.setTotalPage(totalPage);
          //    list;当前页数据
          //select * from tb_user limit ? , ?
          //第一个? : 开始脚标 = (当前页数 - 1) * 每页记录数
          //第二个? : 查询多少个 (每页记录数) pageSize
          Integer begin = (currentPage - 1) * pageSize;
          List<User> list = userDao.selectUserListByPage(begin, pageSize);
          pageBean.setList(list);
          return pageBean;
      }
  • UserDao
获取总记录数
获取当前页数据
  public class UserDaoImpl implements UserDao {
     
      @Override
      public Integer selectTotalSize() throws Exception {
     
          return new QueryRunner(JDBCUtils.getDataSource())
                  .query("select * from tb_user",
                          new BeanListHandler<User>(User.class))
                  .size();
      }
  
      @Override
      public List<User> selectUserListByPage(Integer begin, Integer pageSize) throws Exception {
     
          return new QueryRunner(JDBCUtils.getDataSource())
                  .query("select * from tb_user limit ? , ?" ,
                          new BeanListHandler<User>(User.class),
                          begin,
                          pageSize);
      }
  }
13.3.2 前端代码
  <body>
  
  <table border="1px" cellspacing="0px" cellpadding="10px" width="600px" height="200px">
      <tr>
          <td>IDtd>
          <td>账户td>
          <td>密码td>
      tr>
      <c:forEach items="${pageBean.list}" var="user">
          <tr>
              <td>${user.id}td>
              <td>${user.username}td>
              <td>${user.password}td>
          tr>
      c:forEach>
      <%--分页工具条--%>
      <tr>
          <td colspan="3" align="center">
              第${pageBean.currentPage}/${pageBean.totalPage}页
              总记录数:${pageBean.totalSize}
              每页显示${pageBean.pageSize}条
  
              <c:if test="${pageBean.currentPage != 1}">
                  <a href="${pageContext.request.contextPath}/user?methodName=selectUserListByPage¤tPage=1"> 
                      [首页] 
                  a>
                  <a href="${pageContext.request.contextPath}/user?methodName=selectUserListByPage¤tPage=${pageBean.currentPage-1}"> 
                      [上一页] 
                  a>
              c:if>
  
              <c:if test="${pageBean.currentPage != pageBean.totalPage}">
                  <a href="${pageContext.request.contextPath}/user?methodName=selectUserListByPage¤tPage=${pageBean.currentPage+1}"> 
                      [下一页] 
                  a>
                  <a href="${pageContext.request.contextPath}/user?methodName=selectUserListByPage¤tPage=${pageBean.totalPage}"> 
                      [尾页] 
                  a>
              c:if>
  
          td>
      tr>
  table>
  
  
  body>

13.4 另一版本

通过Page类分页查询


十四 文件上传

14.1 概念及原理

本质就是将一台电脑中的文件根据网络协议通过io流传递到另外一台电脑(服务器)上

网络编程
	ip:电脑在网络中的唯一标识
  	port:软件在电脑中的唯一标识
  	协议:不同设备进行沟通的规则!
概念:
	当用户在前端页面点击文件上传后,文件数据交给服务器端,实现保存
步骤:	
	编写文件上传表单
	编写服务器程序接收文件

文件上传表单提交方式:post
表单提交类型:mulitipart/form-data

14.2 页面实现

<form action="" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="up">
    <input type="submit" value="上传">
form>

14.3 编写服务器程序

核心步骤:
	· 编写Servlet
	· 使用@MultipartConfig注解
	· 使用javax.servlet.http.Part接口接收文件类型
14.3.1 文件上传
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件上传
 * 如果让Servlet支持文件上传,需要加一个注解@MultipartConfig(servlet3.0以后才有)
 */
@WebServlet(name = "UploadServlet", urlPatterns = "/upload")
// maxFileSize单个文件大小,单位字节;
// maxRequestSize请求中多个文件的总大小;
@MultipartConfig(maxFileSize = 1024 * 1024, maxRequestSize = 1024 * 1024 * 2)
public class UploadServlet extends HttpServlet {
     


    // 限制上传文件类型
    private List<String> FILE_TYPES = new ArrayList<>();

    public UploadServlet() {
     
        FILE_TYPES.add(".jpg");
        FILE_TYPES.add(".png");
        FILE_TYPES.add("gif");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        //拿到文件
        Part part = request.getPart("up");
        String fileName = part.getSubmittedFileName();//用户上传的文件名
        String ext = fileName.substring(fileName.lastIndexOf("."));//扩展名
        PrintWriter writer = response.getWriter();
        if (FILE_TYPES.contains(ext)) {
     
            part.write("D:\\upload\\" + System.currentTimeMillis() + ext);//以文件流的形式存入磁盘,加上时间戳
            writer.write("上传成功");
        } else {
     
            writer.write("文件格式错误");
        }

    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        doPost(request, response);
    }
}
13.3.2 文件下载
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

@WebServlet(name = "DownloadServlet", urlPatterns = "/download")
public class DownloadServlet extends HttpServlet {
     
    private String path = "D:\\load\\";// 服务端存放文件的地方
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        String name = request.getParameter("name");
        response.setHeader("content-disposition","attachment:filename="+ URLEncoder.encode(name,"utf-8"));
        File file = new File(path+name);
        FileInputStream inputStream = new FileInputStream(file);//从系统中读取文件
        ServletOutputStream outputStream = response.getOutputStream();// 响应流
        // 从水池1中取水,放到水池2
        byte[] buf = new byte[1024];//水桶
        int len = 0;//水桶每次从水池1拿多少水,等于-1,代表水池已经空了
        while ((len = inputStream.read(buf))!=-1) {
     //从水池1中给水桶灌水
            outputStream.write(buf,0,len);//把水桶里的水,倒进水池2,偏移量 最后一次可能桶没装满
        }
        outputStream.close();
        inputStream.close();
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        doPost(request, response);
    }
}
13.3.3 多文件上传
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@WebServlet(name = "MutilUploadServlet", urlPatterns = "/mutilupload")
@MultipartConfig(maxFileSize = 1024 * 1024, maxRequestSize = 1024 * 1024 * 2)
public class MutilUploadServlet extends HttpServlet {
     
    private List<String> FILE_TYPES = new ArrayList<>();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //拿到文件
        Collection<Part> parts = request.getParts();//多文件上传,拿到的是请求中所有的内容,包括文字和文件
        for (Part part : parts) {
     
            String name = part.getName();//参数名
            String fileName = part.getSubmittedFileName();//代表当前的part是一个文字参数,文字参数没有文件名
            if (fileName != null) {
     //是文件
                System.out.println("文件名" + fileName);
                System.out.println(request.getRemoteAddr());
                String ext = fileName.substring(fileName.lastIndexOf("."));
                PrintWriter writer = response.getWriter();
                if (FILE_TYPES.contains(ext)) {
     
                    part.write("D:\\upload\\" + System.currentTimeMillis() + ext);//以文件流的形式,写入磁盘
                    writer.write("上传成功");
                } else {
     
                    writer.write("文件格式错误");
                }
            } else {
     
                System.out.println(name + "=" + request.getParameter(name));
            }
        }
    }

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

十五 BaseServlet

BaseServlet

你可能感兴趣的:(笔记)