使用Spring啟動(dòng)時(shí)運(yùn)行自定義業(yè)務(wù)
在Spring應(yīng)用啟動(dòng)時(shí)運(yùn)行自定義業(yè)務(wù)的場(chǎng)景很常見(jiàn),但應(yīng)用不當(dāng)也可能會(huì)導(dǎo)致一些問(wèn)題。
基于Spring控制反轉(zhuǎn)(Inverse of Control)功能用戶(hù)幾乎不用干預(yù)bean實(shí)例化過(guò)程,對(duì)于自定義業(yè)務(wù)則需要控制部分流程及容器,因此值得須特別關(guān)注。
1. Spring啟動(dòng)時(shí)運(yùn)行自定義業(yè)務(wù)
我們不能簡(jiǎn)單包括自定義業(yè)務(wù)在bean的構(gòu)造函數(shù)或在實(shí)例化任何對(duì)象之后調(diào)用方法,這些過(guò)程不由我們控制。請(qǐng)看示例:
@Component
public class InvalidInitExampleBean {
@Autowired
private Environment env;
public InvalidInitExampleBean() {
env.getActiveProfiles();
}
}
這里嘗試在構(gòu)造函數(shù)中訪問(wèn)自動(dòng)裝配的屬性。當(dāng)調(diào)用構(gòu)造函數(shù)時(shí),Spring bean仍沒(méi)有全部初始化,因此導(dǎo)致NullPointerExceptions異常。下面介紹幾種方式解決此問(wèn)題。
1.1 @PostConstruct 注解
@PostConstruct注解用于方法上,實(shí)現(xiàn)bean初始化后立刻執(zhí)行一次。需要注意的是,即使沒(méi)有對(duì)象注入,Spring也會(huì)執(zhí)行注解方法。
@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()));
}
}
上面示例可以實(shí)現(xiàn)Environment environment被安全注入,然后調(diào)用注解方法且不會(huì)出現(xiàn)空指針異常。
1.2 InitializingBean 接口
InitializingBean接口實(shí)現(xiàn)功能與上節(jié)類(lèi)似。但需要實(shí)現(xiàn)接口并重寫(xiě)afterPropertiesSet方法。
下面重寫(xiě)前節(jié)的示例:
@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()));
}
}
1.3 ApplicationListener 監(jiān)聽(tīng)器
該方法可用于在Spring上下文初始化之后執(zhí)行自定義業(yè)務(wù)。因此不針對(duì)特定bean,而是等待所有bean初始化之后。應(yīng)用時(shí)需要實(shí)現(xiàn)ApplicationListener接口:
@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++;
}
}
同樣可以引入@EventListener注解實(shí)現(xiàn):
@Component
public class EventListenerExampleBean {
private static final Logger LOG
= Logger.getLogger(EventListenerExampleBean.class);
public static int counter;
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
LOG.info("Increment counter");
counter++;
}
}
上面示例使用ContextRefreshedEvent,具體選擇哪種事件根據(jù)你的業(yè)務(wù)需要。
1.4 @Bean的初始化方法
該注解的initMethod屬性可用于在bean初始化之后執(zhí)行方法,示例:
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()));
}
}
既不要實(shí)現(xiàn)接口,也不要特定注解。通過(guò)注解定義Bean:
@Bean(initMethod="init")
public InitMethodExampleBean initMethodExampleBean() {
return new InitMethodExampleBean();
}
對(duì)應(yīng)xml配置:
<bean id="initMethodExampleBean" class="com.baeldung.startup.InitMethodExampleBean" init-method="init"> </bean>
1.5 構(gòu)造函數(shù)注入
如果使用構(gòu)造器注入屬性,可以簡(jiǎn)單地在構(gòu)造函數(shù)中包括業(yè)務(wù):
@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()));
}
}
1.6 Spring Boot CommandLineRunner接口
Spring Boot 提供了CommandLineRunner接口,重寫(xiě)run方法,可以在應(yīng)用啟動(dòng)時(shí)Spring應(yīng)用上下文實(shí)例化之后調(diào)用。
@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++;
}
}
CommandLineRunner bean在相同上下文中可以定義多個(gè),通過(guò)使用Ordered 接口或@Ordere注解確定順序。
1.7 Spring Boot ApplicationRunner
與CommandLineRunner類(lèi)似,Spring Boot 也提供了ApplicationRunner接口,重寫(xiě)run方法可以實(shí)現(xiàn)應(yīng)用啟動(dòng)時(shí)執(zhí)行自定義業(yè)務(wù)。另外其回調(diào)方法沒(méi)有使用String參數(shù),而是使用ApplicationArguments類(lèi)的實(shí)例。
ApplicationArguments有方法可以獲取可選參數(shù)及普通參數(shù)的值,參數(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++;
}
}
2. 執(zhí)行順序
多種方法對(duì)bean同時(shí)進(jìn)行控制,對(duì)應(yīng)執(zhí)行順序如下:
- 構(gòu)造函數(shù)
- @PostConstruct注解方法
- InitializingBean的afterPropertiesSet()
- @Bean或xml中標(biāo)注的初始化方法
讀者可以自行測(cè)試進(jìn)行驗(yàn)證。
3. 總結(jié)
本文介紹多種方式實(shí)現(xiàn)在Spring啟動(dòng)時(shí)實(shí)現(xiàn)自定義業(yè)務(wù)。通過(guò)對(duì)比不同方式實(shí)現(xiàn)加深對(duì)Spring的理解,掌握更多控制bean實(shí)例化過(guò)程的方式。以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java后臺(tái)實(shí)現(xiàn)瀏覽器一鍵導(dǎo)出下載zip壓縮包
這篇文章主要為大家詳細(xì)介紹了Java后臺(tái)實(shí)現(xiàn)瀏覽器一鍵導(dǎo)出下載zip壓縮包,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
Spring Boot @Async 異步任務(wù)執(zhí)行方法
本篇文章主要介紹了Spring Boot @Async 異步任務(wù)執(zhí)行方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
詳解spring cloud hystrix請(qǐng)求緩存(request cache)
這篇文章主要介紹了詳解spring cloud hystrix請(qǐng)求緩存(request cache),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Java中的Random和ThreadLocalRandom詳細(xì)解析
這篇文章主要介紹了Java中的Random和ThreadLocalRandom詳細(xì)解析,Random 類(lèi)用于生成偽隨機(jī)數(shù)的流, 該類(lèi)使用48位種子,其使用線性同余公式進(jìn)行修改,需要的朋友可以參考下2024-01-01

