web知识点总结

一 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
    2
  • 3 点击Configure -> 点击加号 -> 选择tomcat
    3
    45

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

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 服务器编译环境设置

1
2
3
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

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--3,配置Servlet-->
    <!--3.1,声明Servlet-->
    <servlet>
        <!--servlet的名称-->
        <servlet-name>Demo01Servlet</servlet-name>
        <!--servlet的全类名-->
        <servlet-class>com.qfedu.servlet.Demo01Servlet</servlet-class>
    </servlet>
    <!--
        3.2,给Servlet设置访问名称
           Servlet should have a mapping
    -->
    <servlet-mapping>
        <!--servlet的名称-->
        <servlet-name>Demo01Servlet</servlet-name>
        <!--servlet的访问名称-->
        <url-pattern>/demo01</url-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();
}
  • 第一次访问servlet,servlet会被创建,并将servlet对象常驻内存,调用init方法进行初始化操作,init方法中执行一次
  • 调用service方法,用于处理来自浏览器端的请求,以后都是开启一个线程来处理浏览器端请求
  • 当tomcat服务器正常关闭时,会调用destroy方法将servlet销毁

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

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>Demo01Servlet</servlet-name>
        <servlet-class>servlet.Demo01Servlet</servlet-class>
    </servlet>

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

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

​必须以"/"开头,否则会报错:IllegalArgumentException : Invalid <url-pattern>

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

  • 目录匹配
要求网址上的访问名称中的目录和<url-parttern>一致

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

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

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

  • 后缀名匹配
要求网址上的访问名称中的后缀和<url-parttern>一致

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

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

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

5.8 缺省Servlet

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

  • 缺省Servlet的作用
凡是在web.xml文件总找不到匹配的<servlet-mapping>元素的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>Demo11Servlet</servlet-name>
        <servlet-class>servlet.Demo11Servlet</servlet-class>
        <init-param>
            <param-name>username</param-name>
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>root123</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>Demo11Servlet</servlet-name>
        <url-pattern>/demo11</url-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<String,Object>,可以通过它实现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>username</param-name>
        <param-value>root</param-value>
    </context-param>
    <servlet>
        <servlet-name>Demo15Servlet</servlet-name>
        <servlet-class>servlet.Demo15Servlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Demo15Servlet</servlet-name>
        <url-pattern>/demo15</url-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方法
    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 请求转发和重定向

请求次数
  * 重定向有2次请求
  * 转发有1次请求

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

路径区别
  * 要转发的资源的相对路径无区别
  * 要转发的资源的绝对路径有区别
    * 重定向,是从先从项目开始找,再找资源
    * 转发,是直接从项目中找资源
/**
 * 重定向和转发的区别之站内资源、站外资源
 */
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就可以不随着会话的关闭而销毁
  • 路径设置
默认情况下,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 商品浏览记录
需求:
	浏览商品,将商品的浏览的记录起来,并显示!
  <!--页面代码 books.html-->
  <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("<font color='red'>没有浏览记录</font>,");
		      responseContent.append("<a href='books.html'>浏览商品</a>");
		  } else {
		      //有浏览记录
		      //获取浏览记录 0-1-2-3
		      String[] bookNames = {"西游记","红楼梦","水浒传","三国志"};
		      String historyStr = cookie.getValue();
		      String[] historys = historyStr.split("-");
		      responseContent.append("您的浏览记录如下:<br>");
		      for (String history : historys) {
		          //history : 0 / 1 / 2 /3
		          String bookName = bookNames[Integer.parseInt(history)];
		          responseContent.append(bookName+"<br>");
		      }
		  }
		  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>30</session-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案例

9.4.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("您还没有登录,<a href='/day57/login.html'>请登录</a>");
              //方式二:跳转到登录页面
              response.sendRedirect("/day57/login.html");
              //看需求,选择方式一还是方式二
              //登录状态权限管理!!
          }
      }
  
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          doPost(request, response);
      }
  }

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

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

9.4.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
  <!--声明Demo01Filter过滤器-->
  <filter>
      <filter-name>Demo01Filter</filter-name>
      <filter-class>com.qfedu.filter.Demo01Filter</filter-class>
  </filter>
  
  <!--配置Demo01Filter的过滤路径-->
  <filter-mapping>
      <filter-name>Demo01Filter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

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

10.3 过滤器的相关配置

  • 初始化参数
    • Filter配置初始化参数
	<filter>
          <filter-name>Demo03Filter</filter-name>
          <filter-class>com.qfedu.filter.Demo03Filter</filter-class>
          <!--初始化参数-->
          <init-param>
              <param-name>username</param-name>
              <param-value>root</param-value>
          </init-param>
          <init-param>
              <param-name>password</param-name>
              <param-value>root123</param-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>Demo03Filter</filter-name>
        <servlet-name>Demo01Servlet</servlet-name>
    </filter-mapping>

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

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

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

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

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

后缀名匹配:必须以"*"开头,以后缀名结尾
      <filter-mapping>
          <filter-name>Demo03Filter</filter-name>
          <url-pattern>*.jsp</url-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>EncodingFilter</filter-name>
        <filter-class>com.qfedu.filter.EncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    
    </filter>
    
    <filter-mapping>
        <filter-name>EncodingFilter</filter-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.MyListener01</listener-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.MyServletContextAttributeListener</listener-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 监听器综合案例

11.3.1 记录登录人数
  • login.html
<!DOCTYPE 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 + '\'' +
                '}';
    }
}
  • 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("您还没有登录;<a href='/day59/login.html'>请登录</a><br>");
        } else {
            //在登录状态,显示信息
            responseBody.append("欢迎回来," + existUser.getUsername() + "   <a href='/day59/logout'>注销</a><br>");
        }
        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);
    }
}
发布了62 篇原创文章 · 获赞 0 · 访问量 1984

猜你喜欢

转载自blog.csdn.net/qq_41841482/article/details/105418939