什么是 Web 监听器?Web 监听器是一种 Servlet 特殊类,它们能帮助开发者监听 Web 中特定的事件,比如 ServletContext、HttpSession、ServletRequest 的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控。

Spring Boot 中监听器的使用

Web 监听器的使用场景很多,比如监听 Servlet 上下文用来初始化一些数据、监听 HTTP Session 用来获取当前在线的人数、监听客户端请求的 ServletRequest 对象来获取用户的访问信息等等。

监听 Servlet 上下文对象

监听 Servlet 上下文对象可以用来初始化数据,用于缓存。什么意思呢?我举一个很常见的场景,比如用户在点击某个站点的首页时,一般都会展现出首页的一些信息,而这些信息基本上或者大部分时间都保持不变,但这些信息都是来自数据库。如果用户的每次点击,都要从数据库中去获取数据的话,用户量少还可以接受,如果用户量非常大的话,这对数据库也是一笔很大的开销。

针对这种首页数据,如果大部分都不常更新的话,我们完全可以把它们缓存起来,每次用户点击的时候,我们都直接从缓存中拿,这样既可以提高首页的访问速度,又可以降低服务器的压力。如果做得更加灵活一点,可以再加个定时器,定期的来更新这个首页缓存。比如一个页面的排名。

@Service
public class UserService {/*** 获取用户信息* @return*/public User getUser() {// 实际中会根据具体的业务场景,从数据库中查询对应的信息return new User(1L, "倪升武", "123456");}
}

然后写一个监听器,实现 ApplicationListener<ContextRefreshedEvent> 接口,重写 onApplicationEvent 方法,将 ContextRefreshedEvent 对象传进去。如果我们想在加载或刷新应用上下文时,也重新刷新下我们预加载的资源,就可以通过监听 ContextRefreshedEvent 来做这样的事情。代码如下:

/*** 使用ApplicationListener来初始化一些数据到application域中的监听器* @author shengni ni* @date 2018/07/05*/
@Component
public class MyServletContextListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {// 先获取到application上下文ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();// 获取对应的serviceUserService userService = applicationContext.getBean(UserService.class);User user = userService.getUser();// 获取application域对象,将查到的信息放到application域中ServletContext application = applicationContext.getBean(ServletContext.class);application.setAttribute("user", user);}
}

正如注释中描述的那样,首先通过 contextRefreshedEvent 来获取 Application 上下文,再通过 Application 上下文获取 UserService 这个 Bean,项目中可以根据实际业务场景,也可以获取其他的 Bean,然后再调用自己的业务代码获取相应的数据,最后存储到 Application 域中,这样前端在请求相应数据的时候,我们就可以直接从 Application 域中获取信息,减少数据库的压力。下面写一个 Controller 直接从 Application 域中获取 user 信息来测试一下。

@RestController
@RequestMapping("/listener")
public class TestController {@GetMapping("/user")public User getUser(HttpServletRequest request) {ServletContext application = request.getServletContext();return (User) application.getAttribute("user");}
}

不过 Application 是缓存在内存中,对内存会有消耗,Redis更加适合操作。


************************************************************************************************************************************************


监听 HTTP 会话 Session 对象

监听器还有一个比较常用的地方,就是用来监听 Session 对象,以获取在线用户数量。现在很多开发者都有自己的网站,监听 Session 来获取当前眼下用户数量是个很常见的使用场景。下面介绍下如何使用。

/*** 使用HttpSessionListener统计在线用户数的监听器* @author shengwu ni* @date 2018/07/05*/
@Component
public class MyHttpSessionListener implements HttpSessionListener {private static final Logger logger = LoggerFactory.getLogger(MyHttpSessionListener.class);/*** 记录在线的用户数量*/public Integer count = 0;@Overridepublic synchronized void sessionCreated(HttpSessionEvent httpSessionEvent) {logger.info("新用户上线了");count++;httpSessionEvent.getSession().getServletContext().setAttribute("count", count);}@Overridepublic synchronized void sessionDestroyed(HttpSessionEvent httpSessionEvent) {logger.info("用户下线了");count--;httpSessionEvent.getSession().getServletContext().setAttribute("count", count);}
}

可以看出,首先该监听器需要实现 HttpSessionListener 接口,然后重写 sessionCreated 和 sessionDestroyed 方法,在 sessionCreated 方法中传递一个 HttpSessionEvent 对象,之后将当前 Session 中的用户数量加1,sessionDestroyed 方法刚好相反,不再赘述。接下来,我们写一个 Controller 测试一下。

@RestController
@RequestMapping("/listener")
public class TestController {/*** 获取当前在线人数,该方法有bug* @param request* @return*/@GetMapping("/total")public String getTotalUser(HttpServletRequest request) {Integer count = (Integer) request.getSession().getServletContext().getAttribute("count");return "当前在线人数:" + count;}
}

但是如果关闭一个浏览器再打开,理论上应该还是2,但是实际测试却是 3。原因是 Session 销毁的方法没有执行(可以在后台控制台观察日志打印情况),当重新打开时,服务器找不到用户原来的 Session,于是又重新创建了一个 Session,那怎么解决该问题呢?我们可以将上面的 Controller 方法改造一下:

@GetMapping("/total2")
public String getTotalUser(HttpServletRequest request, HttpServletResponse response) {Cookie cookie;try {// 把sessionId记录在浏览器中cookie = new Cookie("JSESSIONID", URLEncoder.encode(request.getSession().getId(), "utf-8"));cookie.setPath("/");//设置cookie有效期为2天,设置长一点cookie.setMaxAge( 48*60 * 60);response.addCookie(cookie);} catch (UnsupportedEncodingException e) {e.printStackTrace();}Integer count = (Integer) request.getSession().getServletContext().getAttribute("count");return "当前在线人数:" + count;
}

可以看出,该处理逻辑是让服务器记得原来那个 Session,即把原来的 sessionId 记录在浏览器中,下次再打开时,把这个 sessionId 传过去,这样服务器就不会重新再创建了。重启一下服务器,在浏览器中再次测试一下,即可避免上面的问题。


************************************************************************************************************************************************


监听客户端请求 Servlet Request 对象

使用监听器获取用户的访问信息比较简单,实现 ServletRequestListener 接口即可,然后通过 Request 对象获取一些信息。代码如下:

/*** 使用ServletRequestListener获取访问信息* @author shengwu ni* @date 2018/07/05*/
@Component
public class MyServletRequestListener implements ServletRequestListener {private static final Logger logger = LoggerFactory.getLogger(MyServletRequestListener.class);@Overridepublic void requestInitialized(ServletRequestEvent servletRequestEvent) {HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();logger.info("session id为:{}", request.getRequestedSessionId());logger.info("request url为:{}", request.getRequestURL());request.setAttribute("name", "倪升武");}@Overridepublic void requestDestroyed(ServletRequestEvent servletRequestEvent) {logger.info("request end");HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();logger.info("request域中保存的name值为:{}", request.getAttribute("name"));}}

这个比较简单,不再赘述,接下来写一个 Controller 测试一下即可。

@GetMapping("/request")
public String getRequestInfo(HttpServletRequest request) {System.out.println("requestListener中的初始化的name数据:" + request.getAttribute("name"));return "success";
}

************************************************************************************************************************************************


Spring Boot 中自定义事件监听

在实际项目中,我们往往需要自定义一些事件和监听器来满足业务场景,比如在微服务中会有这样的场景:微服务 A 在处理完某个逻辑之后,需要通知微服务 B 去处理另一个逻辑,或者微服务 A 处理完某个逻辑之后,需要将数据同步到微服务 B。这种场景非常普遍,这时我们可以自定义事件以及监听器来监听,一旦监听到微服务 A 中的某事件发生,就去通知微服务 B 处理对应的逻辑。

自定义事件

自定义事件需要继承 ApplicationEvent 对象,在事件中定义一个 User 对象来模拟数据,构造方法中将 User 对象传进来初始化。如下:

/*** 自定义事件* @author shengwu ni* @date 2018/07/05*/
public class MyEvent extends ApplicationEvent {private User user;public MyEvent(Object source, User user) {super(source);this.user = user;}// 省去get、set方法
}

自定义监听器

接下来,自定义一个监听器来监听上面定义的 MyEvent 事件,自定义监听器实现 ApplicationListener 接口即可。如下:

/*** 自定义监听器,监听MyEvent事件* @author shengwu ni* @date 2018/07/05*/
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent myEvent) {// 把事件中的信息获取到User user = myEvent.getUser();// 处理事件,实际项目中可以通知别的微服务或者处理其他逻辑等等System.out.println("用户名:" + user.getUsername());System.out.println("密码:" + user.getPassword());}
}

然后重写 onApplicationEvent 方法,将自定义的 MyEvent 事件传进来,因为该事件中,我们定义了 User 对象(该对象在实际中就是需要处理的数据,在下文来模拟),然后就可以使用该对象的信息了。

OK,定义好了事件和监听器之后,需要手动发布事件,这样监听器才能监听到,这需要根据实际业务场景来触发,针对本文的例子,我写个触发逻辑,如下:

/*** UserService* @author shengwu ni*/
@Service
public class UserService {@Resourceprivate ApplicationContext applicationContext;/*** 发布事件* @return*/public User getUser2() {User user = new User(1L, "倪升武", "123456");// 发布事件MyEvent event = new MyEvent(this, user);applicationContext.publishEvent(event);return user;}
}

在 Service 中注入 ApplicationContext,在业务代码处理完之后,通过 ApplicationContext 对象手动发布 MyEvent 事件,这样我们自定义的监听器就能监听到,然后处理监听器中写好的业务逻辑。

最后,在 Controller 中写一个接口来测试一下:

@GetMapping("/request")
public String getRequestInfo(HttpServletRequest request) {System.out.println("requestListener中的初始化的name数据:" + request.getAttribute("name"));return "success";
}

总结:

系统地介绍了监听器原理,以及在 Spring Boot 中如何使用监听器,列举了监听器的三个常用案例,有很好的实战意义。最后讲解了项目中如何自定义事件和监听器,并结合微服务中常见的场景,给出具体的代码模型,均能运用到实际项目中去。

springboot细节挖掘(监听器)相关推荐

