本文包含的内容有:

  1. Servlet的理解
  2. 自定义Servlet、监听器和过滤器
  3. 三者的一点点编程设计思想
  4. 后续的学习

JavaWeb是Web开发的重要基础,对Servlet、监听器和过滤器等知识的掌握程度,将会影响到你后面学习SpringWeb框架难易程度。

先了解下我们在学习的东西是干嘛的

B/S模式

B端=浏览器端,可以看作是通用标准的客户端,所有浏览器都基于通用标准去开发的客户端软件;

S端=服务器端,就是我们开发的web服务,在B/S端中我们只需要开发服务端,区别于C/S模式,在C/S模式中我们客户端app和服务端服务都需要我们自己开发完成。

简单理解下浏览器到服务器的过程:

  1. 浏览器请求发出http://www.xxxx.com/path1/ser/
  2. 通过DNS域名解析找到IP地址
  3. 通过IP地址找到部署服务的服务器
  4. 通过端口找到服务器中具体的服务程序
  5. 因为一个Tomcat(web服务器)可以部署多个项目,所以往往喜欢使用端口后的第一个路径表示要访问的服务(path1路径)
  6. 接着就是路径ser,ser表示要访问的servlet,一个项目必然有数不清的servlet入口,分别用于实现不同功能的小程序服务。

那为啥叫小程序服务呢?有小就有大的咯,哪大的是谁?是Tomcat服务器咯。可以看作一个Tomcat中有多个独立提供不同功能的小程序服务--servlet。

到此引出了为什么要有servlet,为不同请求独立提供不同的服务

学习之前记得配好开发环境:两个都要配置

  1. JAVA_HOME配置:C:\Program Files\Java\jdk1.8.0_181
  2. CLASS_PATH配置:.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

1.Servlet

1.1.简介

Servlet(Server applet),全称Java Servlet,直接意思是Java的小程序服务,是运行在 Web 服务器或应用服务器上的程序。Servlet一般只用来扩展HTTP协议的Web服务。

Servlet的作用:接收用户请求、提供服务处理入口、响应处理结果。

Servlet接口源码

public interface Servlet {
void init(ServletConfig var1) throws ServletException; ServletConfig getServletConfig(); void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException; String getServletInfo(); void destroy();
}

Servlet的生命周期

Servlet生命周期指Servlet的对象从被创建到被销毁的过程。

  1. 由Tomcat服务器启动时创建Servlet;
  2. Servlet的配置信息ServletConfig作为参数传递到void init(ServletConfig var1)方法中;
  3. 在开发Servlet时,将init方法中的ServletConfig对象赋值给成员变量(放大作用域),再通过getServletConfig() 获取ServletConfig对象;这件事已经由抽象类GenericServlet做了,所以一般自定义Servlet都是继承GenericServlet类或其子类来实现;
  4. 每个Servlet请求的入口为service()方法,每次请求都会从这里进去;
  5. Servlet对象销毁前(WEB项目卸载时)调用destroy(),用来做一些收尾工作,释放资源。

关于getServletInfo的方法,再GenericServlet中实现结果时返回空字符串。

两个关键点

init()方法:每个servlet都只会在第1次请求时执行init初始化方法;

service()方法

  • 每次请求进来都会调用service()方法;
  • Servlet是以单实例多线程方式工作,它是以多线程的方式调用service()方法;
  • Servlet不是线程安全,所以尽量不要在service()方法中操作全局变量,若非要调用全局变量务必使用关键字volatile修饰,并通过synchronized修饰的方法来操作全局变量。

通过Servlet接口源码可知,ServletConfig接口、ServletContext接口、ServletRequest接口和ServletResponse接口最为重要。

1.2.ServletConfig

Servlet的配置信息,每一个Servlet都有其唯一对应的ServletConfig。

创建:由Tomcat服务器启动时创建

获取:ServletConfig对象作为参数传递到init()方法中,可以在init()方法直接获取。

抽象类GenericServlet已经实现Servlet和ServletConfig接口,所以一般自定义Servlet都是继承GenericServlet类或其子类来实现,可以直接调用ServletConfig接口中的4个方法来获取servlet配置信息。

