【JavaWeb】Servlet学习

Servlet

文章目录

Servlet介绍

Servlet是Java Web开发中的一个重要组件,它是在Web服务器中运行的Java程序,用于处理客户端请求并生成响应。Servlet通常用于构建动态Web应用程序,如电子商务网站、社交媒体平台、在线银行服务等。

Servlet是一个Java类,它实现了javax.servlet.Servlet接口,该接口定义了Servlet的生命周期和处理请求的方法。Servlet容器(如Tomcat、Jetty等)在Web服务器中运行,它负责管理Servlet的生命周期,包括加载、实例化、初始化、调用服务方法和销毁。

Web服务器-Tomcat

Tomcat是一个开源的Web服务器,它是基于Java Servlet和JavaServer Pages(JSP)技术的Web应用程序容器。Tomcat的工作机制可以简单概括为以下几个步骤:

  1. 接收HTTP请求:当客户端向Tomcat发送HTTP请求时,Tomcat会接收到该请求并进行处理。

  2. Servlet容器:Tomcat内部包含了一个Servlet容器,它负责管理Servlet的生命周期、处理Servlet请求和响应等。

  3. Servlet实例化:当Servlet容器接收到一个请求时,它会根据请求中的URL信息找到对应的Servlet,并创建该Servlet的一个实例。

  4. Servlet处理请求:Servlet实例会调用doGet()、doPost()等方法来处理请求,并生成响应。

  5. JSP处理:如果请求中包含JSP页面,Tomcat会将该页面编译成Servlet,并在Servlet容器中运行。

  6. 响应客户端:Tomcat将生成的响应发送回客户端,完成整个请求-响应周期。


Servlet接口

Servlet编程需要实现Servlet接口,或者可以通过继承GenericServlet或HttpServlet抽象类,这两个类都是实现了Servlet接口。一般称实现Servlet接口的类为动态资源文件,浏览器可以通过发送请求调用动态资源文件,来完成数据处理、数据库查询、生成动态网页等。

Servlet接口介绍:
  • Servlet编程可以直接实现Servlet接口来创建动态资源文件,其中共有五个方法,都是抽象方法,没有方法体,需要程序员自己定义。

  • GenericServlet抽象类实现Servlet接口,在原生Servlet接口上实现了除service方法外的所有方法,进而简化程序员开发流程,我们可以继承GenericServlet抽象类,实现service方法,在方法中定义处理业务代码。

  • HttpServlet抽象类继承自GenericServlet抽象类,GernericServlet更像一个通用的Servlet接口的抽象实现类,而HttpServlet则是基于http协议的一个Servlet接口的抽象实现类,其中service抽象方法已经定义好了,程序员只需要针对于不同http中不同请求提交方式来重写对应do方法,来做到不同请求提交方式下不同的数据处理代码,以完成Servlet程序的功能。在开发中,最常见的是继承HttpServlet类。

类图:

Servlet继承链

Servlet接口源码:

Servlet接口源码

GenericServlet抽象类:

GenerciServlet方法

HttpServlet抽象类:

HttpServlet方法

Servlet实现类开发步骤:
  1. 在webapp文件下新建lib文件夹,用来保存外部jar包;
  2. 将servlet-api.jar放入lib文件夹中,并将lib文件作为项目或模块的libaray(库);
  3. 自定义类,继承HttpServlet抽象类;
  4. 重写doGet和doPost方法(常见的),可以根据自己需求重写对应请求方式的do方法。
  5. 在web.xml文件中,添加声明servlet名称和URI
1. 导入servlet-api.jar

导入servlet-api的jar包

2. 将lib文件夹设为项目或模块的库

设置文件夹作为库

3. 自定义类继承HttpServlet类,并重写对应do方法

定义HttpServlet子类

4. 在web.xml中声明servlet名称和URI

在web.xml声明servlet名称和URI


Servlet对象的生命周期

  1. 创建:当浏览器第一次通过URL请求对应servlet资源时,Web服务器会自动创建servlet对象,并调用其中init方法;

servlet自启动: 特殊情况下,当在web.xml中为对应servlet设置load-on-startup的内容为正整数(默认为0),一旦服务器启动,该servlet对象就会被创建;

  1. 处理:调用service方法,进行业务实现;

  2. 销毁:Web服务器关闭时,servlet对象会进行销毁,调用destory方法。

Servlet自启动:设置load-on-startup

sevlet自启动


Servlet中的注解

在Servlet 3.0中,常见的注解有以下几种:

  1. @WebServlet:用于将Servlet类声明为一个Web应用程序的Servlet,可以指定Servlet的URL映射、初始化参数等信息。

  2. @WebFilter:用于声明一个过滤器,可以拦截请求和响应,对它们进行处理或修改。

  3. @WebListener:用于声明一个监听器,可以监听Web应用程序的生命周期事件,如contextInitialized、contextDestroyed等。

  4. @WebInitParam:用于为Servlet或过滤器指定初始化参数,可以在代码中通过getInitParameter()方法获取这些参数的值。

  5. @MultipartConfig:用于指定Servlet支持文件上传,可以设置上传文件的大小限制、临时文件路径等参数。

注意: 注解是在Servlet 3.0引入的,Servlet 3.0之前无法通过注解来配置Servlet。

@WebServlet

@WebServlet用于将一个类声明为Servlet组件。通过@WebServlet注解,我们可以在Java代码中指定Servlet的URL映射、初始化参数、加载顺序等信息,从而简化Servlet组件的配置和管理。