  1. springboot细节挖掘(集成ElasticSearch)

    ElasticSearch下载 官网地址:https://www.elastic.co/cn/downloads/elasticsearch DEMO参考地址:https://github.com/s ...

  2. springboot细节挖掘(配置Swagger2)

    首先启动一个springboot的项目: 配置pow.xml,在maven里面添加依赖 <!--springboot之swaager的配置 start--><dependency&g ...

  3. springboot细节挖掘(jar和war打包)

    打包运行 springboot下面分别构建俩种包的构建演示 war包配置: <packaging>war</packaging> <build> <final ...

  4. springboot细节挖掘(对测试的支持)

    Spring Boot 提供了专门支持测试的组件 Spring Boot Test,其集成了业内流行的 7 种强大的测试框架: JUnit,一个 Java 语言的单元测试框架: Spring Test ...

  5. springboot细节挖掘(知识积累)

    转载地址:https://blog.csdn.net/HQZ820844012/article/details/80400058#spring-顶级框架

  6. springboot细节挖掘(日志系统)

    注意: 1.Spring Boot 1.3.x和以下版本支持log4j的日志输出 2.Spring Boot 1.3.x以上版本只支持log4j2,logback的日志输出 Slf4j+logback ...

  7. Springboot细节挖掘(对web的支持之数据校验)

