Spring Boot 2系列(二十七):Listener, Filter, Interceptor

  Listener 监听器,Filter 过滤器,Interceptor 拦截器是 Java Web领域非常重要的三大神器(组件),会经常使用到。

  关注这三个的知识点本篇不做描述,主要记录在Spring Boot框架中这三大组件的使用。

  前面文章也有提到:Spring事件监听,SpringMVC之HandlerInterceptor拦截器, Spring Boot实践系列(二十二):Web相关配置详解

  官方文档:Spring Boot -> Application Events and Listeners,Spring Boot -> Servlets, Filters, Listeners, Spring Boot -> Add a Servlet, Filter, or Listener to an Application

Spring Boot

在 Spring Boot 框架中,将自定义的 Servlet, Filter, Listener 注册为 Spring Bean有两种方式:

  1. Spring Boot框架中在自定义的 Servlet, Filter, Listener 三大组件类上添加@Component 或 @Configuration注解将监听器注册为 Spring Bean, 可调用 IoC 容器的产资源(Bean)。
  2. 在自定义的 Servlet, Filter, Listener 类上添加@WebServlet, @WebFilter, @WebListener注解,会自动将自定义三大给件类注册为 Servlet 组件并启动。

注意:如果使用的是 Spring Boot 内嵌的 Servlet 容器,还需要在使用了@Configuration注解的类上添加@ServletComponentScan注解; 如果部署在外部独立的Servlet 容器,则不需要**@ServletComponentScan**注解,由独立容器的内部发现机制将自定义的三大组件类注册为 Servlet组件。

@ServletComponentScan注解,是在使用 Spring Boot 内嵌的 Servlet 容器时需要添加,用于是启用扫描使用了**@WebServlet, @WebFilter, @WebListener**注解的类并将之注册为 Servlet 组件, 可通过valuebasePackages属性指定自定义的 Servlet, Filter, Listener 所在的包路径, 就会自动将该包下使用了 @WebServlet, @WebFilter, @WebListener 注解的自定义类注册 Servlet 组件; 默认情况下会从 @ServletComponentScan注解所在类的包开始扫描,所以很多时候直接把这个注解放在启动入口类上。

Listener

Listener是 Servlet 规范,由 Servlet 容器提供支持,随Servlet容器启动就启动监听, 监听到事件时,在监听器里面相应的方法里执行处理。
Servlet 提供的3个基本监听器接口:ServletContextListener,ServletRequestListener,HttpSessionListener,使用时实现用到的接口,重写里面的方法 。

  1. 根据创建的Session数统计在线人数
    根据 Session 统计 Web 站点的计在线人数并不十分准确, 若用户离线了但 Session 仍在有效期内则认为这个用户仍在线。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @Name: CountOnlineUserListener
* @Desc: 统计在线用户数
**/
@WebListener
public class CountOnlineUserListener implements HttpSessionListener {

private static final Logger logger = LogManager.getLogger(CountOnlineUserListener.class);

private static final String TOTAL_ONLINE_USER="totalOnlineUser";

private RedisTemplate<String, Integer> redisTemplate;

@Override
public void sessionCreated(HttpSessionEvent event) {

HttpSession session = event.getSession();
//session默认有效时间是30分钟,可不设置
session.setMaxInactiveInterval(30 * 60);
ServletContext servletContext = session.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
redisTemplate = (RedisTemplate<String, Integer>) webApplicationContext.getBean("redisTemplate");
Integer integer = redisTemplate.opsForValue().get(TOTAL_ONLINE_USER);
integer = (integer == null ? 0 : integer);
//创建Session数
redisTemplate.opsForValue().set(TOTAL_ONLINE_USER,integer + 1);
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
Integer integer = redisTemplate.opsForValue().get(TOTAL_ONLINE_USER);
redisTemplate.opsForValue().set(TOTAL_ONLINE_USER,integer - 1);
}
}
  1. 容器启动监听器,执行初始化操作
    因上面的统计在线用户数是存在 Redis 中, 如果系统宕机或崩溃是不会清空引在线用户数的值的,会存在较大误差。所在在系统起动时重置下。如果是单机系统,可以将计算的用户数放到ServletContext域中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* @Desc: 重写ServletContext
* @Date: 2018/8/14 11:29
**/
@WebListener
public class DydContextLoaderListener implements ServletContextListener {

private RedisTemplate<String, Integer> redisTemplate;

/**
* @Desc: 容器启动增加初始化操作
* @Param: [sce]
* @Return: void
**/
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
redisTemplate = (RedisTemplate<String, Integer>) webApplicationContext.getBean("redisTemplate");
//重置在线人数
redisTemplate.opsForValue().set("totalOnlineUser",0);
}

@Override
public void contextDestroyed(ServletContextEvent sce) {

}
}

Filter

Filter过滤器是 Servlet 规范,由 Servlet 容器提供支持,在 Servlet 之前和之后起作用,无法调用 Spring IOC 中的资源。

基础知识,参考 Servlet 编写过滤器

Interceptor

Interceptor 是 Spring 提从的组件,由 Spring 框架提供支持,由 Spring 管理,可在拦截器中注入 IoC 中的资源。

  1. 登录拦截器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    /**
    * @ClassName LoginInterceptor
    * @Description 登录验证拦截器
    **/
    @Component
    public class LoginInterceptor implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(LoginInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    HttpSession session = httpServletRequest.getSession();
    // 判断是否登录
    if (null != session.getAttribute("SESSION_USER_OBJ")) {
    return true;
    } else {
    httpServletResponse.sendRedirect("/user/toLogin");
    return false;
    }
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
    }
  2. 配置拦截器和放行路径
    注:新的 Spring 5.0 是实现 WebMvcConfigurer 接口,WebMvcConfigurerAdapter 已标注为过期。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    @Configuration
    public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(loginInterceptor())
    .addPathPatterns("/**")
    .excludePathPatterns("/login");
    }

    /*
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    //注册自定义拦截器,添加拦截路径和排除拦截路径
    registry.addInterceptor(new LoginInterceptor())
    .addPathPatterns("/**")
    .excludePathPatterns("/index")
    .excludePathPatterns("/user/register")
    .excludePathPatterns("/user/login")
    .excludePathPatterns("/error");
    }
    */

    @Bean
    public LoginInterceptor loginInterceptor() {
    return new LoginInterceptor();
    }
    }

三大神器的执行顺序:监听电话 -> 过滤垃圾信号 -> 拦截恐怖事件,任务处理完则原路返回。

注意

在注册 监听器、过滤器或拦截器时,最好是先通过 @Bean 注解将其声明为 Bean,注册时调用申请为 Bean 的方法 来使用。

因为这三大组件在注册时先于 Bean 注册,如果在这三大给件里注入了其它 Bean,在引用时就会报空指针。例如,在拦截器里注入了 RedisTemplate,在 WebMvcConfigurer 添加拦截器时,使用 new 的方式,则

参考

Java三大器之监听器

Spring Boot 2系列(二十七):Listener, Filter, Interceptor

http://blog.gxitsky.com/2018/08/14/SpringBoot-27-listener-filter-interceptor/

作者

光星

发布于

2018-08-14

更新于

2022-06-17

许可协议

评论