Spring 监听器
springboot源码分析(四)-监听器实现原理(上)
springboot源码分析(四)-监听器实现原理(上)开篇之前先把祖师爷搬出来 费⽟清:问⼤家⼀个脑筋急转弯,⼥⼈⽣孩⼦,⽤⼀个成语来形容这⼀现象 思考。
思考。
思考。
揭晓谜底: 反正谜底我已经揭晓了,⾄于⼤家能不能看到,我就不管了,哈哈概述 监听器,在很多的源码中都⾮常的常见,之后我会写⼀个nacos源码分析的⽂章,那⾥⾯也⽤了很多的监听器,为什么要使⽤监听器,举个例⼦,⼤家在过红绿灯的时候,每⼀个司机其实就是⼀个观察者,那观察的⽬标是什么呢?观察的⽬标就是红绿灯,那这个过程中会产⽣什么事件呢?就是红灯,黄灯,绿灯的事件,当司机收到这些事件之后会做出不同的举动。
那如果不使⽤这种模式,就是我不⽤红绿灯,我直接找个交警去⼀个司机⼀个司机通知,告诉每个司机,你可以⾛了,你不能⾛,⽐较⼀下⼤家就可以发现第⼀种⽅式效率更⾼。
其实上⾯的红绿灯的场景就是⼀个典型的监听器模式,是23中设计模式中的⼀种,要想搞明⽩springboot中的监听器原理,就要先搞清楚监听器模式原理,所以本篇⽂章就先介绍设计模式之-监听器模式。
监听器模式核⼼组成部分⽬标:这个很难解释,我就把⽐较官⽅的话贴出来吧,⼤家看完例⼦之后回头看看官⽅的定义,⽬标被观察的对象,在⽬标中定义了⼀个观察者集合,提供⼀系列⽅法可以增加或者删除观察者,同时定义了通知观察者的⽅法,⽬标类可以是接⼝或者抽象类或者具体类。
具体⽬标:是⽬标类的字类Observer:观察者,对观察⽬标的改变做出反应,观察者⼀般定义为接⼝ConcreteObserver:具体观察者,和具体的⽬标状态绑定看这些概念很头⼤,下⾯看⼀个具体的例⼦观察者package com.example.demo.event;/*** @author steve* @date 2020/6/3 3:02 下午*/public interface WeekendListener {public void onEvent(WeekendEvent event);}具体观察者public class SleepListener implements WeekendListener{@Overridepublic void onEvent(WeekendEvent event) {if (event instanceof SleepEvent){System.out.println("hello,"+event.getName());}}}具体观察者public class ShoppingListener implements WeekendListener{@Overridepublic void onEvent(WeekendEvent event) {if (event instanceof ShoppingEvent){System.out.println("hello, "+event.getName());}}}事件public class ShoppingEvent implements WeekendEvent{@Overridepublic String getName() {return "shopping";}}事件public class SleepEvent implements WeekendEvent{@Overridepublic String getName() {return "sleep";}}⽬标public abstract class AbstractEventtMulticaster {public List<WeekendListener> listeners ;public AbstractEventtMulticaster(List<WeekendListener> listeners){this.listeners = new ArrayList<>(listeners);}public void multicaster(WeekendEvent event){doStart();listeners.stream().forEach(l -> l.onEvent(event));doEnd();}public void addListener(WeekendListener listener){listeners.add(listener);}public void removeListener(WeekendListener listener){listeners.remove(listener);}abstract public void doStart();abstract public void doEnd();}具体⽬标public class SimpleEventMulticaster extends AbstractEventtMulticaster{ public SimpleEventMulticaster(List<WeekendListener> listeners) {super(listeners);}@Overridepublic void doStart() {System.out.println("start 搞事情");}@Overridepublic void doEnd() {System.out.println("end 搞事情");}}测试public class Test {public static void main(String[] args) {ShoppingListener shoppingListener = new ShoppingListener();SleepListener sleepListener = new SleepListener();SimpleEventMulticaster simpleEventMulticaster = new SimpleEventMulticaster(new ArrayList<WeekendListener>());simpleEventMulticaster.addListener(shoppingListener);simpleEventMulticaster.addListener(sleepListener);simpleEventMulticaster.multicaster(new ShoppingEvent());simpleEventMulticaster.removeListener(shoppingListener);simpleEventMulticaster.multicaster(new ShoppingEvent());}}输出结果:start 搞事情hello, shoppingend 搞事情start 搞事情end 搞事情解释:这⾥定义了观察者WeekendListener,这个观察者的作⽤就是观察周末到底要⼲啥,然后定义了两个具体观察者,就是ShoppingListener和SleepListener,这两个是具体观察者,观察这个⼈是睡觉还是去购物,然后就定义了两种⾏为,也就是两个事件SleepEvent和ShoppingEvent,这两个事件就是具体的状态,然后定义了⽬标就是AbstractEventtMulticaster,这个⾥⾯定义了观察者的集合,定义了新增和删除观察者,同时还定义了⼀个关键的⽅法multicaster,这个⽅法会在出现具体的事件的时候,将事件⼴播给每⼀个观察者,然后执⾏回调onEvent()⽅法,其实在具体观察者中可以定义interest()⽅法,这个⽅法的作⽤就是看这个具体的观察者是不是对这个事件感兴趣,如果感兴趣就可以执⾏具体某个⾏为。
8种监听器分别是:
8种监听器分别是:
1.监听器⽤来监听web程序中的事件,例如创建、修改、删除session、request、context等。
想要实现监听器的功能需要实现监听器的接⼝xxxLinetener,当Tomcat触发监听器的时候,会⾃动调⽤。
监听器可以分为四种类型:
1.监听对象的创建:
ServletContext:主要监听servletContext的创建,需要实现ServeltContextListener接⼝。
ServletRequest:主要监听request的创建,需要实现ServletRequestListener接⼝
HttpSession:主要监听session的创建,需要实现HttpSessionListener接⼝
2.监听属性的改变:
ServletContext:主要监听servletContext属性的更改、添加、删除,需要实现ServeltContextAttrbuteListener接⼝。
ServletRequest:主要监听request属性的更改、添加、删除,需要实现ServletRequestAttrbuteListener接⼝
HttpSession:主要监听session属性的更改、添加、删除,需要实现HttpSessionAttrbuteListener接⼝
3.监听session的活化与钝化:
httpSessionActivationListener主要监听了session的活化与钝化
4.监听session与对象的绑定:
httpSessionBindingListener监听了session与对象的绑定。
applicationlistener原理
applicationlistener原理ApplicationListener是Spring框架中的一个重要接口,用于监听Spring容器中发布的事件。
它提供了一种解耦的机制,使得应用程序的各个模块可以通过监听事件的方式进行通信,而不需要显式地依赖其他模块。
在Spring中,事件的发布和监听是通过ApplicationEventPublisher和ApplicationEventMulticaster来实现的。
ApplicationEventPublisher是Spring容器中的一个接口,用于发布事件;而ApplicationEventMulticaster是一个事件广播器,负责将事件分发给相应的监听器。
ApplicationListener接口继承了EventListener接口,它定义了一个onApplicationEvent方法,用于处理事件。
当一个事件被发布时,ApplicationEventMulticaster会遍历所有注册的监听器,并调用它们的onApplicationEvent方法。
因此,如果一个类实现了ApplicationListener接口,并注册为监听器,那么它就能够接收到相应的事件,并执行相应的逻辑。
在Spring中,事件的发布通常是通过ApplicationContext来完成的。
ApplicationContext是Spring容器的核心接口,它继承了ApplicationEventPublisher接口,因此可以通过调用publishEvent方法来发布事件。
当一个事件被发布时,ApplicationContext会将事件传递给ApplicationEventMulticaster,并由它来负责分发给相应的监听器。
在实际应用中,ApplicationListener常用于处理一些系统级的事件,比如应用启动时的事件、应用关闭时的事件等。
通过监听这些事件,我们可以实现一些初始化操作、资源释放等功能。
spring actuator原理
spring actuator原理Spring Actuator是一个功能强大的管理和监控应用程序端点,可以用来提供对Spring Boot应用程序运行情况的状态视图。
Spring Actuator有助于提供信息,可以成为针对应用程序的管理、监控以及调整的依据。
Spring Actuator使用HTTP和JMX来暴露一组端点,这些端点支持管理程序,如诊断、健康、配置和安全检查,以及在运行时可以被修改的参数。
此外,它还可以提供来自应用程序内部的运行时信息,例如JVM、类路径和内存信息。
Spring Actuator工作原理如下:1. Spring Boot应用程序启动时,Spring Actuator会自动注册一组特定的端点,这些端点用于收集有关Spring Boot应用程序的运行状态信息,以及提供相应的应用程序管理功能。
2. 使用Spring Actuator可以查看应用程序的状态信息,并使用HTTP 或JMX来访问应用程序的端点信息。
3. 当应用程序有变更时,Spring Actuator会收集有关变更的信息,并将其发布到HTTP或JMX,以保持应用程序的最新状态和管理控制情况。
4. Spring Actuator端点实现了可扩展性,可以让开发人员根据自己的需要添加新端点,或修改现有端点的行为。
5. 要使用Spring Actuator,只需在应用程序根目录中添加一个“actuator”依赖,然后就可以使用Spring Actuator的功能了。
Spring Actuator旨在为开发人员提供一个简单的、可扩展的工具,以便更好地了解和管理他们的应用程序。
它的核心功能在于向开发人员提供运行时应用程序的状态视图,以及支持管理程序的端点,如健康检查、诊断等,这有助于在良好的运行状态下快速部署应用程序。
nacos配置中心热更新原理
nacos配置中心热更新原理随着云计算和微服务的发展,配置中心成为了一个重要的基础设施。
在微服务架构中,服务之间的通信和协调往往需要大量的配置信息,这些信息需要在不同的服务之间共享和管理。
Nacos作为一款开源的配置中心,提供了一系列的功能,包括服务发现、配置管理、流量管理等,被广泛应用于云原生应用开发中。
在使用Nacos配置中心时,常常需要更新配置信息。
由于配置信息的变化可能会影响到应用程序的行为,因此需要确保配置信息的变化能够及时生效,而不需要重启应用程序。
这就需要使用到热更新技术。
本文将介绍Nacos配置中心热更新的原理和实现方式。
一、热更新的概念和原理热更新是指在应用程序运行时更新程序的代码或配置信息,而不需要停止或重启应用程序。
热更新可以提高应用程序的可用性和稳定性,同时也可以提高开发和部署的效率。
热更新的实现方式有多种,包括Java的热部署、Spring的热加载、Nginx的热重载等。
热更新的原理是通过动态加载和卸载资源来实现的。
在Java中,可以使用ClassLoader来实现动态加载和卸载类。
ClassLoader是Java虚拟机的一个重要组成部分,它负责加载Java类文件并将其转换成Java类对象。
ClassLoader可以根据需要从不同的来源加载类文件,例如从本地磁盘、网络、数据库等。
在热更新中,ClassLoader 可以动态加载和卸载类文件,从而实现热部署的效果。
二、Nacos配置中心的热更新实现方式在Nacos配置中心中,热更新主要是指动态更新配置信息。
Nacos 配置中心支持多种方式更新配置信息,包括手动更新、定时更新、监听器更新等。
其中,监听器更新是实现热更新的核心方式。
1. 手动更新手动更新是指通过Nacos控制台或API手动修改配置信息。
手动更新的优点是操作简单,可以快速更新配置信息。
但是手动更新的缺点是需要手动操作,容易出错,而且不适合大规模的部署。
2. 定时更新定时更新是指通过定时任务定期更新配置信息。
SpringBoot如何监控Redis中某个Key的变化(自定义监听器)
SpringBoot如何监控Redis中某个Key的变化(⾃定义监听器)⽬录SpringBoot 监控Redis中某个Key的变化1.声明2.基本理念3.实现和创建监听4.基本demo的其他配置5.基本测试6.⼩结⼀下SpringBoot⾃定义监听器原理⽰例SpringBoot 监控Redis中某个Key的变化1.声明当前内容主要为本⼈学习和基本测试,主要为监控redis中的某个key的变化(感觉⽹上的都不好,所以⾃⼰看Spring源码直接写⼀个监听器)个⼈参考:Redis官⽅⽂档Spring-data-Redis源码2.基本理念⽹上的demo的缺点使⽤继承KeyExpirationEventMessageListener只能监听当前key消失的事件使⽤KeyspaceEventMessageListener只能监听所有的key事件总体来说,不能监听某个特定的key的变化(某个特定的redis数据库),具有缺陷直接分析获取可以操作的步骤查看KeyspaceEventMessageListener的源码解决问题基本思想创建⾃⼰的主题(⽤来监听某个特定的key)创建监听器实现MessageListener注⼊⾃⼰的配置信息查看其中的⽅法(init⽅法)public void init() {if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {RedisConnection connection = listenerContainer.getConnectionFactory().getConnection();try {Properties config = connection.getConfig("notify-keyspace-events");if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);}} finally {connection.close();}}doRegister(listenerContainer);}/*** Register instance within the container.** @param container never {@literal null}.*/protected void doRegister(RedisMessageListenerContainer container) {listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS);}主要操作如下向redis中写⼊配置notify-keyspace-events并设置为EA向RedisMessageListenerContainer中添加本⾝这个监听器并指定监听主题所以本⼈缺少的就是这个主题表达式和监听的notify-keyspace-events配置直接来到redis的官⽅⽂档找到如下内容所以直接选择的是:__keyspace@0__:myKey,使⽤的模式为KEA所有的⼯作全部完毕后开始实现监听3.实现和创建监听创建监听类:RedisKeyChangeListener本类中主要监听redis中数据库0的myKey这个keyimport java.nio.charset.Charset;import java.util.Properties;import org.springframework.data.redis.connection.Message;import org.springframework.data.redis.connection.MessageListener;import org.springframework.data.redis.connection.RedisConnection;import org.springframework.data.redis.listener.KeyspaceEventMessageListener;import org.springframework.data.redis.listener.PatternTopic;import org.springframework.data.redis.listener.RedisMessageListenerContainer;import org.springframework.data.redis.listener.Topic;import org.springframework.util.StringUtils;/**** @author hy* @createTime 2021-05-01 08:53:19* @description 期望是可以监听某个key的变化,⽽不是失效**/public class RedisKeyChangeListener implements MessageListener/* extends KeyspaceEventMessageListener */ {private final String listenerKeyName; // 监听的key的名称private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*"); //表⽰只监听所有的keyprivate static final Topic TOPIC_KEYEVENTS_SET = new PatternTopic("__keyevent@0__:set"); //表⽰只监听所有的keyprivate static final Topic TOPIC_KEYNAMESPACE_NAME = new PatternTopic("__keyspace@0__:myKey"); // 不⽣效// 监控//private static final Topic TOPIC_KEYEVENTS_NAME_SET_USELESS = new PatternTopic("__keyevent@0__:set myKey"); private String keyspaceNotificationsConfigParameter = "KEA";public RedisKeyChangeListener(RedisMessageListenerContainer listenerContainer, String listenerKeyName) {this.listenerKeyName = listenerKeyName;initAndSetRedisConfig(listenerContainer);}public void initAndSetRedisConfig(RedisMessageListenerContainer listenerContainer) {if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {RedisConnection connection = listenerContainer.getConnectionFactory().getConnection();try {Properties config = connection.getConfig("notify-keyspace-events");if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);}} finally {connection.close();}}// 注册消息监听listenerContainer.addMessageListener(this, TOPIC_KEYNAMESPACE_NAME);}@Overridepublic void onMessage(Message message, byte[] pattern) {System.out.println("key发⽣变化===》" + message);byte[] body = message.getBody();String string = new String(body, Charset.forName("utf-8"));System.out.println(string);}}其实就改了⼏个地⽅…4.基本demo的其他配置1.RedisConfig配置类@Configuration@PropertySource(value = "redis.properties")@ConditionalOnClass({ RedisConnectionFactory.class, RedisTemplate.class })public class RedisConfig {@AutowiredRedisProperties redisProperties;/**** @author hy* @createTime 2021-05-01 08:40:59* @description 基本的redisPoolConfig* @return**/private JedisPoolConfig jedisPoolConfig() {JedisPoolConfig config = new JedisPoolConfig();config.setMaxIdle(redisProperties.getMaxIdle());config.setMaxTotal(redisProperties.getMaxTotal());config.setMaxWaitMillis(redisProperties.getMaxWaitMillis());config.setTestOnBorrow(redisProperties.getTestOnBorrow());return config;}/*** @description 创建redis连接⼯⼚*/@SuppressWarnings("deprecation")private JedisConnectionFactory jedisConnectionFactory() {JedisConnectionFactory factory = new JedisConnectionFactory(new JedisShardInfo(redisProperties.getHost(), redisProperties.getPort()));factory.setPassword(redisProperties.getPassword());factory.setTimeout(redisProperties.getTimeout());factory.setPoolConfig(jedisPoolConfig());factory.setUsePool(redisProperties.getUsePool());factory.setDatabase(redisProperties.getDatabase());return factory;}/*** @description 创建RedisTemplate 的操作类*/@Beanpublic StringRedisTemplate getRedisTemplate() {StringRedisTemplate redisTemplate = new StringRedisTemplate();redisTemplate.setConnectionFactory(jedisConnectionFactory());redisTemplate.setEnableTransactionSupport(true);return redisTemplate;}@Beanpublic RedisMessageListenerContainer redisMessageListenerContainer() throws Exception {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(jedisConnectionFactory());return container;}// 创建基本的key监听器/* */@Beanpublic RedisKeyChangeListener redisKeyChangeListener() throws Exception {RedisKeyChangeListener listener = new RedisKeyChangeListener(redisMessageListenerContainer(),""); return listener;}}其中最重要的就是RedisMessageListenerContainer 和RedisKeyChangeListener2.另外的RedisProperties类,加载redis.properties⽂件成为对象的/**** @author hy* @createTime 2021-05-01 08:38:26* @description 基本的redis的配置类**/@ConfigurationProperties(prefix = "redis")public class RedisProperties {private String host;private Integer port;private Integer database;private Integer timeout;private String password;private Boolean usePool;private Integer maxTotal;private Integer maxIdle;private Long maxWaitMillis;private Boolean testOnBorrow;private Boolean testWhileIdle;private Integer timeBetweenEvictionRunsMillis;private Integer numTestsPerEvictionRun;// 省略get\set⽅法}省略其他代码5.基本测试创建⼀个key,并修改发现变化可以发现返回的是这个key执⾏的⽅法(set),如果使⽤的是keyevent⽅式那么返回的就是这个key的名称6.⼩结⼀下1.监听redis中的key的变化主要利⽤redis的机制来实现(本⾝就是发布/订阅)2.默认情况下是不开启的,原因有点耗cpu3.实现的时候需要查看redis官⽅⽂档和SpringBoot的源码来解决实际的问题SpringBoot⾃定义监听器原理Listener按照监听的对象的不同可以划分为:监听ServletContext的事件监听器,分别为:ServletContextListener、ServletContextAttributeListener。
SpringBoot中配置ApplicationListener监听器的几种方式
SpringBoot中配置ApplicationListener监听器的⼏种⽅式转载⾃ https:///u013202238/article/details/83215311设置Spring ApplicationListener 的6种⽅式第⼀种⽆法监听org.springframework.boot.context.event.ApplicationStartedEvent第四种,第五种配置⽅式⽆法监听org.springframework.boot.context.event.ApplicationStartedEventorg.springframework.boot.context.event.ApplicationEnvironmentPreparedEventorg.springframework.boot.context.event.ApplicationPreparedEvent1 在application.yml或者在application.properties配置⽂件中通过context.listener.classes配置2 在resources⽬录下新建META-INF⽂件夹并新建spring.factories⽂件通过org.springframework.context.ApplicationListener配置3 在启动main函数中通过SpringApplication配置SpringApplication springApplication = new SpringApplication(null);springApplication.addListeners(你的监听器);4 使⽤@Configuration 注解配置,同时可以配合@Order(-100)设置优先级5 使⽤@EventListener 注解配置在bean中定义任意⽅法并使⽤该注解, 注解属性class中可以指定具体监控的事件类,通过⽅法参数指定事件类型,如果不指定则表⽰监控所有的事件6 通过实现接⼝org.springframework.context.ApplicationContextInitializer,得到context后通过编程式,设置监听器。
Spring中ApplicationContext对事件的支持
Spring中ApplicationContext对事件的支持ApplicationContext具有发布事件的能力。
这是因为该接口继承了ApplicationEventPublisher接口。
Spring 中与事件有关的接口和类主要包括ApplicationEvent、ApplicationListener。
定义一个事件的类需要继承ApplicationEvent或者ApplicationContextEvent抽象类,该抽象类中只有一个构造函数,并且带有一个Object类型的参数作为事件源,并且该事件源不能为null,因此我们需要在自己的构造函数中执行super(Object)。
public class UserEvent extends ApplicationEvent{private String eventContent;public String getEventContent(){return eventContent;}public void setEventContent(String eventContent){this.eventContent = eventContent;}public UserEvent(Object source,String eventContent){super(source);this.eventContent = eventContent;}}针对一种事件,可能需要特定的监听器,因此,监听器需要实现ApplicationListener接口。
当监听器接收到一个事件的时候,就会执行它的onApplicationEvent()方法。
由于Spring IoC中的事件模型是一种简单的、粗粒度的监听模型,当有一个事件到达时,所有的监听器都会接收到,并且作出响应,如果希望只针对某些类型进行监听,需要在代码中进行控制。
public class UserListener implements ApplicationListener{public void onApplicationEvent(ApplicationEvent event){if(event instanceof UserEvent){ //只对UserEvent类型进行处理UserEvent ue = (UserEvent)event;String result = ue.getEventContent();System.out.println("Event Content:"+result);}}}对于发布事件,我们可以实现ApplicationContextAware或者ApplicationEventPublisherAware接口。
spring事件监听(eventListener)
spring事件监听(eventListener)原理:观察者模式spring的事件监听有三个部分组成,事件(ApplicationEvent)、监听器(ApplicationListener)和事件发布操作。
事件事件类需要继承ApplicationEvent,代码如下:public class HelloEvent extends ApplicationEvent {private String name;public HelloEvent(Object source, String name) {super(source); = name;}public String getName() {return name;}}这⾥为了简单测试,所以写的很简单。
事件类是⼀种很简单的pojo,除了需要继承ApplicationEvent也没什么了,这个类有⼀个构造⽅法需要super。
事件监听器@Componentpublic class HelloEventListener implements ApplicationListener<HelloEvent> {private static final Logger logger = LoggerFactory.getLogger(HelloEventListener.class);@Overridepublic void onApplicationEvent(HelloEvent event) {("receive {} say hello!",event.getName());}}事件监听器需要实现ApplicationListener接⼝,这是个泛型接⼝,泛型类类型就是事件类型,其次需要是spring容器托管的bean,所以这⾥加了@component,只有⼀个⽅法,就是onApplicationEvent。
事件发布操作有了事件和监听器,不发布事件也不⽤,事件发布⽅式很简单applicationContext.publishEvent(new HelloEvent(this,"lgb"));然后调⽤⽅法就能看到2018-10-02 19:08:00.052 INFO 284928 --- [nio-5577-exec-3] l.b.e.c.s.event.HelloEventListener : receive lgb say hello!疑问1. 同样的事件能有多个监听器 -- 经过测试是可以的2. 事件监听器⼀定要写⼀个类去实现吗 -- 其实是可以不需要的,spring有个注解@EventListener,修饰在⽅法上,稍后给出使⽤⽅法3. 事件监听操作和发布事件的操作是同步的吗? -- 是的,所以如果有事务,监听操作也在事务内使⽤@EventListener注解先给出代码@EventListenerpublic void listenHello(HelloEvent event) {("listen {} say hello from listenHello method",event.getName());}EventListener这个注解其实可以接受参数来表⽰事件源的,有两个参数classes和condition,顾明思议前者是表⽰哪⼀个事件类,后者是当满⾜什么条件是会调⽤该⽅法,但其实都可以不⽤⽤到,直接在⽅法上写参数HelloEvent就⾏。
【Spring】9、Spring中的事件Event
【Spring】9、Spring中的事件EventSpring的ApplicationContext提供了⽀持事件和代码中监听器的功能。
我们可以创建bean⽤来监听在ApplicationContext中发布的事件。
ApplicationEven t类和在ApplicationContext接⼝中处理的事件,如果⼀个bean实现了ApplicationListener接⼝,当⼀个ApplicationEvent被发布以后,bean会⾃动被通知。
1 2 3 4 5 6 7 8public class AllApplicationEventListener implements ApplicationListener < ApplicationEvent > {@Overridepublic void onApplicationEvent(ApplicationEvent applicationEvent){//process event}}Spring 提供了以下5中标准的事件:1. 上下⽂更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布。
也可以在调⽤ConfigurableApplicationContext 接⼝中的refresh()⽅法时被触发。
2. 上下⽂开始事件(ContextStartedEvent):当容器调⽤ConfigurableApplicationContext的Start()⽅法开始/重新开始容器时触发该事件。
3. 上下⽂停⽌事件(ContextStoppedEvent):当容器调⽤ConfigurableApplicationContext的Stop()⽅法停⽌容器时触发该事件。
4. 上下⽂关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。
patterntopic用法
PatternTopic是Spring Data Redis的监听器容器,用于监听Redis消息。
在实例化RedisMessageListenerContainer对象时,可以添加多个PatternTopic,每个PatternTopic表示一个监听的频道。
PatternTopic的构造方法可以接受一个String类型的参数,表示要监听的频道名称的模式。
在实例化PatternTopic对象时,可以通过传递一个具体的频道名称来指定要监听的频道。
以下是一个使用PatternTopic的示例:
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(this.redisConnectionFactory);
container.addMessageListener(new MyMessageListener(),
Arrays.asList(new PatternTopic("myChannel:*"), new PatternTopic("myOtherChannel:*")));
在上面的示例中,我们创建了一个RedisMessageListenerContainer对象,设置了连接工厂,并添加了一个监听器MyMessageListener,同时指定了要监听的频道模式。
通过使用PatternTopic,我们可以监听多个频道,并使用通配符来匹配不同的频道名称。
Spring:ApplicationListener用法及原理
if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } } 派发方法源码: @Override public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); //获取所有事件监听器 for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
手动发布事件 还是调用publishEvent方法
容器关闭 调用doClose方法:
事件派发器的创建 容器启动时,调用refresh方法,其中有一步就初始化好了一些事件派发器
// Decorate event as an ApplicationEvent if necessary ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<Object>(this, event); if (eventType == null) {
java 监听器应用场景及实例代码
一、Java 监听器的概念及作用Java 监听器是一种回调机制,用于在特定事件发生时,通知相关的监听器进行相应操作。
在Java开发中,监听器的应用广泛,可以用于各种事件的处理,例如用户界面的操作、网络通信的状态变化、数据模型的变化等。
通过监听器,可以实现模块间的解耦,提高代码的灵活性和可维护性。
二、Java 监听器的实现方式在Java中,监听器的实现主要依靠接口和事件对象。
通常会定义一个监听器接口,该接口包含事件处理方法;然后通过事件源注册监听器对象,并在事件发生时调用相应的监听器方法来处理事件。
在实际应用中,可以通过实现监听器接口,自定义事件对象,注册监听器等方式来实现监听器功能。
三、Java 监听器的应用场景1.用户界面交互在用户界面交互的场景下,可以通过监听器来处理按钮点击、菜单选择、鼠标移动等各种事件。
当用户点击按钮时,可以通过注册按钮监听器来处理按钮点击事件,触发相应的业务逻辑。
2.网络通信状态变化在网络通信的应用中,可以通过监听器来处理网络连接状态变化、数据接收等事件。
当网络连接建立时,可以触发连接监听器来处理连接成功事件,执行相关的数据传输操作。
3.数据模型变化在数据模型的应用中,可以通过监听器来处理数据对象的状态变化、属性修改等事件。
当数据对象的某个属性发生变化时,可以触发监听器来处理属性修改事件,更新相关的界面显示。
四、Java 监听器的实例代码以下是一个简单的Java监听器实例代码,用于实现按钮点击事件的处理。
```javaimport java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JOptionPane;public class ButtonListenerExample {public static void m本人n(String[] args) {JFrame frame = new JFrame("Button Listener Example"); JButton button = new JButton("Click Me");button.addActionListener(new ActionListener() {Overridepublic void actionPerformed(ActionEvent e) {JOptionPane.showMessageDialog(null, "Button Clicked");}});frame.add(button);frame.setSize(300, 200);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true);}}```以上代码创建了一个简单的Swing窗口应用,包含一个按钮组件,当用户点击按钮时,会弹出一个消息框提示按钮被点击。
spring整合redis消息监听通知使用的实现示例
spring整合redis消息监听通知使⽤的实现⽰例⽬录问题引⼊1.1 过期问题描述1.2 常⽤解决⽅案分析1.3.整合SpringData Redis开发spring整合redis监听消息1. 配置监听redis消息2 测试消息结合redis的key失效机制和消息完成过期优惠券处理1 模拟过期代⾦卷案例2 配置redis中key失效的消息监听3 接收失效消息完成过期代⾦卷处理问题引⼊在电商系统中,秒杀,抢购,红包优惠卷等操作,⼀般都会设置时间限制,⽐如订单15分钟不付款⾃动关闭,红包有效期24⼩时等等。
那对于这种需求最简单的处理⽅式就是使⽤定时任务,定时扫描数据库的⽅式处理。
但是为了更加精确的时间控制,定时任务的执⾏时间会设置的很短,所以会造成很⼤的数据库压⼒。
是否有更加稳妥的解决⽅式呢?我们可以利⽤REDIS的key失效机制结合REDIS的消息通知机制结合完成类似问题的处理。
1.1 过期问题描述在电商系统中,秒杀,抢购,红包优惠卷等操作,⼀般都会设置时间限制,⽐如订单15分钟不付款⾃动关闭,红包有效期24⼩时等等1.2 常⽤解决⽅案分析⽬前企业中最常见的解决⽅案⼤致分为两种:使⽤定时任务处理,定时扫描数据库中过期的数据,然后进⾏修改。
但是为了更加精确的时间控制,定时任务的执⾏时间会设置的很短,所以会造成很⼤的数据库压⼒。
使⽤消息通知,当数据失效时发送消息,程序接收到失效消息后对响应的数据进⾏状态修改。
此种⽅式不会对数据库造成太⼤的压⼒1.3.整合SpringData Redis开发我们使⽤redis解决过期优惠券和红包等问题,并且在java环境中使⽤redis的消息通知。
⽬前世⾯⽐较流⾏的java代码操作redis的AIP有:Jedis和RedisTemplateJedis是Redis官⽅推出的⼀款⾯向Java的客户端,提供了很多接⼝供Java语⾔调⽤。
SpringData Redis是Spring官⽅推出,可以算是Spring框架集成Redis操作的⼀个⼦框架,封装了Redis的很多命令,可以很⽅便的使⽤Spring操作Redis数据库。
SpringBoot使用过滤器和拦截器分别实现REST接口简易安全认证示例代码详解
SpringBoot使⽤过滤器和拦截器分别实现REST接⼝简易安全认证⽰例代码详解本⽂通过⼀个简易安全认证⽰例的开发实践,理解过滤器和拦截器的⼯作原理。
很多⽂章都将过滤器(Filter)、拦截器(Interceptor)和监听器(Listener)这三者和Spring关联起来讲解,并认为过滤器(Filter)、拦截器(Interceptor)和监听器(Listener)是Spring提供的应⽤⼴泛的组件功能。
但是严格来说,过滤器和监听器属于Servlet范畴的API,和Spring没什么关系。
因为过滤器继承⾃javax.servlet.Filter接⼝,监听器继承⾃javax.servlet.ServletContextListener接⼝,只有拦截器继承的是org.springframework.web.servlet.HandlerInterceptor接⼝。
上⾯的流程图参考⾃⽹上资料,⼀图胜千⾔。
看完本⽂以后,将对过滤器和拦截器的调⽤过程会有更深刻理解。
⼀、安全认证设计思路有时候内外⽹调⽤API,对安全性的要求不⼀样,很多情况下外⽹调⽤API的种种限制在内⽹根本没有必要,但是⽹关部署的时候,可能因为成本和复杂度等问题,内外⽹要调⽤的API会部署在⼀起。
实现REST接⼝的安全性,可以通过成熟框架如Spring Security或者 shiro 搞定。
但是因为安全框架往往实现复杂(我数了下Spring Security,洋洋洒洒⼤概有11个核⼼模块,shiro的源码代码量也⽐较惊⼈)同时可能要引⼊复杂配置(能不能让⼈痛快⼀点),不利于中⼩团队的灵活快速开发、部署及问题排查。
很多团队⾃⼰造轮⼦实现安全认证,本⽂这个简易认证⽰例参考⾃我所在的前⼚开发团队,可以认为是个基于token的安全认证服务。
⼤致设计思路如下:1、⾃定义http请求头,每次调⽤API都在请求头⾥传⼈⼀个token值2、token放在缓存(如redis)中,根据业务和API的不同设置不同策略的过期时间3、token可以设置⽩名单和⿊名单,可以限制API调⽤频率,便于开发和测试,便于紧急处理异状,甚⾄临时关闭API4、外⽹调⽤必须传⼈token,token可以和⽤户有关系,⽐如每次打开页⾯或者登录⽣成token写⼊请求头,页⾯验证cookie和token有效性等在Spring Security框架⾥有两个概念,即认证和授权,认证指可以访问系统的⽤户,⽽授权则是⽤户可以访问的资源。
Spring事件机制
Spring事件机制概念在⼀个完整的事件体系中、存在以下的⾓⾊1. 事件:描述发⽣了什么事情、⽐如说请求处理完成、Spring 容器刷新完毕2. 事件源:事件的产⽣者、任何⼀个事件都必须有⼀个事件源。
⽐如请求处理完成的事件源就是DispatcherServlet、Spring 容器刷新完毕的事件源就是ApplicationContext3. 事件⼴播器:事件和事件监听器的桥梁、负责把事件通知给事件监听器4. 事件监听器:监听事件的发⽣、可以在监听器中做⼀些处理Spring 事件我们常见的事件可能就是ApplicationContextEvent、它的⼦类ContextRefreshedEvent是我们常见的事件类型、在 Spring 将所有⾮延迟加载的 bean 实例化之后发布。
再来看看 Spring 事件的体系结构Spring 监听器事件⼴播器ApplicationContext 对事件的⽀持ApplicationEventPublisher 这个是 Spring 提供给⽤户使⽤的⼀个事件发布器啊、真正实现发布功能还是委托给上⾯的 ApplicationEventMulticaster 去实现的。
Spring 提供了 ApplicationEventPublisherAware 让⽤户可以去获取这个发布器进⾏事件发布。
使⽤⽅式Spring 提供了两种⽅式1. 实现 ApplicationListener 接⼝2. 使⽤注解 @EventListener注解的实现源码我们直接看到 EventListenerMethodProcessor 该类实现了接⼝ SmartInitializingSingleton 接⼝、该接⼝会在 Spring 初始化完所有的⾮延迟加载的 bean 之后被调⽤。
public interface SmartInitializingSingleton {/*** Invoked right at the end of the singleton pre-instantiation phase,* with a guarantee that all regular singleton beans have been created* already. {@link ListableBeanFactory#getBeansOfType} calls within* this method won't trigger accidental side effects during bootstrap.* <p><b>NOTE:</b> This callback won't be triggered for singleton beans* lazily initialized on demand after {@link BeanFactory} bootstrap,* and not for any other bean scope either. Carefully use it for beans* with the intended bootstrap semantics only.*/void afterSingletonsInstantiated();}其实实现逻辑⾮常简单for (Method method : annotatedMethods.keySet()) {for (EventListenerFactory factory : factories) {if (factory.supportsMethod(method)) {Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));ApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}context.addApplicationListener(applicationListener);break;}}}找出所有被注解修饰的⽅法、然后分别创建⼀个对应的 ApplicationListener、收到事件后反射调⽤该⽅法。
Spring事件监听器之@EventListener原理分析
Spring事件监听器之@EventListener原理分析⽬录Spring事件监听器之@EventListener原理⼀、解析@EventListener前的准备⼯作⼆、开始解析@EventListenerEventListener.FactoryEventListener.Factory监听⽹络请求全过程问题是如何将这些数据回传回来呢Spring事件监听器之@EventListener原理Spring为我们提供的⼀个事件监听、订阅的实现,内部实现原理是观察者设计模式;为的就是业务系统逻辑的解耦,提⾼可扩展性以及可维护性。
事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的⼯作只是为了发布事件⽽已。
在spring中我们可以通过实现ApplicationListener接⼝或者@EventListener接⼝来实现事件驱动编程⽐如我们做⼀个电商系统,⽤户下单⽀付成功后,我们⼀般要发短信或者邮箱给⽤户提⽰什么的,这时候就可以把这个通知业务做成⼀个单独事件监听,等待通知就可以了;把它解耦处理。
public class OrderEvent extends ApplicationEvent {public OrderEvent(Object source) {super(source);}}@Componentpublic class OrderEventListener {@EventListenerpublic void listener(OrderEvent event) {System.out.println("i do OrderEventListener" );}}@Controller@RequestMapping("person")public class PersonController implements ApplicationContextAware {private ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@ResponseBody@GetMapping("publishOrderEvent")public String publishOrderEvent() {applicationContext.publishEvent(new OrderEvent("我发布了事件"));System.out.println(" publishOrderEvent ");return "发送事件了!";}}EventListenerMethodProcessor是@EventListener的解析类,他是⼀个SmartInitializingSingleton和BeanFactoryPostProcessor⼀、解析@EventListener前的准备⼯作1.1 EventListenerFactory和EventListenerMethodProcessor的注⼊EventListenerFactory是把@EventListener标注的⽅法变成ApplicationListener的关键,其是在容器最初期(refresh⽅法发⽣前)就放到容器中去public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, Object source) {//获取对象DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);//org.springframework.context.event.internalEventListenerProcessor//@EventListener注解处理器if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));}//org.springframework.context.event.internalEventListenerProcessor//内部管理的EventListenerFactory的bean名称if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));}return beanDefs;}如果容器中没有名字是org.springframework.context.event.internalEventListenerProcessor的bean,那么就注⼊⼀个EventListenerMethodProcessor到容器中如果容器中没有名字是org.springframework.context.event.internalEventListenerProcessor的bean,那么就注⼊⼀个DefaultEventListenerFactory到容器中1.2 EventListenerMethodProcessor和EventListenerFactory关系的建⽴EventListenerMethodProcessor会在容器启动时被注⼊到容器中,他是⼀个BeanFactoryPostProcessor,EventListenerMethodProcessor和EventListenerFactory关系的建⽴就发⽣在其⽅法postProcessBeanFactory中public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {@Nullableprivate List<EventListenerFactory> eventListenerFactories;//初始化eventListenerFactories@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {this.beanFactory = beanFactory;//获取容器中所有的EventListenerFactory,并把他们实例化Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);List<EventListenerFactory> factories = new ArrayList<>(beans.values());AnnotationAwareOrderComparator.sort(factories);//将EventListenerFactory储存到缓存eventListenerFactories中,便于后来使⽤this.eventListenerFactories = factories;}}EventListenerFactory的实例化时机只⽐BeanFactoryPostProcessor完点,他⽐BeanPostProcessor实例化时机早⼆、开始解析@EventListenerEventListenerMethodProcessor是⼀个SmartInitializingSingleton,所以他会在所以bean实例化后,执⾏其afterSingletonsInstantiated⽅法注意:只有单例的SmartInitializingSingleton,才会执⾏其afterSingletonsInstantiated⽅法2.1 基本流程public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {@Overridepublic void afterSingletonsInstantiated() {ConfigurableListableBeanFactory beanFactory = this.beanFactory;Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");// 这⾥厉害了,⽤Object.class 是拿出容器⾥⾯所有的Bean定义~~~ ⼀个⼀个的检查String[] beanNames = beanFactory.getBeanNamesForType(Object.class);for (String beanName : beanNames) {//if (!ScopedProxyUtils.isScopedTarget(beanName)) {Class<?> type = null;try {// 防⽌是代理,吧真实的类型拿出来type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);}catch (Throwable ex) {if (logger.isDebugEnabled()) {logger.debug("", ex);}}if (type != null) {// 对专门的作⽤域对象进⾏兼容~~~~(绝⼤部分都⽤不着)if (ScopedObject.class.isAssignableFrom(type)) {try {Class<?> targetClass = AutoProxyUtils.determineTargetClass(beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));if (targetClass != null) {type = targetClass;}}catch (Throwable ex) {// An invalid scoped proxy arrangement - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);}}}try {// 真正处理这个Bean⾥⾯的⽅法们。
conditionevaluationreportlogginglistener - -回复
conditionevaluationreportlogginglistener - -回复什么是conditionevaluationreportlogginglistenerConditionEvaluationReportLoggingListener(条件评估报告日志监听器)是一个在Spring框架中用于监听条件评估报告的组件。
它负责在系统运行过程中,收集和记录对于条件评估的相关信息,以便开发人员在调试和优化系统时能够获得更多的信息。
在Spring框架中,条件评估被广泛应用于实现各种功能的开关。
通过配置条件评估,我们可以根据应用程序的不同环境设置不同的功能。
例如,在开发环境中,我们可能希望启用一些调试日志输出,而在生产环境中,我们可能希望关闭这些调试输出以提高性能。
条件评估报告是Spring框架用于记录条件评估结果的一种机制。
它告诉我们在系统初始化期间每个条件的评估结果是什么。
这些评估结果非常重要,因为它们决定了系统中哪些功能被启用或被禁用。
ConditionEvaluationReportLoggingListener的作用是监听条件评估报告,并将其记录下来。
这样,我们就可以在系统运行过程中查看条件评估的结果,以便更好地理解系统的行为和性能。
ConditionEvaluationReportLoggingListener的使用ConditionEvaluationReportLoggingListener是一个Java类,我们可以通过在Spring的配置文件中进行相应的配置来使用它。
通常,我们需要将它添加到Spring的ApplicationContext中。
下面是一个示例配置文件,展示了如何使用ConditionEvaluationReportLoggingListener:xml<beans><context:annotation-config/><context:component-scanbase-package="com.example"/><bean id="conditionEvaluationReportLoggingListener" class="org.springframework.boot.context.config.ConditionEvaluati onReportLoggingListener"/><! 其他相关配置></beans>在上面的配置中,我们使用了`context:component-scan`标签来指定需要扫描的包。
Spring的事件和监听器-同步与异步详解
Spring的事件和监听器-同步与异步详解⽬录Spring的事件和监听器-同步与异步1、⾸先新建StartWorkflowEvent.java,2、新建⼀个监听器StartWorkflowListener.java3、创建⼀个事件发布类EventPublisher.java4、相关的配置Spring事件、异步监听这可以对系统进⾏解耦Spring的事件和监听器-同步与异步Application下抽象⼦类ApplicationContextEvent的下⾯有4个已经实现好的事件ContextClosedEvent(容器关闭时)ContextRefreshedEvent(容器刷新是)ContextStartedEvent(容器启动时候)ContextStoppedEvent(容器停⽌的时候)同样,这四个事件都继承了ApplicationEvent,如果我们想⾃定义事件,也可以通过继承ApplicationEvent来实现1、⾸先新建StartWorkflowEvent.java,继承ApplicationEvent抽象类public class StartWorkflowEvent extends ApplicationEvent {//存放构造器送⼊的值private String msg;//构造器参数可以随意设置,这⾥为了⽅便调试,设置为字符串public StartWorkflowEvent (String msg) {super(msg);this.msg=msg;}//⾃定义⼀个⽅法,这个⽅法也可以随意写,这⾥也是测试⽤public void myevent(){System.out.println("********My event**************");System.out.println(msg);System.out.println("*******************************");}}2、新建⼀个监听器StartWorkflowListener.java实现ApplicationListener<StartWorkflowEvent>/*** 发起流程事件监听*/@Component("startWorkflowListener")public class StartWorkflowListener implements ApplicationListener<StartWorkflowEvent> {@Autowiredprivate OaWorkflowHepler oaWorkflowHepler;//@Async注解异步调⽤时使⽤, 异步调⽤时, 需要在xml配置⽂件中添加 <task:annotation-driven />// @Async@Overridepublic void onApplicationEvent(StartWorkflowEvent event) {oaWorkflowHepler.start(event.getMsg());}}3、创建⼀个事件发布类EventPublisher.java/*** 发布事件*/@Component("eventPublisher")public class EventPublisher {@Autowiredprivate ApplicationContext applicationContext;/*** 发布事件* @param event*/public void publishEvent(ApplicationEvent event) {applicationContext.publishEvent(event);}}4、相关的配置<task:annotation-driven />配置:executor:指定⼀个缺省的executor给@Async使⽤。
springboot用监听器统计在线人数案例分析
springboot⽤监听器统计在线⼈数案例分析本⽂在springboot 的项⽬,⽤HttpSessionListener 监听器(监听器的其中⼀种)统计在线⼈数,实质是统计session 的数量。
思路很简单,但是有个细节没处理好,让我调试了⼤半天,才把bug搞好。
先写个HttpSessionListener 监听器。
count 是session的数量(⼈数),session 创建的时候,会触发监听器的sessionCreated ⽅法,session销毁的时候,会触发监听器的sessionDestroyed ⽅法。
在监听器中计算完⼈数count,把他放进servletContext(可以理解为⼀个仓库,任意请求可以存储和获取⾥⾯的属性)。
注意监听器加上@WebListener,这样就不⽤配置。
@WebListenerpublic class OnLineCount implements HttpSessionListener {public int count=0;//记录session的数量//监听session的创建,synchronized 防并发bugpublic synchronized void sessionCreated(HttpSessionEvent arg0) {System.out.println("【HttpSessionListener监听器】count++ 增加");count++;arg0.getSession().getServletContext().setAttribute("count", count);}@Overridepublic synchronized void sessionDestroyed(HttpSessionEvent arg0) {//监听session的撤销System.out.println("【HttpSessionListener监听器】count-- 减少");count--;arg0.getSession().getServletContext().setAttribute("count", count);}}接着写⼀个查询session 数量的controller,我开始的时候是像下⾯这样写的,是错误的!从servletContext 中取出count ,把count返回前端。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
既然是监听器,他监听什么呢?
主要包括:
看下api,主要针对下面几个的监听,
ServletContent 就是jsp中的application;
HttpSession,jsp中的session;
HttpServletRequest,jsp中的request.
先看一下ServletContent吧,他是通过两个接口实现监听的:
Interface ServletContextAttributeListener,主要是针对
application的属性进行监听,看里面的方法:
public void attributeAdded(ServletContextAttributeEventscab),属性增加public void attributeRemoved(ServletContextAttributeEventscab),属性移除public void attributeReplaced(ServletContextAttributeEvent scab),属性替换和
Interface ServletContextListener,主要是对application的创
建与销毁进行监听,从里面的方法可以看出:
public void contextInitialized(ServletContextEvent sce),
public void contextDestroyed(ServletContextEvent sce)
一个是初始化,一个是销毁。
关于Session等,也是类似查看下api吧。
不在详细说明了。
下面以一个实例说明servlet监听器的具体使用吧。
用与统计在线人员列表:
先看下大体框架:
package com.zzc;
public class ShowPerson implements
ServletContentListener,HttpSessionListener,HttpSessionAttributer{
//对应接口的方法都要写上。
当然实现每个接口都是有用处的,
ServletContentListener不是相当于对application监听吗,这里
用于记录在线用户。
HttpSessionListener,把用户存入session .
HttpSessionAttributer,对在线人员属性的监听。
}
具体的写下,当然方法等都还是在api上复制下来的。
package com.zzc;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class ShowPerson implements
ServletContextListener,HttpSessionListener,HttpSessionAttributeListener{
private ServletContext application = null;
public void contextInitialized(ServletContextEvent sce){
this.application = sce.getServletContext();
this.application.setAttribute("allusers",new ArrayList()); //初始化存放用户的一个容器,采用数组存放
}
public void contextDestroyed(ServletContextEventsce){}
public void sessionCreated(HttpSessionEvent se){}
public void sessionDestroyed(HttpSessionEventse){//删除离开的用户
List l = (List)
this.application.getAttribute("allusers");
String value =(String) se.getSession().getAttribute("uname");
l.remove(value);
this.application.setAttribute("allusers",l);
}
public void attributeAdded(HttpSessionBindingEvent se){
/*当一个用户登陆成功时,存放进application*/
List l = (List)
this.application.getAttribute("allusers");
l.add(se.getValue());
this.application.setAttribute("allusers",l);
}
public void attributeRemoved(HttpSessionBindingEvent se){}
public void attributeReplaced(HttpSessionBindingEvent se){}
}
一个简单的显示在线用户的监听器完成了,不要忘记配置下xml.
<listener>
<listener-class>com.zzc.ShowPerson</listener-class>
</listener>
和过滤器不大一样,要简单点。
怎么在具体jsp文件中使用它呢?。
首先要有个action,用于用户登陆,在着简单方便光记个用户名。
还要用个循环显示登陆用户。
具体jsp 文件差不多就出来了。
<%@ page contentType="text/html;charset=utf-8"%>
<%@ page import="java.util.*"%>
<%
if(request.getParameter("name")!=null)
{
session.setAttribute("uname",request.getParameter("uname")) ;
}
%>
<!-- 传递参数到当前页面-->
<form action="?" method="post">
用户名:
<input type="text" name="uname">
<input type="submit" value="登陆">
<a href="#">注销</a> //可以在另一个页面调用session.invalidate(); </form>
<!-- 向session接收输入的用户名-->
<h2>在线人员</h2>
<hr />
<%
List l = (List)application.getAttribute("allusers") ;
Iterator it = l.iterator() ;
while(it.hasNext())
{
%>
<li><%=it.next()%>
<%
}
%>
到这框架差不多就完成了,然后可以对其自己修改或增加新的功能了。