    数据校验: 输入验证是最重要的 Web 开发任务之一,在 Spring MVC 中有两种方式可以验证输入:一种是 Spring 自带的验证框架,另外一种是利用 JSR 实现. JSR 是一个规范文档, ...

  8. springboot细节挖掘(数据初始化)

    如何加载一些启动就需要的初始化数据呢? CommandLineRunner spring Boot 为我们提供了一个方法,通过实现接口 CommandLineRunner 来实现 定义初始化类 MyC ...

  9. springboot的细节挖掘(ActiveMq集成)

    官网下载地址: http://activemq.apache.org/?utm_source=csdn_toolbar 下载的时候区别:ActiveMQ 5 "Classic"和A ...

最新文章

  1. 打算看的书或正在看的书
  2. 计算机在外语专业中有哪些应用,CALL(3):计算机在外语教学中的应用
  3. 02.Teams组成概述及使用分享
  4. 全球首个!腾讯优图开源3D医疗影像大数据预训练模型
  5. 实用的CSS3属性和使用技巧
  6. 4-google translate插件安装及使用
  7. 如何在阿里云注册域名-阿里云域名注册与域名解析完整教程
  8. Excel之分类汇总,定位,组合
  9. warning: array subscript is above array bounds
  10. 抖音很火的小程序表白html,最近抖音很火的表白小程序写法C#版
  11. iphone一键转移_iPhone 12换机首选,QQ同步助手一键智能备份迁移
  12. DDPG中的Ornstein-Uhlenbeck过程怎么理解
  13. 【VMware】VMware 虚拟机使用笔记
  14. django 调用数据库图片的路径并在html显示
  15. 道路曲线线路坐标计算 ∈ C# 编程笔记
  16. java公共自行车租赁系统ssh
  17. 关于概率分布理论的原理分析的一些讨论,以及经典概率分布的应用场景,以及概率统计其在工程实践中的应用...
  18. python如何进入编程界面_Python可视化界面编程入门
  19. 人体发病的“红灯”信号
  20. matlab逐差法处理数据非线性,逐差法使用条件(逐差法处理数据的条件)

热门文章

  1. 团队编程项目作业1-成员简介及分工
  2. bootstrap datetimepicker 复选可删除,可规定指定日期不可选
  3. 桶排序Bucket sort(转)
  4. fullpage.js(cndjs)
  5. 大型网站采用的具有稳定性的系统构架
  6. 配置tomcat用户
  7. Spring mvc介绍
  8. android 颜色反转 api,来自Android camera2 API的图像数据在Galaxy S5上翻转和压缩
  9. 在优图网,临摹借鉴设计大咖作品|品图标设计:主要趋势
  10. 彰显城市等级的最典型代表,商业综合体城市PSD海报素材