目录
2、自定义监听器:TestEventListener.java
2.3 监听客户端请求 Servlet Request 对象
监听器
监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。
Servlet 监听器:Servlet 规范中定义的一种特殊类,它用于监听 web 应用程序中的 ServletContext,HttpSession 和 ServletRequest 等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。
创建时机:
ServletContext:
创建时间:web应用被加载(即服务器启动或发布web应用)时;
销毁时间:web应用被卸载(即服务器关闭或者该应用被移除)时;
HttpSession :
事实上HttpSession是直到服务器端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建。
当客户端浏览器第一次访问服务器时,服务器为每个浏览器创建不同的HttpSession对象。
需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session,可调用request.getSession(true)强制生成Session。
ServletRequest:
当客户请求到来时,容器创建一个ServletRequest对象,封装请求数据,同时创建一个ServletResponse对象,封装响应数据。
Servlet规范针对这三个对象上的操作,又把这多种类型的监听器划分为三种类型。
- 监听三个域对象创建和销毁的事件监听器
- 监听域对象中属性的增加和删除的事件监听器
- 监听绑定到 HttpSession 域中的某个对象的状态的事件监听器。
Servlet 监听器
(1)Servlet 规范为每种事件监听器都定义了相应的接口,开发人员编写的事件监听器程序只需实现这些接口,web 服务器根据用户编写的事件监听器所实现的接口把它注册到相应的被监听对象上。
(2)一些 Servlet 事件监听器需要在 web 应用程序的 web.xml 文件中进行注册,一个 web.xml 文件中可以注册多个 Servlet 事件监听器,web 服务器按照它们在 web.xml 文件中的注册顺序来加载和注册这些 Serlvet 事件监听器。
(3)Serlvet 事件监听器的注册和调用过程都是由 web 容器自动完成的,当发生被监听的对象被创建,修改或销毁事件时,web容器将调用与之相关的 Servlet 事件监听器对象的相关方法,开发人员在在这些方法中编写的事件处理代码即被执行。
(4)由于一个 web 应用程序只会为每个事件监听器创建一个对象,有可能出现多个线程同时调用同一个事件监听器对象的情况,所以,在编写事件监听器类时,应考虑多线程安全的问题。
自定义监听器
自定义监听创建步骤:
自定义事件:编写一个事件,只需要继承ApplicationEvent即可
自定义监听器:自定义监听器实现 ApplicationListener接口,把自定义的事件作为参数,重写onApplicationEvent方法即可
触发器:注入ApplicationContext,调用publishEvent发布自定义事件即可
调用触发器:简单了,就是写个controller类,调用触发器方法即可
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;
}
}
2、自定义监听器:TestEventListener.java
自定义监听器实现 ApplicationListener
接口即可,将自定义的 TestEvent
事件传进来。
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、编写触发器
定义好了事件和监听器之后,需要手动发布事件,创建TestService。以下实现的目的是,在创建某一个对象的时候,发布一个监听事件,以便在(4、测试)中调用getPerson时触发监听器中的方法:(这段为我的理解,如有错误之处请大家指正)
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
事件,这样我们自定义的监听器就能监听到,然后处理监听器中写好的业务逻辑。
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
注意观察控制台日志:
Spring Boot2.x 监听器和使用场景
1. 什么是 Web 监听器?
Web 监听器是一种 Servlet
特殊类,它们能帮助开发者监听 Web 中特定的事件,比如 ServletContext、HttpSession 、ServletRequest
的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控。
2 .常用Web监听
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;
}
}
测试:
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"));
}
}
测试:
访问:http://localhost:8080/api/total
注意观察控制台日志:
从控制台日志中,可以看出在访问一个请求时,在请求前后,都执行了相关日志,说明监听生效了。