文章目录
一、学习目标
1、它们的区别是什么?适用的场景是怎样的?它们的执行顺序怎样的?
2、它们在springboot
中怎么开发?
3、简单了解一下拦截器
底层代码的执行原理。
二、过滤器(Filter)
1、简单说明
Filter
过滤器是Servlet
容器层面的,在实现上基于函数回调,可以对几乎所有请求进行过滤。过滤器是对数据进行过滤,预处理过程,当我们访问网站时,有时候会发布一些敏感信息,发完以后有的会用*
替代,还有就是登陆权限控制等,一个资源,没有经过授权,肯定是不能让用户随便访问的,这个时候,也可以用到过滤器。
过滤器的功能还有很多,例如实现URL
级别的权限控制、压缩响应信息、编码格式等等。
SpringBoot
实现过滤器,常见有三种方式,越复杂功能越强大。
2、代码案例
3、源码探究
三、监听器(Listener)
1、简单说明
Listener
监听器也是Servlet
层面的,可以用于监听Web应用
中某些对象、信息的创建、销毁和修改等动作发生,然后做出相应的响应处理。根据监听对象,将监听器分为3类:
ServletContext
:对应application
,实现接口ServletContextListener
。在整个Web
服务中只有一个,在Web
服务关闭时销毁。可用于做数据缓存,例如结合redis,在Web
服务创建时从数据库拉取数据到缓存服务器。
HttpSession
:对应session
会话,实现接口HttpSessionListener
。在会话起始时创建,一端关闭会话后销毁。可用作获取在线用户数量。
ServletRequest
:对应request
,实现接口ServletRequestListener
。request
对象是客户发送请求时创建的,用于封装请求数据,请求处理完毕后销毁。可用作封装用户信息。
在写Listener
的类时,实现方式和Filter
一样,同样有两种实现方式。
一种是只加@Component
;
另一种是 @WebListener 和 @ServletComponentScan
配合使用。
不过实现接口则根据监听对象区分,如:ServletContextListener
、HttpSessionListener
和ServletRequestListener
。
2、代码案例
3、源码探究
四、拦截器(Interceptor)
1、简单说明
Interceptor
拦截器和Filter和Listener
有本质上的不同,前面二者都是依赖于Servlet
容器,而Interceptor
则是依赖于Spring
框架,是aop
的一种实现,基于Java
的动态代理
实现的。在SpringBoot
中实现拦截器的方式,有点类似于实现过滤器的第三种方式,所以要通过下面两个步骤。
声明拦截器的类:通过实现 HandlerInterceptor
接口,实现preHandle、postHandle和afterCompletion
方法。
通过配置类配置拦截器:通过实现WebMvcConfigurer
接口,实现addInterceptors
方法。
一个springboot
工程中,可以有多个拦截器。
2、代码案例
拦截器接口规范
1、声明拦截器
/**
* 登录检查
* 1、配置好拦截器要拦截哪些请求
* 2、把这些配置放在容器中
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}",requestURI);
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
//放行
return true;
}
//拦截住。未登录。跳转到登录页
request.setAttribute("msg","请先登录");
// re.sendRedirect("/");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
/**
* 目标方法执行完成以后
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}",modelAndView);
}
/**
* 页面渲染以后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}",ex);
}
}
2、配置拦截器
/**
* 1、编写一个拦截器实现HandlerInterceptor接口
* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
}
}
3、springmvc
实现方式
声明拦截器,是一样的。
配置拦截器,是在spring-mvc.xml
中配置的。
<mvc:interceptors>
<mvc:interceptor>
<!-- 匹配的是url路径, 如果不配置或/**,将拦截所有的Controller-->
<mvc:mapping path="/**" />
<!-- /register 和 /login 不需要拦截-->
<mvc:exclude-mapping path="/User/login.do" />
<bean class="com.kfc.interceptor.TokenInterceptor"></bean>
</mvc:interceptor>
<!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法-->
</mvc:interceptors>
4、springboot
配置多个拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor01()).addPathPatterns("/**").excludePathPatterns("/templates/**").order(2);
registry.addInterceptor(new MyInterceptor02()).addPathPatterns("/**").excludePathPatterns("/templates/**").order(1);
}
}
那么,配置多个拦截器的执行顺序是:
order越小,越先执行,否则按照register注册的顺序执行
3、源码探究
springboot
的所有请求,从org.springframework.web.servlet.DispatcherServlet#doDispatch
方法入手,探究拦截器原理,也不例外。
经过前面的学习,我们知道,请求进入doDispatch
后,先根据HandlerMapping
找到对应的处理请求的controller
方法。
在mappedHandler
中,我们可以看到所有的拦截器集合
接下来,会根据handler
查找对应的handlerAdapter
。
在用handlerAdapter
去执行controller
方法。
而在执行方法前,springboot
会先执行拦截器链的preHandle
方法。
会看到,先调用的applyPreHandle
,然后在调用handlerAdapter
的handle
方法。
所有拦截器的preHandle
方法正常执行返回true
后,才执行controller
方法,否则,直接return
,进入finally
块结束调用。
进入applyPreHandle
方法,看看具体逻辑
org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle和triggerAfterCompletion
这里主要是,循环调用所有的拦截器的preHandle
方法。如果,有返回false
的,则触发afterCompletion
方法,afterCompletion
方法是倒序调用的。
当正常执行preHandle
方法后,会执行controller
方法,然后,倒序执行拦截器的postHandle
postHandle
执行完后,执行response
响应。
进入processDispatchResult
方法内部
在方法的最下面,依旧执行了拦截器的afterCompletion
方法。
到此,一个正常的请求结束。
如果在执行了preHandle
一切正常,但是,在controller
方法内,发生了异常。
那么会进入doDispatch
方法的catch
异常捕获环节,在catch
中执行拦截器的afterCompletion
方法。
4、源码逻辑梳理
1、根据当前请求,找到HandlerExecutionChain
【可以处理请求的handler
以及handler
的所有 拦截器】
2、先来顺序执行 所有拦截器的 preHandle
方法
● 1、如果当前拦截器prehandler
返回为true
。则执行下一个拦截器的preHandle
● 2、如果当前拦截器返回为false
。直接 倒序执行所有已经执行了的拦截器的 afterCompletion
;
3、如果任何一个拦截器返回false
。直接跳出不执行目标方法
4、所有拦截器都返回True
。执行目标方法
5、倒序执行所有拦截器的postHandle
方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion
五、AOP
1、简单说明
2、代码案例
3、源码探究
六、Servlet
1、简单说明
2、代码案例
3、源码探究
七、总结
1、范围
2、执行顺序
它们的执行顺序如下(@ControllerAdvice
本文暂不做介绍):
拦截顺序:ServletContextListener> Filter > Interception > AOP > 具体执行的Controller方法 > AOP > @ControllerAdvice > Interception > Filter > ServletContextListener
3、实现原理
根据实现原理分成下面两大类:
Filter
和Listener
:依赖Servlet
容器,基于函数回调实现。可以拦截所有请求,覆盖范围更广,但无法获取IOC容器中的Bean
。
Interceptor
和AOP
:依赖Spring
框架,基于Java
反射和动态代理实现。只能拦截Controller
的请求,可以获取IOC容器中的Bean
。
从 Filter -> Interceptor -> aop
,拦截的功能越来越细致、强大,尤其是Interceptor和aop
可以更好的结合spring
框架的上下文进行开发。但是拦截顺序也是越来越靠后,请求是先进入Servlet
容器的,越早的过滤和拦截对系统性能的消耗越少。具体选用哪种方法,就需要开发人员根据实际业务情况综合考虑了。