监听器,过滤器,拦截器和切面的使用示例、加载/执行顺序、生命周期
本博文示例代码可参考:GitHub
切换语言 | Language(On GitHub):中文版 | English Version
如何在spring boot项目中使用
1. 监听器
1.1. 实现接口ServletRequestListener。并且有许多类型的Listener。比如ServletRequestAttributeListener …
1.2. 添加注解@WebListener
1.3. 让spring可以扫描到它,使用@ServletComponentScan指定可以扫描的类
下面是启动类的代码
@SpringBootApplication
@ServletComponentScan(basePackages = {"top.agno"})
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
下面是监听器类的代码
@Slf4j
@WebListener
public class CustomListenerA implements ServletRequestListener {
public CustomListenerA() {
log.info("监听器 {} 加载", this.getClass().getName());
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
log.info("监听器 {} 销毁", this.getClass().getName());
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
log.info("监听器 {} 初始化", this.getClass().getName());
}
}
2. 过滤器
过滤器在spring boot项目中有两种定义方法。
2.1. 使用@Configuration类或配置文件。
在Spring中,由@Configuration注解的类可以看作是配置文件。
所以我将只展示一种@Configuration方式。
2.1.1. @Configuration class.
@SpringBootApplication
@Configuration // @Step 1
@ServletComponentScan(basePackages = {"top.agno"})
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
@Bean
@Order(1)
public FilterRegistrationBean customFilterA() { // @Step 2
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setEnabled(true);
registration.setFilter(new CustomFilterA());
registration.addUrlPatterns("/*");
return registration;
}
}
2.1.2. Filter class.
@Slf4j
public class CustomFilterA implements Filter /* implements Filter interface. */ {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("过滤器{}初始化...", this.getClass().getName());
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("执行过滤器{}逻辑...", this.getClass().getName());
filterChain.doFilter(servletRequest, servletResponse);
log.info("执行过滤器{}逻辑 执行结束...", this.getClass().getName());
}
@Override
public void destroy() {
log.info("过滤器{}销毁...", this.getClass().getName());
}
}
2.2. Use @WebFilter annotation.
@Slf4j
@Order(-100)
@WebFilter(urlPatterns = "/*", filterName = "customFilterB") // @step 1. Add @WebFilter and set some params.
public class CustomFilterB implements Filter /* @step 2. implements Filter interface. */ {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("过滤器{}初始化...", this.getClass().getName());
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("执行过滤器{}逻辑...", this.getClass().getName());
filterChain.doFilter(servletRequest, servletResponse);
log.info("执行过滤器{}逻辑 执行结束...", this.getClass().getName());
}
@Override
public void destroy() {
log.info("过滤器{}销毁...", this.getClass().getName());
}
}
3. 拦截器
拦截器也需要使用配置文件
3.1. 使用带有@Configuration的类来添加拦截器
@Configuration
class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomInterceptorA()).addPathPatterns("/**");
registry.addInterceptor(new CustomInterceptorB()).addPathPatterns("/**");
}
}
附:不推荐使用抽象类WebMvcConfigurerAdapter,因为它已被废弃。可使用WebMvcConfigurer接口添加拦截器。
3.2. Interceptor class.
@Slf4j
public class CustomInterceptorA implements HandlerInterceptor {
public CustomInterceptorA() {
log.info("拦截器 {} 开始加载", this.getClass().getName());
}
// 请求处理之前调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("拦截器 {} preHandler 方法开始执行", this.getClass().getName());
// return true 继续下面的逻辑
// return false 本次拦截失效
return true;
}
// 请求处理结束,视图渲染之前调用(Controller方法调用之后)
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("拦截器 {} postHandler 方法开始执行", this.getClass().getName());
}
// 请求结束,在 DispatcherServlet 渲染视图后调用
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("拦截器 {} afterCompletion 方法开始执行", this.getClass().getName());
}
}
4. 切面
4.1. 定义一个注解 (@CustomAnnotation)
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String value() default "";
}
4.2. 切面具体逻辑
@Aspect
@Slf4j
@Component
public class ControllerAspect {
public ControllerAspect() {
log.info("切面{}开始初始化", this.getClass().getName());
}
@Pointcut("@annotation(top.agno.project.test.web.aspects.CustomAnnotation)")
public void controllerAspect(){}
@Around("controllerAspect()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
log.info("切面{}开始执行", this.getClass().getName());
Object returnContent = pjp.proceed();
log.info("切面{}执行结束", this.getClass().getName());
return returnContent;
}
}
总结
初始化顺序: Listener(Constructor) > Filter(Init) > Aspect(Constructor) > Interceptor(Constructor)
执行顺序: Listener(initialized()) > Filter(doFilter | before doFilter()) > Interceptor(preHandler) > Aspect(Around | before processed()) >
business_logic() >
Aspect(Around | after processed()) > Interceptor(postHandler) > Interceptor(afterCompletion) > Filter(doFilter | after doFilter()) > Listener(destroy())
附.1. 当第一个请求进入时,DispatcherServlet将在Listener(initialized())之后初始化。
附.2. 有些Servlet容器并不受Spring Bean的管理,需要使用@ServletComponentScan去确保这些容器可以被扫描到。
效果图如下所示: