1. 什么是 Web 监听器?
Web 监听器是一种 Servlet 特殊类,它们能帮助开发者监听 Web 中特定的事件,比如 ServletContext、HttpSession 、ServletRequest 的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控。
2. Spring Boot 中监听器的使用场景
使用场景:比如监听 Servlet 上下文用来初始化一些数据、监听 HTTP Session 用来获取当前在线的人数、监听客户端请求的ServletRequest 对象来获取用户的访问信息等等。
下面我们通过这三个实际的使用场景来学习一下 Spring Boot 中监听器的使用。
2.1 监听 Servlet 上下文对象
2.1.1 监听 Servlet 上下文对象可以用来初始化数据,用于缓存。什么意思呢?
场景举例:缓存首页数据
比如用户在点击某个站点的首页时,一般都会展现出首页的一些信息,而这些信息基本上或者大部分时间都保持不变,但这些信息都是来自数据库。如果用户的每次点击,都要从数据库中去获取数据的话,用户量少还可以接受,如果用户量非常大的话,这对数据库也是一笔很大的开销。针对这种首页数据,如果大部分都不常更新的话,我们完全可以把它们缓存起来,每次用户点击的时候,我们都直接从缓存中拿,这样既可以提高首页的访问速度,又可以降低服务器的压力。如果做得更加灵活一点,可以再加个定时器,定期的来更新这个首页缓存。就类似与 CSDN 个人博客首页中排名的变化一样。
2.1.2 创建监听类MyServletContextListener.java
监听类需要实现ApplicationListener 接口,并实现onApplicationEvent 方法,参数是ContextRefreshedEvent
onApplicationEvent 方法 :在容器初始化完成之后执行。
ContextRefreshedEvent :上下文刷新事件。
代码如下:
package com.ieslab.powergrid.demosvr.utils;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class MyServletContextListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
System.out.println("Spring容器加载完成触发,可用于初始化环境,准备测试数据、加载一些数据到内存");
//此处可以开启一个线程,用于查询首页数据,并缓存在内容中,供所有的首页进行查询
//代码省略
}
}
从代码我们可以看出,这个监听类主要是在Spring 容器启动之后,自动触发此监听器的onApplicationEvent 方法,并执行方法中的内容。这时候我们就想到了,可以在这里吧我们需要处理的业务在此处进行启动,一般用线程启动的较多。同时吧线程的处理结果进行缓存到内存中,或者分布式缓存redis 中。
这样就可以在前端获取数据时,直接从内容中获取即可,加快访问速度。
2.2 监听 HTTP 会话 Session 对象
监听器还有一个比较常用的地方,就是用来监听 Session 对象,以获取在线用户数量。
2.2.1 先创建session监听类:MyHttpSessionListener.java
package com.ieslab.powergrid.demosvr.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@Component
@Slf4j
public class MyHttpSessionListener implements HttpSessionListener {
public static Integer count = 0; //记录在线的用户数量
@Override
public synchronized void sessionCreated(HttpSessionEvent httpSessionEvent) {
log.info("新用户上线了");
count++;
httpSessionEvent.getSession().getServletContext().setAttribute("count", count);
}
@Override
public synchronized void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
log.info("用户下线了");
count--;
httpSessionEvent.getSession().getServletContext().setAttribute("count", count);
}
}
从代码可以看出,该监听器首先需要实现 HttpSessionListener 接口,然后重写 sessionCreated 创建方法和 sessionDestroyed 销毁方法,在 sessionCreated 方法中传递一个HttpSessionEvent 对象,之后将当前 Session 中的用户数量加 1,sessionDestroyed 方法刚好相反。 此处还用了同步(synchronized )的方法,避免数量增减的时候错乱。
2.2.2 我们接下来测试一下
首先创建TestController.java ,并编写一个访问方法,返回当前用户在线个数信息。
@Controller
@RequestMapping("/api")
public class TestController {
/**
* 获取当前在线人数
* @param request
* @return 当前人数信息
*/
@GetMapping("/total")
@ResponseBody
public String getTotalUser(HttpServletRequest request) {
Integer count = (Integer) request.getSession().getServletContext().getAttribute("count");
return "当前在线人数:" + count;
}
}
访问:http://localhost:8080/api/total 效果如下:
控制台中打印:新用户上线了
2.3 监听客户端请求 Servlet Request 对象
2.3.1 创建MyServletRequestListener类
实现request 访问前后的方法,直接上代码:
package com.ieslab.powergrid.demosvr.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
@Component
@Slf4j
public class MyServletRequestListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent servletRequestEvent) {
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
log.info("session id为:{}", request.getRequestedSessionId());
log.info("request url为:{}", request.getRequestURL());
request.setAttribute("name", "斌哥");
}
@Override
public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
log.info("request end");
HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
log.info("request域中保存的name值为:{}", request.getAttribute("name"));
}
}
2.3.2 测试一下
访问:http://localhost:8080/api/total 注意观察控制台日志:
从控制台日志中,可以看出在访问一个请求时,在请求前后,都执行了相关日志,说明监听生效了。
3. 自定义监听器
3.1 自定义事件:TestEvent.java
编写一个事件,只需要继承ApplicationEvent即可
package com.ieslab.powergrid.demosvr.entity;
import org.springframework.context.ApplicationEvent;
public class TestEvent extends ApplicationEvent {
private Person person;
public TestEvent(Object source, Person person) {
super(source);
this.person = person;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
3.2 自定义监听器:TestEventListener.java
自定义监听器实现 ApplicationListener 接口即可
package com.ieslab.powergrid.demosvr.utils;
import com.ieslab.powergrid.demosvr.entity.Person;
import com.ieslab.powergrid.demosvr.entity.TestEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class TestEventListener implements ApplicationListener<TestEvent> {
@Override
public void onApplicationEvent(TestEvent testEvent) {
// 把事件中的信息获取到
Person person = testEvent.getPerson();
// 处理事件,实际项目中可以通知别的微服务或者处理其他逻辑等
log.info("用户名:" + person.getFirstName());
log.info("城市:" + person.getCity());
}
}
然后重写onApplicationEvent 方法,将自定义的 TestEvent 事件传进来。
3.3 编写触发器
定义好了事件和监听器之后,需要手动发布事件,创建TestService
package com.ieslab.powergrid.demosvr.service;
import com.ieslab.powergrid.demosvr.entity.Person;
import com.ieslab.powergrid.demosvr.entity.TestEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
//发布事件触发器
@Service
@Slf4j
public class TestService {
@Resource
private ApplicationContext applicationContext;
/**
* 发布事件
* @return
*/
public Person getPerson() {
Person person = new Person("", "斌哥","","济南");
// 发布事件
TestEvent event = new TestEvent(this, person);
applicationContext.publishEvent(event);
log.info("触发器被触发");
return person;
}
}
在 Service 中注入 ApplicationContext ,在业务代码处理完之后,通过ApplicationContext 对象手动发布 TestEvent 事件,这样我们自定义的监听器就能监听到,然后处理监听器中写好的业务逻辑。
3.4 测试
在TestController.java类中添加对触发器方法的访问。
@Controller
@RequestMapping("/api")
@Api(tags="RequestParam用法测试接口类")
public class TestController {
@Autowired
TestService testService;
/**
* 测试自定义监听器入口方法
* @return 当前人数信息
*/
@GetMapping("/testEvent")
@ResponseBody
public Person testEvent() {
return testService.getPerson();
}
...
访问:http://localhost:8080/api/testEvent 注意观察控制台日志:
3.5 总结
自定义监听创建步骤:
- 自定义事件:编写一个事件,只需要继承
ApplicationEvent 即可
- 自定义监听器:自定义监听器实现
ApplicationListener 接口,把自定义的事件作为参数,重写onApplicationEvent 方法即可
- 触发器:注入
ApplicationContext ,调用publishEvent 发布自定义事件即可
- 调用触发器:简单了,就是写个
controller 类,调用触发器方法即可
4.总结
本章介绍监听的集中应用场景,基于servlet的、session的、request请求的,最后又介绍了如何去创建自定义的监听,并创建了一个场景进行讲解。
关于自定义监听的应用场景,可以根据自己的业务环境,进行灵活应用。
|