@WebServlet注解的常用属性包括:

  • name:Servlet组件的名称,默认为类名;
  • urlPatterns:Servlet组件处理的URL模式,可以是一个字符串数组;
  • value:与urlPatterns属性相同,用于指定Servlet组件处理的URL模式;
  • loadOnStartup:Servlet组件的加载顺序,值越小越先加载;
  • initParams:Servlet组件的初始化参数,可以是一个数组,每个元素是一个@WebInitParam注解。

下面是一个使用@WebServlet注解的示例:

@WebServlet(name = "MyServlet", urlPatterns = { "/hello" }, initParams = {
        @WebInitParam(name = "message", value = "Hello, World!") })
public class MyServlet extends HttpServlet {

    private String message;

    public void init() throws ServletException {
        message = getServletConfig().getInitParameter("message");
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html");

        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }

    public void destroy() {
        // do nothing.
    }
}

在这个示例中,我们使用@WebServlet注解将MyServlet类声明为Servlet组件,并指定了它处理的URL模式为/hello,初始化参数为message=Hello, World!。在Servlet的doGet()方法中,我们获取初始化参数,并将其作为响应输出到客户端。

@WebFilter

@WebFilter注解用于在Web应用程序中定义过滤器。过滤器是一种拦截HTTP请求和响应的组件,可以对请求进行处理或修改,也可以对响应进行处理或修改,从而实现一些通用的功能,比如安全验证、日志记录、字符编码转换等。

@WebFilter注解可以用来指定过滤器的名称、URL模式、执行顺序等属性。下面是一个@WebFilter注解的示例:

@WebFilter(filterName = "myFilter", urlPatterns = {"/*"})
public class MyFilter implements Filter {
    // 过滤器的实现代码
}

这个示例中,@WebFilter注解指定了过滤器的名称为"myFilter",URL模式为"/*",表示该过滤器将拦截所有的HTTP请求。过滤器的实现类实现了Filter接口,并实现了doFilter()方法,用于处理HTTP请求和响应。

在应用程序中使用@WebFilter注解定义过滤器可以简化过滤器的配置,避免在web.xml文件中进行繁琐的配置。

@WebListener

@WebListener注解是Java Servlet规范中定义的一种监听器注解,用于标记一个类为Web应用程序的监听器。Web应用程序的监听器是一种特殊的Java类,它可以监听Web应用程序中的事件,例如Servlet的生命周期事件、Session的创建和销毁事件、ServletContext的属性变化事件等。当Web应用程序中发生这些事件时,监听器会自动调用相应的方法进行处理。

@WebListener注解可以应用在实现了ServletContextListener、ServletRequestListener、HttpSessionListener等接口的Java类上。例如,我们可以定义一个实现ServletContextListener接口的Java类,用@WebListener注解标记它,这样它就可以监听ServletContext的生命周期事件。具体来说,当Web应用程序启动时,监听器会自动调用contextInitialized方法;当Web应用程序关闭时,监听器会自动调用contextDestroyed方法。

下面是一个使用@WebListener注解定义的ServletContextListener示例:

@WebListener
public class MyServletContextListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("Web应用程序启动了!");
    }
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("Web应用程序关闭了!");
    }
}

在上面的示例中,MyServletContextListener类实现了ServletContextListener接口,并使用@WebListener注解标记它。当Web应用程序启动时,contextInitialized方法会被自动调用,输出“Web应用程序启动了!”;当Web应用程序关闭时,contextDestroyed方法会被自动调用,输出“Web应用程序关闭了!”。

通过定义监听器,我们可以在Web应用程序中监听各种事件,实现特定的业务逻辑。

@WebInitParam

@WebInitParam可以用来配置Servlet的初始化参数。通过@WebInitParam注解,我们可以在Servlet类上定义一些初始化参数,这些参数可以在Servlet初始化时被读取和使用。

@WebInitParam注解可以添加到@WebServlet注解中,也可以添加到@WebFilter注解或@WebListener注解中。其语法如下:

@WebInitParam(name = "paramName", value = "paramValue")

其中,name属性表示初始化参数的名称,value属性表示初始化参数的值。如果有多个初始化参数,可以使用数组的方式进行定义,如下所示:

@WebInitParam(name = "param1", value = "value1"),
@WebInitParam(name = "param2", value = "value2")

在Servlet中,可以通过getInitParameter()方法来获取这些初始化参数的值,例如:

String paramValue = getInitParameter("paramName");

这样,我们就可以方便地在Servlet中读取和使用这些初始化参数了。

HttpServletRequest接口

浏览器向服务器发送请求协议包,服务器会创建HttpServletRequest对象(简称请求对象),用来保存请求协议包中浏览器所发送的所有请求内容。服务器中Servlet程序可以通过请求对象来获取请求参数、请求行、请求头等。

获取request对象指定的参数值:

获取请求对象指定参数值

注意: 使用getParamter方法时,如果指定的参数有多个值时,只会取URI中第一个参数出现的值。

获取request对象所有参数值:

请求对象所有参数值

获取request对象所有请求头值:

获取请求对象所有请求头值


HttpServletResponse接口

服务器根据浏览器发送的请求协议包进行相应操作后,会得到响应结果写入到HttpServletResponse对象(简称响应对象)中,然后将HttpServletResponse对象中的数据变为响应协议包,并将响应协议包发送给浏览器。响应对象中保存响应协议包中服务器向客户端发送的所有数据,例如状态行、响应头、响应体等。

