目录
WebMvcConfigurer详解:https://blog.youkuaiyun.com/zhangpower1993/article/details/89016503/
一、过滤器 Filter
过滤器( Filter),是 JavaEE 的标准,依赖于 Servlet 容器,使用的时候是配置在 SpringMVC 框架中是配置在web.xml 文件中的,可以配置多个,执行的顺序是根据配置顺序从上到下。在 SpringBoot 项目中也可以采用注解的形式实现。
Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是设置字符集、控制权限、控制转向、做一些业务逻辑判断等。其工作原理是,只要你在web.xml文件配置好要拦截的客户端请求,它都会帮你拦截到请求,此时你就可以对请求或响应(Request、Response)统一配置请求编码以及过滤一些非法参数,垃圾信息,简化操作;同时还可进行逻辑判断,如用户是否已经登陆、有没有权限访问该页面等等工作。它是随你的web应用启动而启动的,只初始化一次,以后就可以拦截相关请求,只有当你的web应用停止或重新部署的时候才销毁。
Filter可以认为是Servlet的一种“加强版”,它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理,是个典型的处理链。Filter也可以对用户请求生成响应,这一点与Servlet相同,但实际上很少会使用Filter向用户请求生成响应。使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter有如下几个用处:
- 在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。
- 根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。
- 在HttpServletResponse到达客户端之前,拦截HttpServletResponse。
- 根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
Filter有如下几个种类:
- 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。
- 日志Filter:详细记录某些特殊的用户请求。
- 负责解码的Filter:包括对非标准编码的请求解码。
- 能改变XML内容的XSLT Filter等。
- Filter可以负责拦截多个请求或响应;一个请求或响应也可以被多个Filter拦截。
javax.servlet.Filter接口
创建Filter必须实现javax.servlet.Filter接口,在该接口中定义了如下三个方法。
- void init(FilterConfig config):用于完成Filter的初始化。
- void destory():用于Filter销毁前,完成某些资源的回收。
- void doFilter(ServletRequest request,ServletResponse response,FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理。该方法可以实现对用户请求进行预处理(ServletRequest request),也可实现对服务器响应进行后处理(ServletResponse response)—它们的分界线为是否调用了chain.doFilter(),执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。
1.通过@WebFilter 注解配置
用这种方式配置TestFilter1、TestFilter2
@WebFilter(urlPatterns = "/hello")
public class TestFilter1 implements Filter {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("##############Filter1 init##############");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//在DispatcherServlet之前执行
System.out.println("##############doFilter1 before##############");
filterChain.doFilter(servletRequest, servletResponse);
// 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("##############doFilter1 after##############");
}
@Override
public void destroy() {
System.out.println("##############Filter1 destroy##############");
}
}
并且在启动类添加 @ServletComponentScan
@SpringBootApplication
@ServletComponentScan
public class TestbootApplication {
public static void main(String[] args) {
SpringApplication.run(TestbootApplication.class, args);
}
}
2.通过@Bean来配置
用这种方式配置TestFilter3、TestFilter4。
创建过滤器:
@Component
public class TestFilter3 implements Filter{
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
System.out.println("##############Filter3 init##############");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//在DispatcherServlet之前执行
System.out.println("##############doFilter3 before##############");
filterChain.doFilter(servletRequest, servletResponse);
// 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("##############doFilter3 after##############");
}
@Override
public void destroy() {
System.out.println("##############Filter3 destroy##############");
}
}
注册过滤器:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean testFilter3RegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter3());
registration.addUrlPatterns("/hello");
registration.setOrder(1); // 值越小越靠前,此处配置有效
return registration;
}
@Bean
public FilterRegistrationBean testFilter4RegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean(new TestFilter4());
registration.addUrlPatterns("/hello");
registration.setOrder(2);
return registration;
}
}
配置order,值越小越靠前
由此可见,@Order的顺序配置没有起作用,registration.setOrder()是有效的。
2、 拦截器 Interceptor
拦截器(Interceptor) 不依赖 Servlet 容器,依赖 Spring 等 Web 框架,在 SpringMVC 框架中是配置在SpringMVC 的配置文件中,在 SpringBoot 项目中也可以采用注解的形式实现。
拦截器是 AOP 的一种应用,底层采用 Java 的反射机制来实现的。与过滤器一个很大的区别是在拦截器中可以注入 Spring 的 Bean,能够获取到各种需要的 Service 来处理业务逻辑,而过滤器则不行。
HandlerInterceptor接口:
-
preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
-
postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解释我们知道这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。
-
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
拦截器的使用
interceptor 的执行顺序大致为:
- 请求到达 DispatcherServlet
- DispatcherServlet 发送至 Interceptor ,执行 preHandle
- 请求达到 Controller
- 请求结束后,postHandle 执行
Spring 中主要通过 HandlerInterceptor 接口来实现请求的拦截,实现 HandlerInterceptor 接口需要实现下面三个方法:
- preHandle() – 在handler执行之前,返回 boolean 值,true 表示继续执行,false 为停止执行并返回。
- postHandle() – 在handler执行之后, 可以在返回之前对返回的结果进行修改
- afterCompletion() – 在请求完全结束后调用,可以用来统计请求耗时等等
配置实现拦截器
1.创建拦截器
自定义一个拦截器实现HandlerInterceptor,实现preHandle,postHandle,afterCompletion三个方法。
@Component
public class TestInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("##############TestInterceptor1 preHandle##############");
return true;
}
//在Controller之后的DispatcherServlet之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("##############TestInterceptor1 postHandle##############");
}
// 在页面渲染完成之后返回给客户端执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("##############TestInterceptor1 afterCompletion##############");
}
}
2. 注册拦截器
自定义拦截器配置类继承自WebMvcConfigurer,重写addInterceptors将自定义的拦截器添加至注册中心。
@Component
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private TestInterceptor1 testInterceptor1;
@Autowired
private TestInterceptor2 testInterceptor2;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(testInterceptor1)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
.addPathPatterns("/hello");
registry.addInterceptor(testInterceptor2).addPathPatterns("/hello");
}
}
注册顺序即为执行顺序。
WebMvcConfigurer常见用途:
- addInterceptors:拦截器
- addViewControllers:页面跳转
- addResourceHandlers:静态资源
- configureDefaultServletHandling:默认静态资源处理器
- configureViewResolvers:视图解析器
- configureContentNegotiation:配置内容裁决的一些参数
- addCorsMappings:跨域
- configureMessageConverters:信息转换器
WebMvcConfigurer详解:
https://blog.youkuaiyun.com/zhangpower1993/article/details/89016503/
SpringMVC流程:
具体流程如下
- 用户发起请求到前端控制器(Controller)
- 前端控制器没有处理业务逻辑的能力,需要找到具体的模型对象处理(Handler),到处理器映射器(HandlerMapping)中查找Handler对象(Model)。
- HandlerMapping返回执行链,包含了2部分内容: ① Handler对象、② 拦截器数组
- 前端处理器通过处理器适配器包装后执行Handler对象。
- 处理业务逻辑。
- Handler处理完业务逻辑,返回ModelAndView对象,其中view是视图名称,不是真正的视图对象。
- 将ModelAndView返回给前端控制器。
- 视图解析器(ViewResolver)返回真正的视图对象(View)。
- (此时前端控制器中既有视图又有Model对象数据)前端控制器根据模型数据和视图对象,进行视图渲染。
- 返回渲染后的视图(html/json/xml)返回。
- 给用户产生响应。
核心就是DispatcherServlet核心控制器 。
三、AOP的基本概念
- Aspect(切面):通常是一个类,里面可以定义切入点和通知
- JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
- Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
- Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
- AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
Spring AOP
Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。
基于注解的AOP配置方式
启用@AsjectJ支持,在applicationContext.xml中配置下面一句:
<aop:aspectj-autoproxy />
- Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
- AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
- AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名
来访问目标方法中所抛出的异常对象
- After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式
- Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint,使用环绕通知,切入要切入的类,当请求的时候回拦截下来,这样可以获取拦截的方法的参数。
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.nbkj.controller.UserController.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("time aspect start");
Object[] args = proceedingJoinPoint.getArgs();
for (Object arg : args) {
System.out.println(arg.getClass().getName());
System.out.println("arg is " + arg);
}
long startTime = new Date().getTime();
Object obj = proceedingJoinPoint.proceed();
System.out.println("time aspect 耗时" + (new Date().getTime() - startTime));
System.out.println("time aspect end");
return obj;
}
}
总结:
下面用一张图说一下过滤器、Servlet容器、拦截器、AOP、Controller之间的关系:
区别:
Spring的Interceptor(拦截器)与Servlet的Filter有相似之处,比如二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。不同的是:
拦截器是 AOP 的一种应用,底层采用 Java 的反射机制来实现的。与过滤器一个很大的区别是在拦截器中可以注入 Spring 的 Bean,能够获取到各种需要的 Service 来处理业务逻辑,而过滤器则不行。
Filter | Interceptor | Summary |
---|---|---|
Filter 接口定义在 javax.servlet 包中 | 接口 HandlerInterceptor 定义在org.springframework.web.servlet 包中 | |
Filter 定义在 web.xml 中 | ||
Filter在只在 Servlet 前后起作用。Filters 通常将 请求和响应(request/response) 当做黑盒子,Filter 通常不考虑servlet 的实现。 | 拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入(hook into)请求的生命周期,在请求过程中获取信息,Interceptor 通常和请求更加耦合。 | 在Spring构架的程序中,要优先使用拦截器。几乎所有 Filter 能够做的事情, interceptor 都能够轻松的实现 |
Filter 是 Servlet 规范规定的。 | 而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。 | 使用范围不同 |
Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的。 | 而拦截器是在 Spring容器内的,是Spring框架支持的。 | 规范不同 |
Filter 不能够使用 Spring 容器资源 | 拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可 | Spring 中使用 interceptor 更容易 |
Filter 是被 Server(Tomcat等) 调用 | Interceptor 是被 Spring 调用 | 因此Filter总是优先于Interceptor执行 |
-
Filter需要在web.xml中配置,依赖于Servlet;
-
Interceptor需要在SpringMVC中配置,依赖于框架;
-
两者的本质区别:拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。
四、监听器Listner
监听器也叫Listener,是servlet的监听器,可以用于监听Web应用中某些对象,信息的创建,销毁,增加,修改,删除等动作的发生,然后做出相应的响应处理。当范围对象的状态发生变化时,服务器自动调用监听器对象中的方法,常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等。
监听器的简单使用如下:先编写监听器的实现:
@WebListener
public class WebListenerDemo implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("监听器初始化。。。。。。。。。。。。");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("监听器销毁。。。。。。。。。。。");
}
}
监听session创建的监听器(可以用来统计在线人数)
@WebListener
public class SessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("。。。创建session成功");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("。。。销毁session");
}
}
然后在启动类上添加注解@ServletComponentScan即可,当然也可以注册到spring容器中省却@ServletComponentScan注解。
@Configuration
public class ListenerConfig {
@Bean
public ServletListenerRegistrationBean servletListenerRegistrationBean() {
ServletListenerRegistrationBean slrBean = new ServletListenerRegistrationBean();
slrBean.setListener(new WebListenerDemo());
return slrBean;
}
@Bean
public ServletListenerRegistrationBean sessionListenerRegistrationBean() {
ServletListenerRegistrationBean slrBean = new ServletListenerRegistrationBean();
slrBean.setListener(new SessionListener());
return slrBean;
}
}
参考:
过滤器(Filter)和拦截器(Interceptor)的执行顺序和区别 - kuotian - 博客园
SpringBoot---WebMvcConfigurer详解_zhangpower1993的博客-优快云博客_webmvcconfigurer
SpringBoot 全局异常捕获不到Filter中的异常_ACGkaka的博客-优快云博客_springboot捕获filter异常
通过ResponseBodyAdvice包装统一返回类型_Lemcoo的博客-优快云博客_responsebodyadvice 统一返回
spring boot 拦截 以及Filter和interceptor 、Aspect区别 - JAVA-ANDROID - 博客园