ServletConfig 接口源码

public interface ServletConfig {
String getServletName();
ServletContext getServletContext();
String getInitParameter(String var1);
Enumeration<String> getInitParameterNames();
}

ServletConfig接口除了ServletName和初始化参数信息,最重要的就是ServletContext对象的获取了。

1.3.ServletContext

一个Web应用对应一个唯一的ServletContext对象(可以在tomcat的server.xml中的Context标签查看)

ServletContext对象在项目启动时创建,并在初始化时提供ServletConfig对象来访问ServletContext,在项目停止或重载配置时销毁。

获取:通过ServletConfig的getServletContext()方法获取。

功能:

1、可以获取整个WEB应用的初始化参数

<context-param>
<param-name>myContextName</param-name>
<param-value>xxxxxxvalue</param-value>
</context-param>

2、可以获取资源的真实路径(物理路径),主要在文件的上传和下载时使用。

3 、可以作为一个域对象在不同的web资源之间共享数据。

1.4.Servlet案例

public class MyFirstServlet implements Servlet{
private ServletConfig servletConfig; @Override
public void init(ServletConfig config) throws ServletException {
System.out.println("==Servlet1==MyFirstServlet--init");
this.servletConfig = config; //将初始化传进来的Servlet配置对象保存起来
// 获取Servlet初始化时的所有初始化参数名称
Enumeration<String> initParameterNames = this.servletConfig.getInitParameterNames();
// 遍历所有初始化 参数名称 和 参数值
while(initParameterNames.hasMoreElements()) {
String nextElement = initParameterNames.nextElement();
String initParameter = this.servletConfig.getInitParameter(nextElement);
System.out.println("[param-name="+ nextElement + ",param-value=" + initParameter +"]");
}
} @Override
public ServletConfig getServletConfig() {
return this.servletConfig; // 提供获取ServletConfig方法
} @Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("==Servlet1==MyFirstServlet--service");
// 查看容器中有多少个Servlet
ServletContext servletContext = getServletConfig().getServletContext();
Map<String, ? extends ServletRegistration> servletRegistrations = servletContext.getServletRegistrations();
Iterator<? extends Map.Entry<String, ? extends ServletRegistration>> iterator = servletRegistrations.entrySet().iterator();
while(iterator.hasNext()) {
Entry<String, ? extends ServletRegistration> next = iterator.next();
System.out.println("key="+next.getKey()+",value="+next.getValue());
}
// 响应浏览器
res.getWriter().write("200");
} @Override
public void destroy() {
System.out.println("==Servlet1==MyFirstServlet--destroy");
} @Override
public String getServletInfo() {
return "";
} }

web.xml 配置

<servlet>
<servlet-name>MyFirstServlet</servlet-name>
<servlet-class>com.yty.servlet.MyFirstServlet</servlet-class>
<init-param>
<param-name>initParam</param-name>
<param-value>asdfsdaf</param-value>
</init-param></servlet>
<servlet-mapping>
<servlet-name>MyFirstServlet</servlet-name>
<url-pattern>/myfirstservlet</url-pattern>
</servlet-mapping>

启动Tomcat服务

浏览器访问:http://localhost:8080/mywebdemo/myfirstservlet

执行结果:Servlet容器有三个Servlet

分析结果:

自定义了一个名称为:MyFirstServlet的Servlet,但打印结果是Servlet容器有三个Servlet。原因是在Tomcat中的web.xml中配置了另外两个Servlet

Tomcat中的两个默认Servlet配置:DefaultServlet和JspServlet

<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>

到这里,应该知道了为什么你的jsp动态页面可以在浏览器访问了吧。

在springboot中的Spring-webmvc默认是不支持jsp的,这两个Servlet也是没有的,默认只有一个dispatcherServlet,可通过上面的方式自行验证。

1.5.GenericServlet 抽象类

前面的Servlet案例是直接实现Servlet接口来实现,在案例中很容易看出有很多代码都是通用的,比如:保存初始化时传进来的Servlet配置对象。为了简化Servlet的开发就有了GenericServlet 抽象类,它实现了除service方法以外的所有方法。继承GenericServlet 只需要实现service方法即可创建一个Servlet。

