在Java Web的开发中,最原始和初期的学习都是从Servlet
开始的,Servlet是Java最为耀眼的技术,也是Java EE的技术变革。目前大火主流的框架spring boot也的spring mvc部分也是基于拓展servlet完成的。回到之前的文章spring 实现了对servlet的封装,来构造控制器controller的过程:Spring MVC 框架基础知识,以及Servlet相关知识
在Java Web开发过程中,除了servlet之外,还有比较重要的拦截器、过滤器、监听器。它们的具体功能如下:
-
Servlet:Web 容器是运行在服务器端的 Java 应用程序。它与平台和协议无关,并且可以动态生成网页。它工作在客户端请求和服务器响应之间的中间层。
-
过滤器:过滤器是可重用的代码片段,可用于转换 HTTP 请求、响应和标头信息。 Filter不像Servlet,它不能生成请求或响应,它只修改对某个资源的请求,或者修改来自某个资源的响应。
-
Listener:监听器,监听器主要用于监听。通过监听器,您可以监听Web服务器中的执行动作,并根据其要求做出相应的响应。
-
拦截器:拦截器主要用于拦截程序并进行相应的处理。例如,拦截器可用于验证权限、记录请求信息日志、判断用户是否登录等。
-
ssm中web.xml中配置了servlet、filter、listener,web.xml中没有配置interceptor。 Spring的拦截器是在spring.xml中配置的。在spring boot中,大多是通过配置类注入。他们的关系如下图所示:
过滤器
Servlet 过滤器是可在 Servlet 编程中使用的 Java 类,具有以下用途:
- 在客户端请求访问后端资源之前拦截它们。
- 在将服务器的响应发送回客户端之前对其进行处理。
规范建议的各类过滤器:
(1)身份验证过滤器(Authentication Filters)。
(2)数据压缩过滤器(Data compression Filters)。
(3)加密过滤器(Encryption Filters)。
(4)触发资源访问事件过滤器。
(5)图像转换过滤器(Image Conversion Filters)。
(6)日志记录和审核过滤器(Logging and Auditing Filters)。
(7)mime-TYPE 链过滤器(MIME-TYPE Chain Filters)。
(8)标记化过滤器(Tokenizing Filters)。
(9)XSL/T 过滤器(XSL/T Filters),转换 XML 内容。
过滤器被部署在部署描述符文件 web.xml
中,然后映射到您的应用程序的部署描述符中的 Servlet 名称或 URL 模式。
当 Web 容器启动您的 Web 应用程序时,它会为您在部署描述符中声明的每个过滤器创建一个实例。过滤器按照它们在部署描述符中声明的顺序执行。
Servlet 过滤器示例:
- 生产过滤器 Filter 和 Web 容器 servlet
过滤器是一个包装 Servlet,因此两者都必须存在
//servlet
//导入必需的 java 库
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/demo")
//扩展 HttpServlet 类
public class DemoServlet extends HttpServlet {
// 处理 GET 方法请求的方法
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// 设置响应内容类型
response.setContentType("text/HTML;charset=UTF-8");
PrintWriter out = response.getWriter();
String title = "HTTP Header 请求实例";
String docType =
"<!DOCTYPE html> \n";
out.println(docType +
"<html>\n" +
"<head><meta charset=\"utf-8\"><title>" + title + "</title></head>\n"+
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + title + "</h1>\n" +
"<table width=\"100%\" border=\"1\" align=\"center\">\n" +
"<tr bgcolor=\"#949494\">\n" +
"<th>Header 名称</th><th>Header 值</th>\n"+
"</tr>\n");
Enumeration headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String paramName = (String)headerNames.nextElement();
out.print("<tr><td>" + paramName + "</td>\n");
String paramValue = request.getHeader(paramName);
out.println("<td> " + paramValue + "</td></tr>\n");
}
out.println("</table>\n</body></html>");
}
// 处理 POST 方法请求的方法
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
//Filter过滤器
// 导入必需的 java 库
import java.io.*;
import javax.servlet.*;
import javax.servlet.Http.*;
import java.util.*;
// 实现 Filter 类
public class LogFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
// 获取初始化参数
String site = config.getInitParameter("Site");
// 输出初始化参数
System.out.println("网站名称: " + site);
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws java.io.IOException, ServletException {
// 获取客户机的 IP 地址
String ipAddress = request.getRemoteAddr();
// 记录 IP 地址和当前时间戳
System.out.println("IP "+ ipAddress + ", Time "
+ new Date().toString());
// 把请求传回过滤链
chain.doFilter(request,response);
}
public void destroy( ){
/* 在 Filter 实例被 Web 容器从服务移除之前调用 */
}
}
web.xml
配置servlet和Filter以及过滤器和web容器的映射关系
//过滤器配置
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.test.LogFilter</filter-class>
<init-param>
<param-name>Site</param-name>
<param-value>kubiji在线教程</param-value>
</init-param>
</filter>
//servlet配置
<servlet>
<!-- 类名 -->
<servlet-name>DemoServlet</servlet-name>
<!-- 所在的包 -->
<servlet-class>com.=test.DemoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DemoServlet</servlet-name>
<!-- 访问的网址 -->
<url-pattern>/demo</url-pattern>
</servlet-mapping>
//Servlet Filter Mapping
<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
过滤范围:
一个过滤器的作用范围与路径配置有关url-pattern
可以配置/*
所有路径也可以配置某个web容器,所以Web 应用程序可以根据特定的目的定义若干个不同的过滤器链:
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>com.kubiji.test.LogFilter</filter-class>
<init-param>
<param-name>test-param</param-name>
<param-value>Initialization Paramter</param-value>
</init-param>
</filter>
<filter>
<filter-name>AuthenFilter</filter-name>
<filter-class>com.kubiji.test.AuthenFilter</filter-class>
<init-param>
<param-name>test-param</param-name>
<param-value>Initialization Paramter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>AuthenFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
过滤器应用顺序:
web.xml 中过滤器映射元素的顺序决定了 Web 容器将过滤器应用到 servlet 的顺序。要反转过滤器的顺序,您只需反转 web.xml 文件中的过滤器映射元素。
SpringMVC的过滤器位于spring-web-[xxx].jar工具包下:
如上所示的spring-web.jar
包结构所示, Spring的web包中中提供有很多过滤器,这些过滤器位于org.springframework.web.filter
并且理所当然地实现了javax.servlet.Filter
。
然而,有几种方法可以实现它:
(1) 直接实现Filter,这一类过滤器只有CompositeFilter;
(2) 继承抽象类GenericFilterBean,该类实现了javax.servlet.Filter,这一类的过滤器只有一个,即DelegatingFilterProxy;
(3) 继承抽象类OncePerRequestFilter,该类为GenericFilterBean的直接子类,这一类过滤器包括CharacterEncodingFilter、HiddenHttpMethodFilter、HttpPutFormContentFilter、RequestContextFilter和ShallowEtagHeaderFilter;
(4) 继承抽象类AbstractRequestLoggingFilter,该类为OncePerRequestFilter的直接子类,这一类过滤器包括CommonsRequestLoggingFilter、Log4jNestedDiagnosticContextFilter和ServletContextRequestLoggingFilter。
过滤器放置在容器结构中的什么位置?
过滤器放置在 Web 资源之前,可以在请求到达应用它的 Web 资源(可以是 Servlet、Jsp 页面,甚至 HTML 页面)之前拦截传入请求,并在请求到达之前拦截传出请求。返回给客户端。 。过滤器:用于拦截客户端和所请求资源之间的请求,目的是重用代码。过滤器链,web.xml 中最先配置的将首先被调用。一些初始化参数也可以在过滤器中配置。
Java中的Filter并不是标准的Servlet。它无法处理用户请求或生成对客户端的响应。它主要用于预处理HttpServletRequest,也可用于后处理HttpServletResponse。这是一个典型的加工链。
- 过滤器触发时序:
过滤器在请求进入容器之后、请求进入 servlet 之前进行预处理。请求完成后、Servlet处理完毕后、返回给前端之前的返回也是如此。
简介: 过滤器包装 servlet,servlet 包装拦截器。
- 过滤器的触发时机是容器后,servlet之前,所以过滤器的
doFilter( ServletRequest request, ServletResponse response, FilterChain chain)
的入参是ServletRequest ,而不是httpservletrequest。因为过滤器是在httpservlet之前。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("before...");
chain.doFilter(request, response);
System.out.println("after...");
}
chain.doFilter(request, response)
这个方法的调用作为分水岭。事实上调用Servlet的doService()方法是在chain.doFilter(request, response)这个方法中进行的。
- 过滤器是JavaEE标准,使用函数回调来实现。在请求进入容器之后、进入Servlet之前进行预处理,在请求返回前端之后进行后处理。
弹簧启动过滤器
在 Spring 中,过滤器在 web.xml 文件中配置和声明。所有过滤器都在 web.xml(Web 容器的配置文件)中定义。在springboot中,web.xml文件消失了。各种配置都被Java类替代。 @Configuration注解标记的是配置类而不是xml配置文件。
因此过滤器也通过注解注入到IOC容器中,注解@WebFilter
声明一个过滤器,里面的参数,filterName 为过滤器名字,urlPatterns 为过滤器的范围,initParams 为过滤器初始化参数。
@Order(1)
@WebFilter(filterName = "piceaFilter", urlPatterns = "/*" , initParams = {
@WebInitParam(name = "URL", value = "http://localhost:8080")})
使用包扫描@ServletComponentScan("com.example.demofilter.filter")
在启动类自动注册Filter。
@Order(1)
@WebFilter(filterName = "myFilter",urlPatterns = {
"/*"})
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化过滤器");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("before filter");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("after filter");
}
@Override
public void destroy() {
System.out.println("过滤器被销毁了");
}
@WebFilter注解,filterName属性代表过滤器的名称,urlPatter代表要拦截的URL资源,可以是一个也可以是多个。用于将类声明为过滤器。这个注解会在部署时被容器处理,容器会根据具体的属性配置部署相应的类作为过滤器。该注解有一些常用属性如下表(以下属性均为可选属性,但value、urlPatterns、servletNames必须至少包含其中之一,且value和urlPatterns不能共存。如果同时指定,value通常被忽略。值
@Order(1)的意思是如果有多个拦截器的话,就是设置这个拦截器的运行级别。数字越小,执行越早。
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean registFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new MyFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setName("Filter1");
registrationBean.setOrder(1);
return registrationBean;
}
}
@ServletComponentScan
:在SpringBootApplication上使用@ServletComponentScan注解后,Servlet、Filter、Listener
可以直接通过@WebServlet、@WebFilter、@WebListener
注解自动注册,无需其他代码。
拦截器
拦截器方法都是通过代理来调用的,拦截器是基于Java反射机制实现的。
第一个不是,第二个才是,Interceptor
是spring中特有的概念。在 Spring中,当请求发送到 Controller 时,在被Controller处理之前,它必须经过 Interceptors(0或多个)。Spring Interceptor是一个非常类似于Servlet Filter 的概念 。
以下情况:
public class LogInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long startTime = System.currentTimeMillis();
System.out.println("brefore login --- ");
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("Start Time: " + System.currentTimeMillis());
request.setAttribute("startTime", startTime);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("login --- ");
System.out.println("Request URL: " + request.getRequestURL());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("after loggin --- ");
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("Request URL: " + request.getRequestURL());
System.out.println("End Time: " + endTime);
System.out.println("Time Taken: " + (endTime - startTime));
}
}
@Configuration
public class MyInterceptorConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
// 将上面自定义好的拦截器添加进去。
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
Interceptor的拦截范围实际上是Controller方法,实际上相当于基于AOP的方法拦截。因为Interceptor只拦截Controller方法,所以需要注意的是,返回到ModelAndView并渲染后,后续的处理就会脱离Interceptor的拦截范围。
拦截器必须实现 HandlerInterceptor 接口,并且可以选择实现 preHandle()、postHandle() 和 afterCompletion() 方法。 preHandle()在调用Controller方法之前执行,postHandle()在Controller方法正常返回后执行,afterCompletion()无论Controller方法是否抛出异常都会执行。参数 ex 是 Controller 方法抛出的异常(未抛出的异常为 null )
过滤器和拦截器的区别:
拦截器Interceptor
同 Filter
过滤器一样,它俩都是面向切面编程——AOP 的具体实现
拦截器是基于java的反射机制的,而过滤器是基于函数回调。
拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
拦截器的概念
SpringMVC中Interceptor的主要作用是拦截用户的url请求,并在执行handler方法之前和之后添加一些特殊的请求。 SpringMVC中的Interceptor也是非常重要和有用的。其主要功能是拦截用户请求并进行相应处理。例如可以用来验证权限,或者判断用户是否登录等。
监听器
Servlet的监听器Listener是一个服务器端程序,它实现了javax.servlet.ServletContextListener接口。当 Web 应用程序启动时它也会启动。它仅初始化一次,并在 Web 应用程序停止时销毁。主要功能是:做一些初始的内容添加工作,设置一些基础内容,比如一些参数或者一些固定的对象等。
监听器是一个实现特定接口的普通Java程序。该程序专门用于监视另一个Java对象的方法调用或属性更改。当被监控对象发生上述事件时,监听器的某个方法会立即执行。
Servle监听器
在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别 ServletContext, HttpSession和ServletRequest这三个域对象。servlet监听器的注册不是直接注册在事件源上,而是由WEB容器负责注册,开发人员只需在web.xml文件中使用标签配置好监听器。