获取response对象所有响应头

获取响应对象所有响应头值

向响应体中写入普通文本数据

向响应体中写入普通文本

向响应体中写入HTML页面

向响应体中写入HTML网页


GET和POST编码字符集问题

  • GET请求方式中,参数值解码是由服务器来完成,而服务器默认的编码方式是 ‘UTF-8’,如果参数值有中文,不会出现乱码

  • POST请求方式中,参数值解码是由request对象来完成的,其默认的编码方式是ISO-8859-1,所以直接解码会中文会变成乱码

问题:

GET和POST编码字符集问题

解决方案1:

GET和POST编码字符集问题解决方案

解决方案2:

GET和POST编码字符集问题解决方案2


请求对象和响应对象的生命周期

  1. 当浏览器向服务器请求动态资源时,会生成Http请求协议包发送给服务器。

  2. 服务器在接收到请求协议包后,会自动生成请求对象和响应对象,并将其作为service的两个参数。

  3. 在service方法中判断此次请求是哪种方式(GET、POST…),又将请求对象和响应对象作为参数调用doGet或doPost方法。

  4. 服务器在完成对请求的处理后,会向浏览器发送Http响应协议包。

  5. 在浏览器接收到服务器的响应后,服务器会销毁请求对象和响应对象。

请求对象保存所有浏览器发送过来的信息,响应对象保存服务器处理完浏览器发送过来的请求所生成的响应结果。

请求对象和响应对象生命周期


状态码

当服务器向浏览器发送响应协议包,其中状态行保存状态码这一部分,用来表示服务器对前面浏览器发送请求的回应。

常见的状态码:

  • 2xx:一般连接正常

  • 4xx:浏览器发送的请求存在错误

  • 5xx:服务端程序出现错误

状态码详解
代码消息描述
100Continue只有请求的一部分已经被服务器接收,但只要它没有被拒绝,客户端应继续该请求。
101Switching Protocols服务器切换协议。
200OK请求成功。
201Created该请求是完整的,并创建一个新的资源。
202Accepted该请求被接受处理,但是该处理是不完整的。
203Non-authoritative Information
204No Content
205Reset Content
206Partial Content
300Multiple Choices链接列表。用户可以选择一个链接,进入到该位置。最多五个地址。
301Moved Permanently所请求的页面已经转移到一个新的 URL。
302Found所请求的页面已经临时转移到一个新的 URL。
303See Other所请求的页面可以在另一个不同的 URL 下被找到。
304Not Modified
305Use Proxy
306Unused在以前的版本中使用该代码。现在已不再使用它,但代码仍被保留。
307Temporary Redirect所请求的页面已经临时转移到一个新的 URL。
400Bad Request服务器不理解请求。
401Unauthorized所请求的页面需要用户名和密码。
402Payment Required您还不能使用该代码。
403Forbidden禁止访问所请求的页面。
404Not Found服务器无法找到所请求的页面。.
405Method Not Allowed在请求中指定的方法是不允许的。
406Not Acceptable服务器只生成一个不被客户端接受的响应。
407Proxy Authentication Required在请求送达之前,您必须使用代理服务器的验证。
408Request Timeout请求需要的时间比服务器能够等待的时间长,超时。
409Conflict请求因为冲突无法完成。
410Gone所请求的页面不再可用。
411Length Required“Content-Length” 未定义。服务器无法处理客户端发送的不带 Content-Length 的请求信息。
412Precondition Failed请求中给出的先决条件被服务器评估为 false。
413Request Entity Too Large服务器不接受该请求,因为请求实体过大。
414Request-url Too Long服务器不接受该请求,因为 URL 太长。当您转换一个 “post” 请求为一个带有长的查询信息的 “get” 请求时发生。
415Unsupported Media Type服务器不接受该请求,因为媒体类型不被支持。
417Expectation Failed
500Internal Server Error未完成的请求。服务器遇到了一个意外的情况。
501Not Implemented未完成的请求。服务器不支持所需的功能。
502Bad Gateway未完成的请求。服务器从上游服务器收到无效响应。
503Service Unavailable未完成的请求。服务器暂时超载或死机。
504Gateway Timeout网关超时。
505HTTP Version Not Supported服务器不支持"HTTP协议"版本。
设置服务器响应的状态码

设置服务器响应的状态码


Web的URI路径

常见路径
路径描述
<img src="picture.jpg">picture.jpg 位于与当前网页相同的文件夹
<img src="images/picture.jpg">picture.jpg 位于当前文件夹的 images 文件夹中
<img src="/picture.jpg">picture.jpg 当前站点根目录的 images 文件夹中
<img src="../picture.jpg">picture.jpg 位于当前文件夹的上一级文件夹中
Tomcat的webapps文件目录

对于Web项目,我们在Eclispe或IDEA中,使用Tomcat部署运行项目后,项目的访问地址一般是http://localhost:8080/项目名,这是因为使用Eclispe或IDEA部署Web项目到Tomcat中,是将整个编译好的项目放到Tomcat的webapps下的以项目名命名的文件夹下,故而项目的访问地址一般是http://localhost:8080/项目名。假如项目直接是在Tomcat的ROOT下,则表示是Tomcat默认项目,可以直接使用http://localhost:8080来进行访问。

Tomcat的webapps文件夹目录