除此之外GenericServlet 抽象类还实现了ServletConfig接口,也就是说我们可以通过GenericServlet 直接获取servlet配置相关信息。

1.6.HttpServlet 抽象类

HttpServlet 抽象类只继承了GenericServlet 抽象类,由上一点知识可以知道,HttpServlet 具备了Servlet功能和获取Servlet配置信息功能。如果说GenericServlet 简化了Servlet的开发,那么HttpServlet 更大程度简化了HTTP协议方面的Servlet开发。

HttpServlet 抽象类面向的就是HTTP协议的Servlet开发。如果我们要实现一个HTTP协议的Servlet开发,那么就直接继承HttpServlet抽象类来实现。HttpServlet抽象类中没有抽象方法,只需要按需实现doGet、doPost、doPut、doDelete等等方法即可,这些doXXX方法本质还是调用service方法。

1.7.关于其他接口和类

比如ServletRequest、ServletResponse、HttpServletRequest、HttpServletResponse等等接口和类,没办法一个个的讲完,还是要自己主动学习。很多类和接口就像字典一样,需要的是怎么学会查字典,而不是背字典,所以来学习怎么“查字典”吧。

以Tomcat 8.5.XX为例:

先打开ServletApi字典:https://tomcat.apache.org/tomcat-8.5-doc/servletapi/allclasses.html

再Ctrl + F,输入HttpServletRequest

找到心目中的对象,打开链接查看你的对象

看不懂全英文的就用浏览器翻译插件翻译,比如谷歌浏览器自带的翻译功能。

2.过滤器--Filter

2.1.简介

过滤器,顾名思义就是用来过滤的,就像家里用来过滤水用的过滤器一样,起到过滤的作用。过滤器只能对进入服务器时对请求和响应对象进行过滤,请求响应的过程是不管的。

水的过滤可以分为很多不同层次的处理,比如:先过滤大颗粒泥沙、再过滤微小的颗粒、再深层过滤、最后杀菌消毒。这种有先后的一层一层的过滤,又称为过滤链编程是现实的映射,所以同样也有过滤链,就像过滤水一样。

为什么要有过滤器呢?在Servlet入口方法service中处理不就可以咯?

过滤器的作用

  • 过滤请求:在客户端的请求访问servlet之前进行过滤。
  • 过滤响应:在servlet的响应发送回客户端之前进行过滤。

过滤器生命周期

服务器启动时创建一个实例执行init方法,过滤器常驻内存中,直到过滤器被移除或服务器关闭才会执行destroy方法

应用场景

  • 统一设置编码格式
  • 身份验证
  • 访问权限控制
  • 日志记录
  • 敏感数据过滤
  • 防止XSS攻击的过滤
  • ……

过滤器的实现跟Servlet类似,都需要直接或间接实现接口。

过滤器接口源码

public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
} void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException; default void destroy() {
}
}

看源码可以知道,Filter接口的设计和Servlet很相似。

特点

  • 服务启动时就进行初始化,初始化时调用init方法,传入FilterConfig对象。开发时,将FilterConfig保存起来,方便后续获取Filter的配置信息。
  • 配了过滤器的Servlet,每次接收请求都会通过过滤处理入口:doFilter方法;doFilter方法前做请求统一处理,doFilter方法后做响应统一处理。

2.2.相关接口和类

FilterConfig接口:过滤器的配置信息(名称,初始化参数,Servlet容器对象);

GenericFilter抽象类:对Filter做了通用实现,继承它可以更简单的实现自定义过滤器;

HttpFilter抽象类:继承GenericFilter,面向HTTP协议的过滤器再封装。

这些类的设计都和Servlet的相似,可参考1.6.谈到的API文档 + 对比Servlet来学习。

2.3.两个Servlet+过滤链案例

通过这个案例来学习Servlet和Filter,为了方便理解的简图

第一个Servlet代码

