新聞中心
我們經(jīng)常需要在容器啟動(dòng)的時(shí)候做一些鉤子動(dòng)作,比如注冊(cè)消息消費(fèi)者,監(jiān)聽配置等,今天就總結(jié)下SpringBoot留給開發(fā)者的7個(gè)啟動(dòng)擴(kuò)展點(diǎn)。

創(chuàng)新互聯(lián)公司專注于利通企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,成都做商城網(wǎng)站。利通網(wǎng)站建設(shè)公司,為利通等地區(qū)提供建站服務(wù)。全流程按需網(wǎng)站設(shè)計(jì),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
容器刷新完成擴(kuò)展點(diǎn)
1、監(jiān)聽容器刷新完成擴(kuò)展點(diǎn)ApplicationListener
基本用法
熟悉Spring的同學(xué)一定知道,容器刷新成功意味著所有的Bean初始化已經(jīng)完成,當(dāng)容器刷新之后Spring將會(huì)調(diào)用容器內(nèi)所有實(shí)現(xiàn)了ApplicationListener
- @Component
- public class StartupApplicationListenerExample implements
- ApplicationListener
{ - private static final Logger LOG
- = Logger.getLogger(StartupApplicationListenerExample.class);
- public static int counter;
- @Override public void onApplicationEvent(ContextRefreshedEvent event) {
- LOG.info("Increment counter");
- counter++;
- }
- }
易錯(cuò)的點(diǎn)
這個(gè)擴(kuò)展點(diǎn)用在web容器中的時(shí)候需要額外注意,在web 項(xiàng)目中(例如spring mvc),系統(tǒng)會(huì)存在兩個(gè)容器,一個(gè)是root application context,另一個(gè)就是我們自己的context(作為root application context的子容器)。如果按照上面這種寫法,就會(huì)造成onApplicationEvent方法被執(zhí)行兩次。解決此問題的方法如下:
- @Component
- public class StartupApplicationListenerExample implements
- ApplicationListener
{ - 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 沒有parent
- LOG.info("Increment counter");
- counter++;
- }
- }
- }
高階玩法
當(dāng)然這個(gè)擴(kuò)展還可以有更高階的玩法:自定義事件,可以借助Spring以最小成本實(shí)現(xiàn)一個(gè)觀察者模式:
- 先自定義一個(gè)事件:
- 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方法
- }
- 注冊(cè)一個(gè)事件監(jiān)聽器
- @Component
- public class NotifyListener implements ApplicationListener
{ - @Override
- public void onApplicationEvent(NotifyEvent event) {
- System.out.println("郵件地址:" + event.getEmail());
- System.out.println("郵件內(nèi)容:" + event.getContent());
- }
- }
- 發(fā)布事件
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class ListenerTest {
- @Autowired
- private WebApplicationContext webApplicationContext;
- @Test
- public void testListener() {
- NotifyEvent event = new NotifyEvent("object", "[email protected]", "This is the content");
- webApplicationContext.publishEvent(event);
- }
- }
- 執(zhí)行單元測(cè)試可以看到郵件的地址和內(nèi)容都被打印出來(lái)了
2、SpringBoot的CommandLineRunner接口
當(dāng)容器上下文初始化完成之后,SpringBoot也會(huì)調(diào)用所有實(shí)現(xiàn)了CommandLineRunner接口的run方法,下面這段代碼可起到和上文同樣的作用:
- @Component
- public class CommandLineAppStartupRunner implements CommandLineRunner {
- private static final Logger LOG =
- LoggerFactory.getLogger(CommandLineAppStartupRunner.class);
- public static int counter;
- @Override
- public void run(String...args) throws Exception {
- LOG.info("Increment counter");
- counter++;
- }
- }
對(duì)于這個(gè)擴(kuò)展點(diǎn)的使用有額外兩點(diǎn)需要注意:
- 多個(gè)實(shí)現(xiàn)了CommandLineRunner的Bean的執(zhí)行順序可以根據(jù)Bean上的@Order注解調(diào)整
- 其run方法可以接受從控制臺(tái)輸入的參數(shù),跟ApplicationListener
這種擴(kuò)展相比,更加靈活
- // 從控制臺(tái)輸入?yún)?shù)示例
- java -jar CommandLineAppStartupRunner.jar abc abcd
3、SpringBoot的ApplicationRunner接口
這個(gè)擴(kuò)展和SpringBoot的CommandLineRunner接口的擴(kuò)展類似,只不過(guò)接受的參數(shù)是一個(gè)ApplicationArguments類,對(duì)控制臺(tái)輸入的參數(shù)提供了更好的封裝,以--開頭的被視為帶選項(xiàng)的參數(shù),否則是普通的參數(shù)
- @Component
- public class AppStartupRunner implements ApplicationRunner {
- private static final Logger LOG =
- LoggerFactory.getLogger(AppStartupRunner.class);
- public static int counter;
- @Override
- public 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初始化完成擴(kuò)展點(diǎn)
前面的內(nèi)容總結(jié)了針對(duì)容器初始化的擴(kuò)展點(diǎn),在有些場(chǎng)景,比如監(jiān)聽消息的時(shí)候,我們希望Bean初始化完成之后立刻注冊(cè)監(jiān)聽器,而不是等到整個(gè)容器刷新完成,Spring針對(duì)這種場(chǎng)景同樣留足了擴(kuò)展點(diǎn):
1、@PostConstruct注解
- @PostConstruct注解一般放在Bean的方法上,被@PostConstruct修飾的方法會(huì)在Bean初始化后馬上調(diào)用:
- @Component
- public class PostConstructExampleBean {
- private static final Logger LOG
- = Logger.getLogger(PostConstructExampleBean.class);
- @Autowired
- private Environment environment;
- @PostConstruct
- public void init() {
- LOG.info(Arrays.asList(environment.getDefaultProfiles()));
- }
- }
2、 InitializingBean接口
InitializingBean的用法基本上與@PostConstruct一致,只不過(guò)相應(yīng)的Bean需要實(shí)現(xiàn)afterPropertiesSet方法
- @Component
- public class InitializingBeanExampleBean implements InitializingBean {
- private static final Logger LOG
- = Logger.getLogger(InitializingBeanExampleBean.class);
- @Autowired
- private Environment environment;
- @Override
- public void afterPropertiesSet() throws Exception {
- LOG.info(Arrays.asList(environment.getDefaultProfiles()));
- }
- }
3、@Bean注解的初始化方法
通過(guò)@Bean注入Bean的時(shí)候可以指定初始化方法:
Bean的定義
- public class InitMethodExampleBean {
- private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);
- @Autowired
- private Environment environment;
- public void init() {
- LOG.info(Arrays.asList(environment.getDefaultProfiles()));
- }
- }
Bean注入
- @Bean(initMethod="init")
- public InitMethodExampleBean initMethodExampleBean() {
- return new InitMethodExampleBean();
- }
4、通過(guò)構(gòu)造函數(shù)注入
Spring也支持通過(guò)構(gòu)造函數(shù)注入,我們可以把搞事情的代碼寫在構(gòu)造函數(shù)中,同樣能達(dá)到目的
- @Component
- public class LogicInConstructorExampleBean {
- private static final Logger LOG
- = Logger.getLogger(LogicInConstructorExampleBean.class);
- private final Environment environment;
- @Autowired
- public LogicInConstructorExampleBean(Environment environment) {
- this.environment = environment;
- LOG.info(Arrays.asList(environment.getDefaultProfiles()));
- }
- }
Bean初始化完成擴(kuò)展點(diǎn)執(zhí)行順序?
可以用一個(gè)簡(jiǎn)單的測(cè)試:
- @Component
- @Scope(value = "prototype")
- public class AllStrategiesExampleBean implements InitializingBean {
- private static final Logger LOG
- = Logger.getLogger(AllStrategiesExampleBean.class);
- public AllStrategiesExampleBean() {
- LOG.info("Constructor");
- }
- @Override
- public void afterPropertiesSet() throws Exception {
- LOG.info("InitializingBean");
- }
- @PostConstruct
- public void postConstruct() {
- LOG.info("PostConstruct");
- }
- public void init() {
- LOG.info("init-method");
- }
- }
實(shí)例化這個(gè)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
當(dāng)前題目:七種方式,教你在SpringBoot初始化時(shí)搞點(diǎn)事情!
分享路徑:http://m.fisionsoft.com.cn/article/djshcjc.html


咨詢
建站咨詢