映射(mapping)中url-pattern
  • /*结尾使用通配符匹配所有文件;

  • / 是用来定义默认映射,映射为欢迎资源文件;

  • 以前缀*.开头用来定义扩展映射,一般是指某类后缀的资源文件,例如*.jsp

  • /开头的地址结尾用来定义路径映射,例如/OneServlet/aaa.html等。

‘通配符’映射

<url-pattern>/*<url-pattern>:在不发生情况下,冲突可通过任意资源文件URI进行访问,包括servlet、静态资源文件、动态页面等。

url-pattern的内容为正斜杠星号

默认映射

<url-pattern>/</url-pattern>:在不发生冲突情况下,可通过默认路径(/)、无后缀资源文件名(例如/OneServlet)或静态资源类型的后缀文件名(例如/one.htmlone.css)进行访问,不会通过动态页面类型的后缀文件名(例如one.jsp)进行访问。

url-pattern内容为正斜杠

扩展映射

<url-pattern>*.jsp</url-pattern>* :在不发生冲突情况下,可以通过默认路径/或以jsp为后缀的文件名(例如/aaa.jsp)进行访问。

url-pattern的内容为星号.jsp

路径匹配
  1. 精确路径匹配。例如,OneServlet 的url-pattern /test,TwoServlet的url-pattern/*,如果访问的URL为http://localhost:8080/test ,这个时候Web服务器发现OneServlet和TwoServlet都可以匹配访问的URL,但OneServlet比TwoServlet的url-pattern更精准,所以Web服务器将会进行调用OneServlet。

  2. 最长路径匹配。例如,OneServlet的url-pattern/test/*,TwoServlet的url-pattern/test/a/*,此时访问http://localhost:8080/test/a时,Web服务器会选择路径最长的servlet来匹配,也就是TwoServlet。

  3. 扩展匹配。如果URL最后一部分包含扩展名,Web服务器将会根据扩展名选择合适的servlet。例如,OneServlet的url-pattern*.action,则用户使用的URL为http://localhost:8080/test时,是无法访问到OneServlet资源的,需要使用http://localhost:8080/或者http://localost:8080/test.action才能访问到OneServlet资源。


欢迎资源文件

使用Eclispe或IDEA创建部署运行Web项目后,我们在浏览器中直接访问http://localhost:8080/项目名,Tomcat会将配置的欢迎资源文件作为响应传给浏览器。

欢迎资源文件配置

其中OneServlet可以改为/OneServlet,但不能使用/网站名/OnseServlet

在web.xml配置欢迎资源文件

欢迎资源文件生效顺序

当在web.xml中配置多个欢迎资源文件时,Tomcat会从上往下依次进行判断,只要找到一个配置的资源文件在项目中存在,就会将该资源文件响应给浏览器。例如,在上面图示中,假如OneServlet在项目中能找到对应的资源文件,就会直接将该资源文件作为响应传给浏览器,后面的index.html等就不会生效;如果OneServlet不存在,则会看后面的index.html,存在则作为响应传给浏览器,后面的就不会生效。


过滤器(Filter)

过滤器可以动态拦截请求和响应,以读取修改请求和响应中的信息。可以将过滤器附加到Servlet、JSP或HTML中。

过滤器作用

过滤器实现以下功能:

  • 在客户端访问后端资源前,拦截请求

  • 在服务器的相应发送给客户端前,处理这些响应

过滤器作用图

过滤器生命周期

Filter的创建和销毁都是由服务器来自动完成的。其生命周期:

  1. 服务器启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

  2. 当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。

  3. Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。

过滤器使用

过滤器就是一个实现Filter接口的类。Filter接口有3个抽象方法:

  • 初始化:public void init(FilterConfig filterConfig)

  • 过滤操作:public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

  • 销毁:public void destroy()

Filter接口源码

Filter接口源码

Filter接口的实现类

Filter接口实现类

过滤器web.xml文件配置

在web.xml中,过滤器配置需要放在servlet配置前,因为在调用servlet前需要调用所有附加的filter。

Filter的web.xml配置

过滤器链

当多个过滤器关联的URL相同时,这多个过滤器将组成一个过滤器链。

过滤器链的使用

Filter链的使用和调用顺序

过滤器链中过滤器执行顺序

Web服务器会根据web.xml中Filter映射注册顺序(即<filter-mapping>标签的先后顺序),来决定哪个过滤器先执行。当第一个过滤器的doFilter方法被调用时,Web服务器会创建一个代表Filter链的FilterChain对象作为参数传递给该doFilter方法。在第一个doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则Web服务器会检查FilterChain对象中是否还有Filter,如果有,则调用第2个Filter,如果没有,则调用目标资源。注意,Filter的初始化和调用顺序只和<filter-mapping>有关,与<filter>无关。

流程图

Filter链流程图

优点

可以将不同的过滤功能分散到多个过滤器中,分工明确,避免一个过滤器做太多的业务处理,降低了代码的耦合度,这体现了单一职责的设计原则,应用了责任链的代码设计模式.


多个Servlet之间调用问题

当用户想要获得某个响应,而服务端要发送该响应需要经过多个servlet时,就存在多个servlet之间调用情况,首先需要先调用某个servlet,这个servlet完成后,有需要调用另外一个servlet。然而,用户不可能在浏览器中通过多次请求URL来访问servlet。正常且合理的情况下,用户只需输入第一个资源文件的url就可以获得最后的响应结果。

多个servlet调用流程图

多个servlet调用流程图

解决方案
  • 重定向
  • 请求转发
解决方案1-重定向
原理

用户第一次通过手动方式通知浏览器访问OneServlet资源文件,OneServlet处理完毕后,将TwoServlet地址写入响应头中location属性中,Tomcat返回状态码302,浏览器接收到这响应后,自动根据响应头中location属性中的地址发起第二次请求,完成请求中剩余的任务。

实现

在OneServlet中将TwoServlet的地址作为参数调用response.sendRedirect方法。

多个servlet之间调用解决方案-重定向

特征
  1. 请求地址:通过response.sendRedirect发送给浏览器的地址可以是 内部资源文件的地址,也可以是外部网站地址。例如,内部资源地址TwoServlet/网站名/TwoServlet(两者都是相同效果),外部网站地址http://www.baidu.com。外部网站地址需要完整URL。

  2. 请求次数:浏览器至少发送两次请求,只有第一次请求是用户手动发送的,后续的请求时浏览器自动发送的。

  3. 请求方式:重定向发送的请求方式都是GET。

缺点

客户端和服务器需要多次往返请求和响应,浪费大量时间。

解决方案2-请求转发
原理

用户第一次手动发送请求访问OneServlet资源后,服务端的OneServlet资源文件中会由请求分派器(RequestDispatcher)代替浏览器自动调用TwoServlet动态资源,而浏览器无需自动发送第二次请求,由服务端自动调用,最后将响应结果发送给浏览器即可。

实现

通过request.getRequestDsipatcher(String path)来获取请求转发对象,其参数是转发的下个资源文件地址,地址格式应是/资源名(地址已经默认加上/项目名),再调用请求转发对象的forward(ServletRequest request, ServletResponse response)方法,参数是当前的请求对象和响应对象。

多个servlet之间调用解决方案-请求转发

特征
  1. 请求地址:只能是内部资源文件地址,地址格式应是/资源名

  2. 请求次数:浏览器和服务端之间只有一次请求和响应。

  3. 请求方式:GET请求方式

优点

浏览器和服务端之间只进行了一个请求和响应,极大减少浏览器和服务端之间发送的请求和响应往返次数。

重定向和请求转发的区别

重定向是在服务器端完成的,当客户端请求一个页面时,服务器返回一个HTTP响应,其中包含一个重定向状态码,告诉客户端请求的资源已经被移动到另一个URL上。客户端收到响应后,会重新向新的URL发起请求,从而完成页面跳转。重定向通常用于处理资源的移动、删除或修改等情况。

请求转发是在服务器内部完成的,当客户端请求一个页面时,服务器接收到请求后,会将请求转发到另一个页面进行处理,然后将处理结果返回给客户端。客户端并不知道服务器内部发生了什么,它只知道自己请求的页面已经被正确处理并返回了结果。请求转发通常用于处理业务逻辑、权限控制等情况。

两者的区别在于重定向是客户端重新向新的URL发起请求,而请求转发是服务器内部转发请求,客户端并不知道发生了什么。重定向会导致客户端多一次请求和响应,而请求转发不会。另外,重定向可以跨域,而请求转发只能在同一域名下进行。


自定义异常或错误处理

当servlet抛出异常或对于浏览器的请求服务端发送错误状态码(后面简称 ‘异常或错误’ )的响应时,可以通过在web.xml中设置<error-page>标签来处理异常或错误。这样我们就可以自定义发生异常或错误时服务端发送给浏览器的响应。

自定义异常或错误处理web.xml配置

添加<error-page>标签,其中子标签<error-code>表示错误状态码,<exception-type>表示程序异常,在一个<error-page>标签中只能存在错误状态码、程序异常中的一个,<location>表示对异常或错误做出的响应,可以是动态资源文件或HTML静态页面。

异常处理web.xml文件配置

自定义异常或错误处理的动态资源类

异常处理类

效果展示

自定义异常或错误响应结果展示


同一Http服务器的多个Servlet数据共享解决方案

全局作用域对象(ServletContext接口)

在一个Servlet中,通过request.getServletContent()获得Web服务器的一个ServletContext实现类对象(一般称为全局作用域对象),调用该对象的setAttribute(key,value)来向全局作用域对象中写入数据或调用getAttribute(key)获取指定键的数据,以实现数据共享。数据在全局作用域中相当于Map,是以键值对的形式保存在Web服务器内存中,其中键只能为String,只可以为任意对象或基本数据类型。

全局作用域对象共享数据原理

每个网站都存在一个全局作用域对象,网站中的一个Servlet可以将一个数据存放到全局作用域对象中,另外的Servlet可以从全局作用域对象中得到存放的数据并进行使用。

全局作用域对象共享数据原理图

全局作用域对象共享数据的演示

ServletContext使用

全局作用域对象生命周期
  1. 当服务器启动过程中,会自动为当前网站创建一个全局作用域对象;

  2. 在Web服务器运行期间,一个网站只会存在一个全局作用域对象;

  3. 在Web服务器运行期间,全局作用域对象会一直处于存活状态;

  4. 当Web服务器关闭,会将当前网站中的全局作用域对象销毁。

全局作用域对象共享数据的限制

以全局作用域对象实现的数据共享只适用于同一个Web服务器,可以是不同客户端。

Cookie类

Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。

Cookie原理

当第一次浏览器访问服务器某个资源,服务器传回的响应中包含cookie,在同一浏览器再次向上次服务器访问资源时,发送的请求中将包含上次服务器传来的cookie,因而服务器可以通过获取这些cookie来实现用户识别、数据共享等。

Cookie原理

Cookie使用步骤
  • 服务器脚本向浏览器发送一组 Cookie。例如:姓名、年龄或识别号码等。调用response.addCookie(cookie)方法,参数为一个Cookie对象。
  • 浏览器将这些信息存储在本地计算机上,以备将来使用。
  • 当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息放到请求头中发送到服务器,服务器将使用这些信息来识别用户、数据共享等功能。
Cookie的使用案例

Servlet Cookie中存在中文时,需要进行编码与解码,方法如下:

String str = java.net.URLEncoder.encode("中文""UTF-8"); //编码
String str = java.net.URLDecoder.decode("编码后的字符串","UTF-8"); // 解码

Cookie的使用

删除cookie

在默认情况下,不设置cookie的MaxAge时,cookie是保存在浏览器的缓存(内存中部分)中,在浏览器关闭后cookie将被删除。可以通过手动设置cookie,将cookie保存在硬盘上,并设置cookie的保存时间,通过cookie.setMaxAge(second)方法,参数是秒。一般称这个过程为存储cookie。

  1. 读取一个现有的 cookie,并把它存储在 Cookie 对象中。

  2. 使用 setMaxAge() 方法设置 cookie 的maxAge为0,来删除现有的 cookie。

  3. 把这个 cookie 添加到响应头。

Cookie实现数据共享限制

对于cookie的使用需要在一个服务器为同一个客户端提供服务的情况下,即同一服务器、同一客户端。

Cookie类中方法
方法描述
public void setDomain(String pattern)该方法设置 cookie 适用的域,例如 baidu.com。
public String getDomain()该方法获取 cookie 适用的域,例如 baidu.com。
public void setMaxAge(int expiry)该方法设置 cookie 过期的时间(以秒为单位)。如果不这样设置,cookie 只会在当前 session 会话中持续有效。
public int getMaxAge()该方法返回 cookie 的最大生存周期(以秒为单位),默认情况下,-1 表示 cookie 将持续下去,直到浏览器关闭。
public String getName()该方法返回 cookie 的名称。名称在创建后不能改变。
public void setValue(String newValue)该方法设置与 cookie 关联的值。
public String getValue()该方法获取与 cookie 关联的值。
public void setPath(String uri)该方法设置 cookie 适用的路径。如果您不指定路径,与当前页面相同目录下的(包括子目录下的)所有 URL 都会返回 cookie。
public String getPath()该方法获取 cookie 适用的路径。
public void setSecure(boolean flag)该方法设置布尔值,表示 cookie 是否应该只在加密的(即 SSL)连接上发送。
public void setComment(String purpose)设置cookie的注释。该注释在浏览器向用户呈现 cookie 时非常有用。
public String getComment()获取 cookie 的注释,如果 cookie 没有注释则返回 null。
HttpSession接口

HttpSession对象中是以map键值对形式存放数据信息的,每当web服务器和客户端之间建立会话,web服务器都会自动创建HttpSession对象,开发者可以向其中以键值对形式存放多个数据信息。HttpSession一般用于在一个会话中保存和共享用户的信息。

HttpSession的使用
//获取会话对象,参数为true,则当不存在会话对象时进行创建,为false时不存在会话对象会返回null,无参时默认是true
HttpSession session = request.getSession(true);

HttpSession的使用

HttpSession中常见的方法
方法描述
public Object getAttribute(String name)该方法返回在该 session 会话中具有指定名称的对象,如果没有指定名称的对象,则返回 null。
public Enumeration getAttributeNames()该方法返回 String 对象的枚举,String 对象包含所有绑定到该 session 会话的对象的名称。
public long getCreationTime()该方法返回该 session 会话被创建的时间,自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。
public String getId()该方法返回一个包含分配给该 session 会话的唯一标识符的字符串。
public long getLastAccessedTime()该方法返回客户端最后一次发送与该 session 会话相关的请求的时间自格林尼治标准时间 1970 年 1 月 1 日午夜算起,以毫秒为单位。
public int getMaxInactiveInterval()该方法返回 Servlet 容器在客户端访问时保持 session 会话打开的最大时间间隔,以秒为单位。
public void invalidate()该方法指示该 session 会话无效,并解除绑定到它上面的任何对象。
public boolean isNew()如果客户端还不知道该 session 会话,或者如果客户选择不参入该 session 会话,则该方法返回 true。
public void removeAttribute(String name)该方法将从该 session 会话移除指定名称的对象。
public void setAttribute(String name, Object value)该方法使用指定的名称绑定一个对象到该 session 会话。
public void setMaxInactiveInterval(int interval)该方法在 Servlet 容器指示该 session 会话无效之前,指定客户端请求之间的时间,以秒为单位。
HttpSession和Cookie的区别

Cookie和HttpSession都是用来维护Web应用程序中的会话状态的机制,但是它们的实现方式和作用有所不同。

Cookie是一种在Web浏览器和Web服务器之间传递数据的机制。当Web服务器向客户端发送响应时,可以在响应头中添加Set-Cookie头部信息,用于设置Cookie的名称、值、过期时间、域名、路径等属性。当Web浏览器向Web服务器发送请求时,会自动将之前存储在本地的符合条件的Cookie信息添加到请求头中,以便Web服务器可以识别用户。Cookie的作用是在客户端存储少量的数据,比如用户的登录状态、购物车中的商品等信息。

HttpSession是在Web服务器端维护会话状态的机制。当Web浏览器向Web服务器发送请求时,Web服务器会为每个客户端创建一个唯一的Session ID,并将Session ID存储在HttpServletResponse对象中,然后将Session ID发送给客户端浏览器,通常是通过Cookie的方式。当客户端浏览器再次向Web服务器发送请求时,会将之前存储在本地的Session ID信息添加到请求头中,以便Web服务器可以识别用户。HttpSession的作用是在服务器端存储大量的数据,比如用户的购物记录、用户的个人信息等。

HttpSession和ServletContext的区别

ServletContext对于Web服务器,始终只会存在一个对象,是在Web服务器启动时自动创建的,而HttpSession则是当Web服务器与Web客户端之间每产生一个会话,在Servlet中通过调用request.getSession(true)后,服务器才创建一个HttpSession对象,如果使用request.getSession(false)则不会创建HttpSession对象。这说明一个Web服务器可能存在0个或多个HttpSession对象。

Web服务器和Web客户端的会话(Seesion)

HTTP协议是一种无状态协议,这意味着每次客户端与服务器之间进行通信时,服务器都不会保存上一次通信的状态信息。为了解决这个问题,HTTP协议引入了会话机制,用于在客户端与服务器之间保存状态信息。

当Web客户端(如浏览器)向Web服务器发送请求时,Web服务器会为该请求创建一个新的会话。会话是指在一定时间范围内,Web服务器和Web客户端之间交互的一系列请求和响应。在会话中,Web服务器可以将一些信息存储在会话对象中,以便在后续请求中使用。这些信息可以是用户的身份认证信息、用户的偏好设置、购物车中的商品信息等。

在会话中,Web服务器还可以使用会话ID来标识不同的会话。当Web客户端发送请求时,它会将该会话的会话ID包含在请求中。Web服务器可以使用会话ID来查找该会话,并将信息存储在会话对象中。这样,在后续的请求中,Web服务器可以使用会话ID来检索该会话,并使用会话对象中的信息来处理请求。

需要注意的是,会话的生命周期由Web服务器控制。当Web客户端关闭或超时时,Web服务器会自动终止会话。此外,Web服务器还可以设置会话的最大生命周期,以便在一定时间范围内终止会话。

会话机制的实现

维持Web客户端和Web服务器之间Session会话的解决方案有以下几种:

  • Cookie

Cookie是一种在Web客户端存储数据的技术,它可以在Web浏览器和Web服务器之间传递数据。当客户端向服务器发送请求时,服务器可以在响应中设置一个Cookie,客户端浏览器会将这个Cookie保存下来,当下一次向同一服务器发送请求时,浏览器会将该Cookie发送给服务器。服务器通过读取Cookie中的数据,就可以识别出客户端的身份和状态,从而维持会话。

  • URL重写

URL重写是一种在URL中添加参数的技术,通过在URL中添加参数来维持会话。例如,在登录时,服务器可以在响应中添加一个重写后的URL,该URL包含了一个Session ID参数,客户端在访问该URL时,服务器就可以通过Session ID参数来识别客户端的身份和状态,从而维持会话。

  • 隐藏表单域

隐藏表单域是一种在HTML表单中添加隐藏域的技术,通过在表单中添加一个隐藏域来维持会话。例如,在登录时,服务器可以在响应中添加一个包含Session ID的隐藏表单域,客户端在提交表单时,就会将该Session ID发送给服务器,服务器通过读取该Session ID来识别客户端的身份和状态,从而维持会话。

以上三种方案的原理都是通过在客户端和服务器之间传递一个会话标识(如Session ID)来维持会话,服务器通过读取该会话标识来识别客户端的身份和状态。其中,Cookie是最常用的一种方案,因为它可以自动保存在客户端浏览器中,而且可以设置过期时间和安全标识,具有较高的可靠性和安全性。

实现操作方面,不同的Web框架和编程语言都有相应的Session管理机制,一般来说,需要在服务器端生成一个Session ID,并将其保存在客户端的Cookie中或者通过URL重写或隐藏表单域传递给客户端,在后续的请求中,服务器通过读取该Session ID来维持会话。具体实现细节可以参考相应的文档和教程。

在HTTP协议中,会话机制通常使用Cookie和Session来实现。Cookie是一种在客户端保存数据的机制,当服务器需要保存状态信息时,会将这些信息存储在Cookie中,并将Cookie发送给客户端保存。当客户端再次向服务器发送请求时,会将Cookie发送给服务器,服务器就可以从Cookie中获取之前保存的状态信息。

而Session是一种在服务器端保存数据的机制,当客户端向服务器发送请求时,服务器会为该客户端创建一个Session,并在Session中保存状态信息。服务器会将Session的ID发送给客户端保存,当客户端再次向服务器发送请求时,客户端会将Session的ID发送给服务器,服务器就可以从Session中获取之前保存的状态信息。

需要注意的是,使用会话机制可能会增加服务器的负担,因为服务器需要为每个客户端创建一个Session,并在Session中保存状态信息。为了减轻服务器的负担,可以通过设置Session的过期时间和使用Cookie来减少Session的数量。

Cookie实现会话维护
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@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 ("admin".equals(username) && "123456".equals(password)) {
            // 登录成功,生成Session ID,并将其保存在Cookie中
            String sessionId = java.util.UUID.randomUUID().toString();
            Cookie cookie = new Cookie("sessionId", sessionId);
            cookie.setMaxAge(60 * 60); // 设置Cookie的过期时间为1小时
            response.addCookie(cookie);

            // 将Session ID保存到服务器端,以便后续使用
            request.getSession().setAttribute("sessionId", sessionId);

            // 跳转到首页
            response.sendRedirect("/index.jsp");
        } else {
            // 登录失败,返回错误信息
            request.setAttribute("error", "用户名或密码错误");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}
URL重写实现会话维护
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class MyServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        HttpSession session = request.getSession(true);

        //设置Session属性
        session.setAttribute("username", "John");

        //获取Session ID
        String sessionId = session.getId();

        //将Session ID添加到URL中
        String encodedURL = response.encodeURL("welcome.jsp");
        String urlWithSessionId = encodedURL + "?sessionId=" + sessionId;

        //重定向到带有Session ID的URL
        response.sendRedirect(urlWithSessionId);
    }
}
表单隐藏域实现会话维护

使用表单隐藏域维护会话的一般步骤如下:

  1. 在服务器端生成一个唯一的会话标识符(session ID)。
  2. 将该会话标识符存储到服务器端的会话管理器中,并将其作为一个名为“sessionID”的隐藏域添加到表单中。
  3. 将表单发送给客户端。
  4. 客户端在提交表单时,将隐藏域中的会话标识符一并提交给服务器端。
  5. 服务器端在接收到表单提交请求后,从请求中获取隐藏域中的会话标识符。
  6. 服务器端根据会话标识符从会话管理器中获取相应的会话对象,并利用该会话对象来处理请求。

通过这种方式,服务器端和客户端之间可以共享同一个会话,从而实现会话状态的维护。需要注意的是,由于隐藏域中的会话标识符可以被用户修改,因此需要采取一些安全措施来防止会话劫持等安全问题的发生。


监听器(Listener)

在Servlet中,监听器是实现特定接口的Java类,用以在特定事件发生时做出相应操作处理,即执行某个方法。

相关概念
  • 监听事件:监听器做出相应操作的触发条件。

  • 事件源:发生监听事件的对象。

  • 监听器:监听器对象,实现类特定监听器接口的对象,其中存在方法用来对响应事件发生时做出相应操作处理。

  • 注册监听:将监听器对象与监听事件进行绑定。

监听源
  • ServletRequest:ServletRequest接口实现类对象,即请求对象。

  • ServletContext:ServletContext实现类的对象,即全局作用域对象。

  • HttpSession:HttpSession实现类的对象,即会话对象。

监听事件
  • 创建与注销:监听源对象的创建和销毁过程

  • 属性改变:监听源对象发生属性的增删改

  • 状态改变:监听源中只有HttpSession存在状态改变的监听事件,

监听器
事件源对象创建与销毁的监听器
  • ServletContextListener接口:用来监听ServletContext实现类对象的创建与销毁

  • HttpSessionListener接口:用来监听HttpSessionListener实现类对象的创建与销毁

  • ServletRequestListener接口:用来监听ServletRequest实现类对象的创建与销毁

事件源对象创建与销毁的监听器

事件源对象属性改变的监听器
  • ServletContextAttributeListener接口:用来监听ServletContext实现类对象的属性改变,使用application.setAttribute(String key, Object value)

  • HttpSessionAttributeListener接口:用来监听HttpSession实现类对象的属性改变,使用session.setAttribute(String key, Object value)

  • ServletRequestAttributeListener接口:用来监听ServletRequest实现类对象的属性改变,使用request.setAttribute(String key, Object value)

事件源对象属性修改的监听器

HttpSession实现类对象状态改变的监听器

HttpSession实现类对象存在四种状态:

  1. 绑定:将JavaBean对象添加到HttpSession实现类对象中,可以使用serAttribute(String key, Object obj)方法进行绑定

  2. 解绑:将JavaBean对象从HttpSession实现类对象中移除,可以使用removeAttribute(String key)方法进行解绑

  3. 钝化:将HttpSession实现类对象通过序列化方式保存到硬盘中

  4. 活化:通过反序列化方式从硬盘中读取除HttpSession实现类对象

  • HttpSessionBindingListener接口:用于用来监听HttpSession实现类对象的绑定和解绑事件

  • HttpSessionActivationListener接口:用来监听HttpSession实现类对象钝化和活化事件

HttpSession实现类对象绑定与解绑、钝化与活化的监听器

监听器的使用

以ServletRequest为监听源的监听器为例:

ServletRequest实现类对象的监听器

多个监听器执行的顺序

当某个资源存在多个监听器时,客户端访问该资源时,监听器的执行顺序是由监听器注册顺序(即web.xml中<listener>标签的顺序)决定。

两个监听器和一个Servlet代码

多个监听器执行顺序-两个监听器和一个Servlet代码

web.xml配置

多个监听器的执行顺序-web.xml配置

执行结果

多个监听器的执行顺序-结果

监听器工作流程

监听器工作流程

监听器、过滤器、Servlet之间顺序关系
  • 创建加载顺序:监听器 > 过滤器 > Servlet

  • 销毁顺序:Servlet > 过滤器 > 监听器

  • 执行顺序:监听器 > 过滤器 > Servlet

监听器、过滤器、Servlet顺序关系

案例源码

Gitee:https://gitee.com/gegaa/demo-for-note

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦年华a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值