public class MyFirstServlet implements Servlet{
private ServletConfig servletConfig; @Override
public void init(ServletConfig config) throws ServletException {
System.out.println("==Servlet1==MyFirstServlet--init");
this.servletConfig = config; //将初始化传进来的Servlet配置对象保存起来
// 获取Servlet初始化时的所有初始化参数名称
Enumeration<String> initParameterNames = this.servletConfig.getInitParameterNames();
// 遍历所有初始化 参数名称 和 参数值
while(initParameterNames.hasMoreElements()) {
String nextElement = initParameterNames.nextElement();
String initParameter = this.servletConfig.getInitParameter(nextElement);
System.out.println("==Servlet1==MyFirstServlet--[param-name="+ nextElement + ",param-value=" + initParameter +"]");
}
} @Override
public ServletConfig getServletConfig() {
return this.servletConfig; // 提供获取ServletConfig方法
} @Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("==Servlet1==MyFirstServlet--service");
// 查看容器中有多少个Servlet
ServletContext servletContext = getServletConfig().getServletContext();
Map<String, ? extends ServletRegistration> servletRegistrations = servletContext.getServletRegistrations();
Iterator<? extends Map.Entry<String, ? extends ServletRegistration>> iterator = servletRegistrations.entrySet().iterator();
while(iterator.hasNext()) {
Entry<String, ? extends ServletRegistration> next = iterator.next();
System.out.println("==Servlet1==MyFirstServlet--[key="+next.getKey()+",value="+next.getValue()+"]");
}
// 响应浏览器
((HttpServletResponse)res).setStatus(200);
res.getWriter().write("MyFirstServlet-200");
} @Override
public void destroy() {
System.out.println("==Servlet1==MyFirstServlet--destroy");
} @Override
public String getServletInfo() {
return "";
} }

第二个Servlet代码

public class MySecondServlet implements Servlet{
private ServletConfig servletConfig; @Override
public void init(ServletConfig config) throws ServletException {
System.out.println("==Servlet2==MySecondServlet--init");
this.servletConfig = config; //将初始化传进来的Servlet配置对象保存起来
} @Override
public ServletConfig getServletConfig() {
return this.servletConfig; // 提供获取ServletConfig方法
} @Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("==Servlet2==MySecondServlet--service");
// 查看容器中有多少个Servlet
ServletContext servletContext = getServletConfig().getServletContext();
Map<String, ? extends ServletRegistration> servletRegistrations = servletContext.getServletRegistrations();
Iterator<? extends Map.Entry<String, ? extends ServletRegistration>> iterator = servletRegistrations.entrySet().iterator();
while(iterator.hasNext()) {
Entry<String, ? extends ServletRegistration> next = iterator.next();
System.out.println("==Servlet2==MySecondServlet--[key="+next.getKey()+",value="+next.getValue()+"]");
}
// 响应浏览器
((HttpServletResponse)res).setStatus(200);// 设置响应码
res.getWriter().write("MySecondServlet-200");//打印到浏览器页面
} @Override
public void destroy() {
System.out.println("==Servlet2==MySecondServlet--destroy");
} @Override
public String getServletInfo() {
return "";
} }

第一个过滤器代码:起到统一编码的功能

public class MyFirstFilter implements Filter {
// 统一设置编码格式
private String encoding; @Override
public void init(FilterConfig filterConfig) throws ServletException {
String filterName = filterConfig.getFilterName();
this.encoding= filterConfig.getInitParameter("Encoding");
System.out.println("==Filter1==MyFirstFilter--"+filterName+"---init---charset参数值="+encoding);
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("==Filter1==MyFirstFilter--doFilter");
/*
* 请求统一过滤处理
*/
request.setCharacterEncoding(encoding);
/*
* 把过滤器接入过滤链
*/
chain.doFilter(request, response);
/*
* 响应统一过滤处理
*/
response.setCharacterEncoding(encoding);
int status = ((HttpServletResponse)response).getStatus();
System.out.println("==Filter1==MyFirstFilter--响应码:"+status); } @Override
public void destroy() {
System.out.println("==Filter1==MyFirstFilter--destroy");
}
}

第二个Filter代码

