Spring ApplicationListener 理解
在开发时有时候需要在整个应用开始运行时执行一些特定代码,比如初始化环境,准备测试数据、加载一些数据到内存等等。
在spring中可以通过ApplicationListener来实现相关的功能,加载完成后触发contextrefreshedevent事件(上下文件刷新事件)。
ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。
如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。这种事件机制都必须需要程序显示的触发。
其中spring有一些内置的事件,当完成某种操作时会发出某些事件动作。比如监听ContextRefreshedEvent事件,当所有的bean都初始化完成并被成功装载后会触发该事件,实现ApplicationListener<ContextRefreshedEvent>接口可以收到监听动作,然后可以写自己的逻辑。
同样事件可以自定义、监听也可以自定义,完全根据自己的业务逻辑来处理。
内置事件
序号 Spring 内置事件 & 描述
1 ContextRefreshedEvent
ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用。
2 ContextStartedEvent
当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
3 ContextStoppedEvent
当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。
4 ContextClosedEvent
当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
5 RequestHandledEvent
这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。
业务方监听事件举例
比如要监听ContextRefreshedEvent的时可以实现ApplicationListener接口,并且传入要监听的事件
@Component
public class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent>{
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
System.out.println(contextRefreshedEvent);
System.out.println("TestApplicationListener............................");
}
}
自定义事件
可以自定义事件,然后做完业务处理后手动发出。同上集成某个监听接口,接收到事件后进行业务处理
事件定义:
public class EmailEvent extends ApplicationEvent{
private String address;
private String text;
public EmailEvent(Object source, String address, String text){
super(source);
this.address = address;
this.text = text;
}
public EmailEvent(Object source) {
super(source);
}
//......address和text的setter、getter
}
监听定义
public class EmailNotifier implements ApplicationListener{
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof EmailEvent) {
EmailEvent emailEvent = (EmailEvent)event;
System.out.println("邮件地址:" + emailEvent.getAddress());
System.our.println("邮件内容:" + emailEvent.getText());
} else {
System.our.println("容器本身事件:" + event);
}
}
}
业务触发
public class SpringTest {
public static void main(String args[]){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//创建一个ApplicationEvent对象
EmailEvent event = new EmailEvent("hello","abc@163.com","This is a test");
//主动触发该事件
context.publishEvent(event);
}
}
不管是内置监听还是外部自定义监听一定要把实现ApplicationListener的类定义成一个bean才行,可以是通过注解@Component等也可以通过xml的方式去执行。
但是这个时候,会存在一个问题,在web 项目中(spring mvc),系统会存在两个容器,一个是root application context ,另一个就是我们自己的 projectName-servlet context(作为root application context的子容器)。
这种情况下,就会造成onApplicationEvent方法被执行两次。为了避免上面提到的问题,我们可以只在root application context初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理,修改后代码。event.getApplicationContext().getDisplayName().equals("Root WebApplicationContext") 。
如下:(要把实现ApplicationListener的类定义成一个bean)
@Component
public class SpringStartedListener implements ApplicationListener<ContextRefreshedEvent> { private static final Logger logger = LoggerFactory.getLogger(SpringStartedListener.class);
private static final String CONTEXT_DISPLAY_NAME = "WebApplicationContext"; @Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor; @Override
public void onApplicationEvent(ContextRefreshedEvent event) { if (event.getApplicationContext().getDisplayName().indexOf(CONTEXT_DISPLAY_NAME) > -1) {
ConfigConstants.initConfig(); // 初始化配置
threadPoolTaskExecutor.submit(() -> {
//在这里启动netty
Server server = NettyServer.getInstance();
try {
logger.debug("NettyServer Starting...");
server.start();
} catch (InterruptedException e) {
logger.error("init NettyServer err:{}", e.getMessage(), e);
}
});
}
}
}
Spring ApplicationListener 理解的更多相关文章
- Java框架-Spring MVC理解001
Spring MVC理解 1.servlet--Spring MVC的本质 2.Spring MVC其实是一个工具,具体的理解可以分为两步:第一步,了解这个工具是怎么创建出来的:第二步,了解这个工具是 ...
- spring的理解
看过<fate系列>的博友知道,这是一个七位英灵的圣杯争夺战争.今天主要来谈谈圣杯的容器概念,以便对spring的理解. 圣杯: 圣杯本身是没有实体的,而是将具有魔术回路的存在(人)作为“ ...
- mybatis中两种取值方式?谈谈Spring框架理解?
1.mybatis中两种取值方式? 回答:Mybatis中取值方式有几种?各自区别是什么? Mybatis取值方式就是说在Mapper文件中获取service传过来的值的方法,总共有两种方式,通过 $ ...
- Spring深入理解(三)
Spring 中 AOP 特性详解 动态代理的实现原理 要了解 Spring 的 AOP 就必须先了解动态代理的原理,因为 AOP 就是基于动态代理实现的.动态代理还要从 JDK 本身说起. 在 Jd ...
- 怎么回答面试官:你对Spring的理解?
最近看了点Spring的源码,正好来稍微扯一扯,帮一部分培训班的朋友撕开一道口子,透透气.我自己都是看的培训班视频,所以也算培训班出身吧.所以下文开口闭口"培训班",不要觉得是我在 ...
- 谈一下spring 的理解
spring,大家基本都在使用,两个核心: 声明式事务AOP : 控制反转依赖注入IOC: 以前对控制反转和依赖注入很模糊,现在大概理解了意思,控制反转,有spring容易来控制bean 的创建,我们 ...
- Spring ApplicationListener使用方法及问题
使用场景 在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载.初始化缓存.特定任务的注册等等.这个时候我们就可以使用Spring提供的ApplicationListener来 ...
- spring IOC理解
用spring做了几个项目后发现,对spring的IOC理解还是不够清晰,今天就来总结下自己的理解(个人的一些见解) 以前用jsp+servlet做网站时,只是分了显示层(jsp),控制层(servl ...
- Spring ApplicationListener使用方法及二次调用问题解决
使用场景 在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载.初始化缓存.特定任务的注册等等.这个时候我们就可以使用Spring提供的ApplicationListener来 ...
随机推荐
- 查看Oracle的表中有哪些索引及其禁用索引
查看Oracle中表的索引是否存在 用user_indexes和user_ind_columns系统表查看已经存在的索引 对于系统中已经存在的索引我们可以通过以下的两个系统视图(user_indexe ...
- (转)理解POST和PUT的区别,顺便提下RESTful
这两个方法咋一看都可以更新资源,但是有本质区别的 具体定义可以百度,我这里就不贴了,光说我自己的理解 首先解释幂等,幂等是数学的一个用语,对于单个输入或者无输入的运算方法,如果每次都是同样的结果,则称 ...
- 《Oracle RAC性能优化》
一 RAC环境 RAC架构,2节点信息 节点1 SQL> show parameter instance NAME TYPE ...
- 国内NLP的那些人那些会
统计学和语言学专家都列在一起了,没有区分.1,黄昌宁,1937年生于广东,1955年考入清华大学电机系,1961年毕业并留校任教至博士生导师, 1983-1984年赴美国耶鲁大学进修,1986-198 ...
- Linux服务器access_log日志分析及配置详解(一)
nginx的log日志分为access log 和 error log 其中access log 记录了哪些用户,哪些页面以及用户浏览器.ip和其他的访问信息 error log 则是记录服务器错误日 ...
- selenium+python学习总结
学习了一个月的selenium+python,终于学有所成,下面以一个简单的项目来总结学习所得. 1. 项目结构 在项目结构中,大家要注意到:每一个源文件夹中都要有一个__init__ ...
- Lua 可控下标数组遍历
, , , , , , , , , , , } , , } local j = 1 while i <= #aaa do if bbb[j] == aaa[i] then -- 如果 b下标元素 ...
- python中的引用传递,可变对象,不可变对象,list注意点
python中的引用传递 首先必须理解的是,python中一切的传递都是引用(地址),无论是赋值还是函数调用,不存在值传递. 可变对象和不可变对象 python变量保存的是对象的引用,这个引用指向堆内 ...
- 你知道C语言为什么会有“_”(下划线)吗?
学过C语言的都知道,变量名只能由字母.数字.下划线组成,且只能以字母或者下划线开头. 学英语时我们都学过连字符(“-”),这个东东主要用来连接单词的.那么C语言为什么不直接 用连字符,而要改用下划线呢 ...
- wix toolset将 cab 打包进msi
在***wxs中添加配置 <MediaTemplate EmbedCab="yes" /> 下面是一个简单配置:(包含写注册表) <?xml version=&q ...