分享

Spring Boot2.x:监听器使用及场景介绍

 anio17 2020-04-21

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 总结

自定义监听创建步骤:

  1. 自定义事件:编写一个事件,只需要继承ApplicationEvent即可
  2. 自定义监听器:自定义监听器实现 ApplicationListener接口,把自定义的事件作为参数,重写onApplicationEvent方法即可
  3. 触发器:注入ApplicationContext,调用publishEvent发布自定义事件即可
  4. 调用触发器:简单了,就是写个controller类,调用触发器方法即可

4.总结

本章介绍监听的集中应用场景,基于servlet的、session的、request请求的,最后又介绍了如何去创建自定义的监听,并创建了一个场景进行讲解。
关于自定义监听的应用场景,可以根据自己的业务环境,进行灵活应用。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多