public class MySecondFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String filterName = filterConfig.getFilterName();
System.out.println("==Filter2==MySecondFilter--"+filterName+"---init");
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("==Filter2==MySecondFilter--doFilter---");
/*
* 请求统一过滤处理
*/
System.out.println("==Filter2==MySecondFilter--请求统一过滤处理");
/*
* 把过滤器接入过滤链
*/
chain.doFilter(request, response);
/*
* 响应统一过滤处理
*/
int status = ((HttpServletResponse)response).getStatus();
System.out.println("==Filter2==MySecondFilter--响应码:"+status);
} @Override
public void destroy() {
System.out.println("==Filter2==MySecondFilter--destroy");
}
}

web.xml 配置

 <filter>
<filter-name>MyFirstFilter</filter-name>
<filter-class>com.yty.filter.MyFirstFilter</filter-class>
<init-param>
<param-name>Encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter>
<filter-name>MySecondFilter</filter-name>
<filter-class>com.yty.filter.MySecondFilter</filter-class>
</filter>
<!-- filter-mapping配置顺序==过滤顺序 -->
<filter-mapping>
<filter-name>MyFirstFilter</filter-name>
<url-pattern>/myfirstservlet</url-pattern><!-- 过滤的/myfirstservlet路径 -->
</filter-mapping>
<filter-mapping>
<filter-name>MySecondFilter</filter-name>
<url-pattern>/*</url-pattern><!-- 过滤本项目的所有路径 -->
</filter-mapping> <!-- 第一个Servlet -->
<servlet>
<servlet-name>MyFirstServlet</servlet-name>
<servlet-class>com.yty.servlet.MyFirstServlet</servlet-class>
<init-param>
<param-name>initParam</param-name>
<param-value>asdfsdaf</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>MyFirstServlet</servlet-name>
<url-pattern>/myfirstservlet</url-pattern>
</servlet-mapping>
<!-- 第二个Servlet -->
<servlet>
<servlet-name>MySecondServlet</servlet-name>
<servlet-class>com.yty.servlet.MySecondServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MySecondServlet</servlet-name>
<url-pattern>/mysecondservlet</url-pattern>
</servlet-mapping>

执行结果:

服务启动阶段:

==Filter1==MyFirstFilter--MyFirstFilter---init---charset参数值=utf-8 ==Filter2==MySecondFilter--MySecondFilter---init

第1次访问/myfirstservlet 控制台打印结果:

第2次访问/myfirstservlet 控制台打印结果:

第1次访问/mysecondservlet 控制台打印结果:

第2次访问/mysecondservlet 控制台打印结果:

3.监听器--Listener

3.1.简介

监听器,顾名思义就是监听某样东西,当它监听的某件事情发生时就触发监听器去处理。比如,手机一直监听着手机电量,当低个于你设置的百分比(如10%电量)时,就会弹一个框提示你:电量过低了,及时充电。

按监听的对象分类

  • ServletContext对象监听器:ServletContext对象创建和销毁的监听器
  • HttpSession对象监听器:HttpSession对象创建和销毁的监听器
  • ServletRequest对象监听器:ServletRequest对象创建和销毁的监听器
  • ……

监听器的作用

  • 预先设定需要监听的事物,监听到事物的变化便做出相应的处理

生命周期

  • 服务器启动时创建,直到监听器被移除或服务器关闭才会执行销毁

应用场景

  • 容器启动时一层层的触发事物,有条不絮的一层层的启动
  • 监听请求和响应
  • 监听会话信息
  • ……

以ServletContextListener为例:当Web应用程序的Servlet上下文更改时,底层事件源会发布一个带有ServletContextEvent事件的变更通知给ServletContextListener.contextInitialized方法,让ServletContextListener监听器去处理容器启动完之后的事情。

3.2.Listener 案例

案例一:自定义ServletContextListener

public class MyServletContextListener implements ServletContextListener{

    @Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("=======MyServletContextListener--contextInitialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("=======MyServletContextListener--contextDestroyed");
} }

web.xml 配置

<listener>
<listener-class>com.yty.listener.MyServletContextListener</listener-class>
</listener>

执行结果:

容器启动完就会打印:

=======MyServletContextListener--contextInitialized

容器销毁就会打印:

=======MyServletContextListener--contextDestroyed

在此只知道监听事件和监听器,关于事件源则不需要开发者去关注,只要知道是Servlet容器启动完成后便会触发。

案例二:自定义HttpSessionListener

public class MyHttpSessionListener implements HttpSessionListener {
private int count=0;//记录session的数量 @Override
public void sessionCreated(HttpSessionEvent se) {
count++;
se.getSession().setAttribute("Count", count);
System.out.println("==MyHttpSessionListener==sessionCreated--"+se.getSession().getId());
} @Override
public void sessionDestroyed(HttpSessionEvent se) {
count--;
se.getSession().setAttribute("Count", count);
System.out.println("==MyHttpSessionListener==sessionDestroyed--"+se.getSession().getId());
} }

在任意Servlet中加入:触发HttpSession事件

// 获取Session
HttpServletRequest hreq = (HttpServletRequest)req;
Integer attribute = (Integer)hreq.getSession().getAttribute("Count");// 获取session数量,触发HttpSession事件
System.out.println("=====session 数量:["+attribute +"]");

web.xml配置

<listener>
<listener-class>com.yty.listener.MyHttpSessionListener</listener-class>
</listener>
<!--默认的会话超时时间间隔,以分钟为单位 -->
<session-config>
<session-timeout>1</session-timeout>
</session-config>

执行结果:

访问带有操作session的Servlet时触发HttpSessionListener,从而为HTTP无状态协议附上sessionID,通过ID来绑定访问用户,实现有状态会话(HTTP+Session场景的有状态会话)。

控制台打印:

==MyHttpSessionListener==sessionCreated--3E08CB886E5838A84756FE08123D7A82 =====session 数量:[1]

在浏览器F12中可以看到浏览器接收到的响应头带有Set-Cookie:JSESSIONID=控制台打印的ID

案例三:自定义ServletRequestListener

public class MyServletRequestListener implements ServletRequestListener{

 @Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("==MyServletRequestListener---requestInitialized--"+sre.getServletRequest().getRemoteHost());
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("==MyServletRequestListener---requestDestroyed--"+sre.getServletRequest().getRemoteHost());
} }

web.xml 配置

 <listener>
<listener-class>com.yty.listener.MyServletRequestListener</listener-class>
</listener>

执行结果:

接收到请求开始触发

==MyServletRequestListener---requestInitialized--0:0:0:0:0:0:0:1

响应请求触发

==MyServletRequestListener---requestDestroyed--0:0:0:0:0:0:0:1

3.3.简单总结

先后顺序简图

第一次访问Servlet到容器销毁的细节顺序

=======MyServletContextListener--contextInitialized

==Filter1==MyFirstFilter--MyFirstFilter---init---charset参数值=utf-8

==Filter2==MySecondFilter--MySecondFilter---init

==MyServletRequestListener---requestInitialized--0:0:0:0:0:0:0:1

==Servlet1==MyFirstServlet--init ==Servlet1

==MyFirstServlet--[param-name=initParam,param-value=asdfsdaf]

==Filter1==MyFirstFilter--doFilter

==Filter2==MySecondFilter--doFilter---

==Filter2==MySecondFilter--请求统一过滤处理

==Servlet1==MyFirstServlet--service

==Servlet1==MyFirstServlet--[key=MySecondServlet,value=org.apache.catalina.core.ApplicationServletRegistration@4582c182]

==Servlet1==MyFirstServlet--[key=default,value=org.apache.catalina.core.ApplicationServletRegistration@1316c8af]

==Servlet1==MyFirstServlet--[key=jsp,value=org.apache.catalina.core.ApplicationServletRegistration@31e644bd]

==Servlet1==MyFirstServlet--[key=MyFirstServlet,value=org.apache.catalina.core.ApplicationServletRegistration@243bc5a2]

==MyHttpSessionListener==sessionCreated--3E08CB886E5838A84756FE08123D7A82

=====session 数量:[1]

==Filter2==MySecondFilter--响应码:200

==Filter1==MyFirstFilter--响应码:200

==MyServletRequestListener---requestDestroyed--0:0:0:0:0:0:0:1 三月 21, 2022 7:52:30 上午 org.apache.catalina.startup.HostConfig reload 信息: 重新加载上下文[/mywebdemo]

==Servlet1==MyFirstServlet--destroy 三月 21, 2022 7:52:30 上午 org.apache.catalina.core.StandardContext reload 信息: 已开始重新加载名为[/mywebdemo]的上下文

==Filter1==MyFirstFilter--destroy

==Filter2==MySecondFilter--destroy

=======MyServletContextListener--contextDestroyed

更多细节可以下载源代码学习:下载链接已经附在文末

Servlet、Filter和Listener的共同特点:在自定义中都可以获取到ServletContext,对Servlet容器在不同阶段进行定制化实现。

来个Servlet、Filter和Listener案例代码全家福

JavaWeb的一点点编程设计思想

单一职责原则

以Servlet的方式分离不同请求,不同职责的Servlet处理不同的请求;不同Filter分别实现不同的过滤功能;不同的Listener分别处理不同的监听事件。

单实例多线程工作方式

Servlet就是以单实例多线程的方式工作,每个请求在一个独立的线程中运行,而提供服务的Servlet实例只有一个。每个请求相关的数据都是用Servlet子类的service方法(或者是doGet或doPost方法)的参数传入的。只要Servlet中的代码只使用局部变量,Servlet就不会导致同步问题。若非要调用全局变量务必使用关键字volatile修饰,并通过synchronized修饰的方法来操作全局变量,根据业务需求尽可能做到线程安全。

装饰模式

提供 ServletRequest 接口的便捷实现,希望将请求适应 Servlet 的开发人员可以将其子类化。此类实现 Wrapper 或 Decorator 模式,方法默认调用包装的请求对象。

观察者模式

通过调用XXXListener类来观察Web中的对象变化情况,如:观察ServletContext、Servlet、ServletRequest、ServletResponse和HttpSession等等对象。当事件源检测到发生预定的事件(Event),便调用监听器(Listener)方法来触发监听事件。

责任链模式

过滤链【chain.doFilter()】使用了责任链模式,不同过滤器负责不同的过滤功能,也就是担任着不同的职责,都肩负着不同的责任,将各施其职的完成一整套可插拔的过滤功能。责任链模式只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递,所以责任链将请求的发送者和请求的处理者解耦了。

后续学习

  1. SpringWeb框架学习

    DispatcherServlet本质是个Servlet,功能是分配任务,简称分配器小程序服务;除了分配器,SpringWeb还扩展了,拦截器,处理器,控制器,视图解析器等等功能,为java Web开发提供更全面更高效的Web框架。

  2. HTTP协议的学习

  3. Tomcat 服务器的学习

下载源代码学习:https://pan.baidu.com/s/1GfZgO7tX6d-lL3jUeAsT7w

防失效--关注WX公众号【Java全栈布道师】回复获取提取码:mywebdemo

Java往期文章

Java全栈学习路线、学习资源和面试题一条龙

我心里优秀架构师是怎样的?

免费下载经典编程书籍

JavaWeb之Servlet、拦截器、监听器及编程思想的更多相关文章

  1. 深入分析JavaWeb Item47 -- Struts2拦截器与文件上传下载

    一.struts2中的拦截器(框架功能核心) 1.过滤器VS拦截器 过滤器VS拦截器功能是一回事. 过滤器是Servlet规范中的技术,能够对请求和响应进行过滤. 拦截器是Struts2框架中的技术. ...

  2. SpringBoot 过滤器, 拦截器, 监听器 对比及使用场景

    1. 过滤器 (实现 javax.servlet.Filter 接口) ① 过滤器是在web应用启动的时候初始化一次, 在web应用停止的时候销毁. ② 可以对请求的URL进行过滤, 对敏感词过滤, ...

  3. struts2拦截器+监听器 .

    一.拦截器是怎么实现: 实际上它是用Java中的动态代理来实现的 二.拦截器在Struts2中的应用 对于Struts2框架而言,正是大量的内置拦截器完成了大部分操作.像params拦截器将http请 ...

  4. Autofac实现拦截器和切面编程

    Autofac.Annotation框架是我用.netcore写的一个注解式DI框架,基于Autofac参考 Spring注解方式所有容器的注册和装配,切面,拦截器等都是依赖标签来完成. 开源地址:h ...

  5. servlet拦截器

    servlet拦截未登录的用户请求 java代码: package com.gavin.filter; import java.io.IOException; import javax.servlet ...

  6. JavaWeb -- Struts 自定义拦截器, 登录权限拦截

    1. 自定义拦截器, 登录权限拦截 login.jsp 登录JSP <%@ page language="java" contentType="text/html; ...

  7. servlet 拦截器 (filter)

    使用: 创建一个类实现javax.servlet.Filter接口,并重写接口中所有的方法: 在web.xml配置所需要拦截的请求. 过程说明: 1>在应用启动的时候就进行装载Filter类(与 ...

  8. Java web servlet 拦截器 以登陆为例子

    以登陆为例子............... public class LoginFilter implements Filter { @Override public void destroy() { ...

  9. 面试题:struts 拦截器和过滤器

    拦截器和过滤器的区别 过滤器是servlet规范中的一部分,任何java web工程都可以使用. 拦截器是struts2框架自己的,只有使用了struts2框架的工程才能用. 过滤器在url-patt ...

随机推荐

  1. spring 注解注入bean

    通过注解方式注入bean,需要在配置类下注入bean 第一步,配置扫描文件夹 首先要在spring.xml中配置需要扫描的配置类 <context:componenet-scan base-pa ...

  2. iOS中属性 (nonatomic, copy, strong, weak)的使用 By hL

    以下内容来自Stackflow的详解 1.Nonatomicnonatomic is used for multi threading purposes. If we have set the non ...

  3. MySQL 数据库SQL语句——高阶版本2

    MySQL 数据库SQL语句--高阶版本2 实验准备 数据库表配置: mysql -uroot -p show databases; create database train_ticket; use ...

  4. 大话devops

    一.敏捷的局限性的促使devops诞生 敏捷的局限性:敏捷只注重开发阶段的敏捷,未涉及到整个产品生命周期流程其他环节导致采用敏捷开发流程后效果不明显. devops成为企业数字化转型的助推器,扮演基础 ...

  5. 《STL源码剖析》学习半生记:第一章小结与反思

    不学STL,无以立.--陈轶阳 从1.1节到1.8节大部分都是从各方面介绍STL, 包括历史之类的(大致上是这样,因为实在看不下去我就直接略到了1.9节(其实还有一点1.8.3的内容)). 第一章里比 ...

  6. elasticsearch查询之三种fetch id方式性能测试

    一.使用场景介绍 elasticsearch除了普通的全文检索之外,在很多的业务场景中都有使用,各个业务模块根据自己业务特色设置查询条件,通过elasticsearch执行并返回所有命中的记录的id: ...

  7. kubeadm + containerd 部署 k8s-v1.23.3(含证书升级)

    文章目录 前言 环境准备 答应我,所有节点都要关闭防火墙 答应我,所有节点都要关闭selinux 答应我,所有节点都要关闭swap 答应我,所有节点都要开启内核模块 答应我,所有节点都要开启模块自动加 ...

  8. 利用DP-SSL对少量的标记样本进行有效的半监督学习

    作者 | Doreen 01 介绍 深度学习之所以能在图像分类.自然语言处理等方面取得巨大成功的原因在于大量的训练数据得到了高质量的标注. 然而在一些极其复杂的场景(例如:无人驾驶)中会产生海量的数据 ...

  9. [LeetCode]1450. 在既定时间做作业的学生人数

    给你两个整数数组 startTime(开始时间)和 endTime(结束时间),并指定一个整数 queryTime 作为查询时间. 已知,第 i 名学生在 startTime[i] 时开始写作业并于 ...

  10. [自动化]ssh自动化免密访问配置

    ssh简介 SSH(Secure Shell)是一种通信加密协议,加密算法包括:RSA.DSA等 RSA:非对称加密算法,其安全性基于极其困难的大整数的分解(两个素数的乘积): DSA:也是非对称加密 ...