七种方式,教你在SpringBoot初始化时搞点事情!
我们经常需要在容器启动的时候做一些钩子动作,比如注册消息消费者,监听配置等,今天就总结下SpringBoot
留给开发者的7个启动扩展点。
容器刷新完成扩展点
1、监听容器刷新完成扩展点ApplicationListener<ContextRefreshedEvent>
基本用法
熟悉Spring
的同学一定知道,容器刷新成功意味着所有的Bean
初始化已经完成,当容器刷新之后Spring
将会调用容器内所有实现了ApplicationListener<ContextRefreshedEvent>
的Bean
的onApplicationEvent
方法,应用程序可以以此达到监听容器初始化完成事件的目的。
@Component
public class StartupApplicationListenerExample implements ApplicationListener<ContextRefreshedEvent> {private static final Logger LOG = Logger.getLogger(StartupApplicationListenerExample.class);public static int counter;@Override public void onApplicationEvent(ContextRefreshedEvent event) {LOG.info("Increment counter");counter++;}
}
易错的点
这个扩展点用在web
容器中的时候需要额外注意,在web 项目中(例如spring mvc
),系统会存在两个容器,一个是root application context
,另一个就是我们自己的context
(作为root application context
的子容器)。如果按照上面这种写法,就会造成onApplicationEvent
方法被执行两次。解决此问题的方法如下:
@Component
public class StartupApplicationListenerExample implements ApplicationListener<ContextRefreshedEvent> {private static final Logger LOG = Logger.getLogger(StartupApplicationListenerExample.class);public static int counter;@Override public void onApplicationEvent(ContextRefreshedEvent event) {if (event.getApplicationContext().getParent() == null) {// root application context 没有parentLOG.info("Increment counter");counter++;}}
}
高阶玩法
当然这个扩展还可以有更高阶的玩法:自定义事件,可以借助Spring
以最小成本实现一个观察者模式:
先自定义一个事件:
public class NotifyEvent extends ApplicationEvent {private String email;private String content;public NotifyEvent(Object source) {super(source);}public NotifyEvent(Object source, String email, String content) {super(source);this.email = email;this.content = content;}// 省略getter/setter方法
}
注册一个事件监听器
@Component
public class NotifyListener implements ApplicationListener<NotifyEvent> {@Overridepublic void onApplicationEvent(NotifyEvent event) {System.out.println("邮件地址:" + event.getEmail());System.out.println("邮件内容:" + event.getContent());}
}
发布事件
@RunWith(SpringRunner.class)
@SpringBootTest
public class ListenerTest {@Autowiredprivate WebApplicationContext webApplicationContext;@Testpublic void testListener() {NotifyEvent event = new NotifyEvent("object", "abc@qq.com", "This is the content");webApplicationContext.publishEvent(event);}
}
执行单元测试可以看到邮件的地址和内容都被打印出来了
2、SpringBoot
的CommandLineRunner
接口
当容器上下文初始化完成之后,SpringBoot
也会调用所有实现了CommandLineRunner
接口的run
方法,下面这段代码可起到和上文同样的作用:
@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {private static final Logger LOG =LoggerFactory.getLogger(CommandLineAppStartupRunner.class);public static int counter;@Overridepublic void run(String...args) throws Exception {LOG.info("Increment counter");counter++;}
}
对于这个扩展点的使用有额外两点需要注意:
多个实现了
CommandLineRunner
的Bean
的执行顺序可以根据Bean
上的@Order
注解调整其
run
方法可以接受从控制台输入的参数,跟ApplicationListener<ContextRefreshedEvent>
这种扩展相比,更加灵活
// 从控制台输入参数示例
java -jar CommandLineAppStartupRunner.jar abc abcd
3、SpringBoot
的ApplicationRunner
接口
这个扩展和SpringBoot
的CommandLineRunner
接口的扩展类似,只不过接受的参数是一个ApplicationArguments
类,对控制台输入的参数提供了更好的封装,以--
开头的被视为带选项的参数,否则是普通的参数
@Component
public class AppStartupRunner implements ApplicationRunner {private static final Logger LOG =LoggerFactory.getLogger(AppStartupRunner.class);public static int counter;@Overridepublic void run(ApplicationArguments args) throws Exception {LOG.info("Application started with option names : {}", args.getOptionNames());LOG.info("Increment counter");counter++;}
}
比如:
java -jar CommandLineAppStartupRunner.jar abc abcd --autho=mark verbose
Bean
初始化完成扩展点
前面的内容总结了针对容器初始化的扩展点,在有些场景,比如监听消息的时候,我们希望Bean
初始化完成之后立刻注册监听器,而不是等到整个容器刷新完成,Spring
针对这种场景同样留足了扩展点:
1、@PostConstruct
注解
@PostConstruct
注解一般放在Bean
的方法上,被@PostConstruct
修饰的方法会在Bean
初始化后马上调用:
@Component
public class PostConstructExampleBean {private static final Logger LOG = Logger.getLogger(PostConstructExampleBean.class);@Autowiredprivate Environment environment;@PostConstructpublic void init() {LOG.info(Arrays.asList(environment.getDefaultProfiles()));}
}
2、 InitializingBean
接口
InitializingBean
的用法基本上与@PostConstruct
一致,只不过相应的Bean
需要实现afterPropertiesSet
方法
@Component
public class InitializingBeanExampleBean implements InitializingBean {private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class);@Autowiredprivate Environment environment;@Overridepublic void afterPropertiesSet() throws Exception {LOG.info(Arrays.asList(environment.getDefaultProfiles()));}
}
3、@Bean
注解的初始化方法
通过@Bean
注入Bean
的时候可以指定初始化方法:
Bean
的定义
public class InitMethodExampleBean {private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);@Autowiredprivate Environment environment;public void init() {LOG.info(Arrays.asList(environment.getDefaultProfiles()));}
}
Bean
注入
@Bean(initMethod="init")
public InitMethodExampleBean initMethodExampleBean() {return new InitMethodExampleBean();
}
4、通过构造函数注入
Spring
也支持通过构造函数注入,我们可以把搞事情的代码写在构造函数中,同样能达到目的
@Component
public class LogicInConstructorExampleBean {private static final Logger LOG = Logger.getLogger(LogicInConstructorExampleBean.class);private final Environment environment;@Autowiredpublic LogicInConstructorExampleBean(Environment environment) {this.environment = environment;LOG.info(Arrays.asList(environment.getDefaultProfiles()));}
}
Bean
初始化完成扩展点执行顺序?
可以用一个简单的测试:
@Component
@Scope(value = "prototype")
public class AllStrategiesExampleBean implements InitializingBean {private static final Logger LOG = Logger.getLogger(AllStrategiesExampleBean.class);public AllStrategiesExampleBean() {LOG.info("Constructor");}@Overridepublic void afterPropertiesSet() throws Exception {LOG.info("InitializingBean");}@PostConstructpublic void postConstruct() {LOG.info("PostConstruct");}public void init() {LOG.info("init-method");}
}
实例化这个Bean
后输出:
[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor
[main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct
[main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean
[main] INFO o.b.startup.AllStrategiesExampleBean - init-method
END
推荐好文
>>【练手项目】基于SpringBoot的ERP系统,自带进销存+财务+生产功能>>分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!
>>能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!
七种方式,教你在SpringBoot初始化时搞点事情!相关推荐
- 七种方式教你在 SpringBoot 初始化时搞点事情
[CSDN 编者按]本文总结了Spring Boot容器初始化和Java Bean初始化两个维度总计七种可以由开发者自定义的Spring Boot扩展点,开发者可以根据这些扩展点实现消息监听,配置监听 ...
- 技术研究院003---六种方式,教你在SpringBoot初始化时搞点事情!
前言 在实际工作中总是需要在项目启动时做一些初始化的操作,比如初始化线程池.提前加载好加密证书....... 那么经典问题来了,这也是面试官经常会问到的一个问题:有哪些手段在Spring Boot 项 ...
- 六种方式,教你在SpringBoot初始化时搞点事情!
前言 在实际工作中总是需要在项目启动时做一些初始化的操作,比如初始化线程池.提前加载好加密证书....... 那么经典问题来了,这也是面试官经常会问到的一个问题:有哪些手段在Spring Boot 项 ...
- 七种方法教你如何获取以太坊测试网Token
七种方法教你如何获取以太坊测试网Token 在使用以太坊测试网时,我们通常都需要获取一些测试币,这里就以MetaMask为例介绍一下怎么获取以太坊测试币. 首先介绍一种最权威且一直可以用的方法,以Ro ...
- MFC对话框控件访问的七种方式
void CTestDlg::OnButtonAdd() {// TODO: Add your control notification handler code here//动态创建按钮 /* if ...
- Python合并字典的七种方式!
Python有很多高级属性,例如合并字典就有七种方式可以实现.小千今天就来给大家展示一下,避免将来遇到的时候不知道是做什么的,提前了解一下以备不时之需. 1.最简单的原地更新 字典对象内置了一个 up ...
- Vue.js 定义组件模板的七种方式
转载自 Vue.js 定义组件模板的七种方式 在 Vue 中定义一个组件模板,至少有七种不同的方式(或许还有其它我不知道的方式): 字符串 模板字面量 x-template 内联模板 render ...
- 我赢助手详解:抖音变现目前流行的是七种方式之直播变现和Ip变现
抖音变现目前流行的是七种方式,电商卖货.广告营销.内容付费.品牌导流.直播变现.IP变现.社群营销. 今天我们来说一说直播变现模式.直播变现,他有两种模式: 第一种就是直接在直播中去带货,然后就转化. ...
- 短视频运营详解:抖音变现目前流行的是七种方式之一电商卖货
短视频运营详解:抖音变现目前流行的是七种方式之电商卖货 抖音变现目前流行的是七种方式,电商卖货.广告营销.内容付费.品牌导流.直播变现.IP变现.社群营销. 我们分别来说一下:权威的三方报告提到过,抖 ...
最新文章
- 27、Power Query-日期与时间数据处理实例(图书室借书统计)
- 汽车行业如何个性化定制转型?看APS系统在这家企业的运用
- 二叉树和为某种所有路径
- boost::intrusive::derivation_value_traits用法的测试程序
- 那个linux系统带有安装工具,linux系统程序安装(一)rpm工具
- 使用ViewPager实现左右循环滑动图片
- MEF 导入(Import)和导出(Export)
- C++ 学习之旅(10)——static与extern
- mysql insert limit_Mysql Limit 调优
- React开发(160):onref使用
- jsp fmt标签详解
- Qt工作笔记-对qmake的认识【两篇转载结合】
- 清除默认的内边距与外边距
- Memory Management Registers(GDTR, LDTR, IDTR, and TR)
- 26秒!全球销量第一的AI音箱就被腾讯黑掉了,然后变身窃听器
- 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-1.SpringBoot整合微信支付开发在线教育视频站点介绍...
- 非华为电脑安装华为电脑管家11.1.1.101
- web前端课程设计:个人博客网站设计——个人博客(6页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 web前端设计与开发期末作品
- 大学校园无线智能调频广播系统
- 据悉知名程序员社区因系统故障至页面乱码瘫痪,如电影情节?
热门文章
- 2799元起!小米CC9 Pro正式开售:1亿像素加持DXOMark总分第一
- 更小的刘海和更宽的5G天线,下一代iPhone你期待吗?
- 机皇再翻车!微信、支付宝关停其指纹支付功能,用户难以原谅
- 暴风集团冯鑫被批捕:涉嫌对非国家工作人员行贿罪、职务侵占罪
- 微博开始动手了:禁言关闭六神磊磊等50个头部账号
- 来了!小米9发布时间确定:2月20日见 为你而战
- request_irq() | 注册中断服务【ZT】
- leetcode--single number.
- 服务器可以修改cookie吗,是否可以为您拥有的网站/服务器设置Cookie?
- Bitmap详解(上)常用概念和常用API