过滤器:Filter
描述:Filter对web服务器管理的所有资源进行拦截,例如实现URL级别的权限访问控制、过滤敏感词汇等。
大致流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter接口中有一个doFilter方法,里面编写我们的业务逻辑,配置对哪个资源进行拦截,在调用service方法之前,都会先调用一下filter的doFilter方法,也可以编写多个Filter,这些Filter组合起来称之为一个Filter链,也可以控制先调用哪个Filter。web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
public void init(FilterConfig filterConfig) //初始化
Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,
完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) //这个方法完成实际的过滤操作。
public void destroy() //销毁
OncePerRequestFilter
Spring的OncePerRequestFilter类实际上是一个实现了Filter接口的抽象类。spring对Filter进行了一些封装处理,能够确保在一次请求只通过一次filter,
而不需要重复执行,因为在执行完后会在request设置标识符。
@Override
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
throw new ServletException("OncePerRequestFilter just supports HTTP requests");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
//得到一个固定标识,这个标识的作用是标记:该过滤器是否被执行过。也就是这个来实现执行一次的操作。
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
//检测当前请求是否已经拥有了该标记,如果拥有该标记则代表该过滤器执行过了
boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
//如果执行过,或者不在拦截范围直接跳过,
if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {
filterChain.doFilter(request, response);
}else {
//往request设置标识。
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
//这个方法是一个抽象方法需要子类去实现具体的过滤逻辑。
doFilterInternal(httpRequest, httpResponse, filterChain);
}finally {
//移除该标记。什么时候会执行到这里?
request.removeAttribute(alreadyFilteredAttributeName);
}
}
}
那么doFilterInternal应该做什么?
public class xxxx extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain){
//执行业务代码
//根据需要是否调用doFilter
filterChain.doFilter(requestToUse, response);
}
}
所以回到上面为什么会在finally里面调用request.removeAttribute() 也就是在请求接口最后,在执行移除标识操作。
拦截器:
SpringBoot之HandlerInterceptorAdapter
在SpringBoot中我们可以使用HandlerInterceptorAdapter这个适配器来实现自己的拦截器。这样就可以拦截所有的请求并做相应的处理。
应用场景:请求日志,权限,JVM性能输出等。
在HandlerInterceptorAdapter中主要提供了以下的方法:
- preHandle:在方法被调用前执行。如果返回true,则继续调用下一个拦截器。如果返回false,则中断执行。
- postHandle:在方法执行后调用。
- afterCompletion:在整个请求处理完毕后进行回调,也就是调用方已经拿到响应时。
拦截器和过滤器的区别:
- 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
- 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
- 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
- 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
- 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
- 拦截器可以获取ioc中的service bean实现业务逻辑,拦截器可以获取ioc中的service
bean实现业务逻辑,在拦截器里注入一个service,可以调用业务逻辑。
触发时机:
过滤器是在请求进入容器后,但请求进入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){
System.out.println("before...");
chain.doFilter(request, response);
System.out.println("after...");
}
chain.doFilter(request, response);这个方法的调用作为分水岭。事实上调用Servlet的doService()方法是在chain.doFilter(request, response);这个方法中进行的。
2.拦截器是被包裹在过滤器之中的。
拦截器的preHandle()方法是在过滤器的chain.doFilter(request, response)方法的前一步执行,不是doFilter(request,response,chain)之前。
postHandle()方法在return ModelAndView之前,可以操控Controller的内容。
afterCompletion()方法是在过滤器返回给前端前一步执行。也就是doFilter(request,response,chain)方法return之前执行。
在我们项目中用的最多是preHandle这个方法,而未用其他的,框架提供了一个已经实现了拦截器接口的适配器类HandlerInterceptorAdapter,继承这个类重写一下需要用到的方法就行了,可以少几行代码,这种方式Java中很多地方都有体现。
本文摘自:
https://www.cnblogs.com/panxuejun/p/7715917.html
https://www.cnblogs.com/weianlai/p/11358768.html
https://blog.csdn.net/u012554102/article/details/51462161
https://www.cnblogs.com/cainiao-chuanqi/p/11326793.html