使用Spring啟動時運行自定義業(yè)務
在Spring應用啟動時運行自定義業(yè)務的場景很常見,但應用不當也可能會導致一些問題。
基于Spring控制反轉(Inverse of Control)功能用戶幾乎不用干預bean實例化過程,對于自定義業(yè)務則需要控制部分流程及容器,因此值得須特別關注。
1. Spring啟動時運行自定義業(yè)務
我們不能簡單包括自定義業(yè)務在bean的構造函數(shù)或在實例化任何對象之后調用方法,這些過程不由我們控制。請看示例:
@Component public class InvalidInitExampleBean { @Autowired private Environment env; public InvalidInitExampleBean() { env.getActiveProfiles(); } }
這里嘗試在構造函數(shù)中訪問自動裝配的屬性。當調用構造函數(shù)時,Spring bean仍沒有全部初始化,因此導致NullPointerExceptions異常。下面介紹幾種方式解決此問題。
1.1 @PostConstruct 注解
@PostConstruct注解用于方法上,實現(xiàn)bean初始化后立刻執(zhí)行一次。需要注意的是,即使沒有對象注入,Spring也會執(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())); } }
上面示例可以實現(xiàn)Environment environment被安全注入,然后調用注解方法且不會出現(xiàn)空指針異常。
1.2 InitializingBean 接口
InitializingBean接口實現(xiàn)功能與上節(jié)類似。但需要實現(xiàn)接口并重寫afterPropertiesSet方法。
下面重寫前節(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)聽器
該方法可用于在Spring上下文初始化之后執(zhí)行自定義業(yè)務。因此不針對特定bean,而是等待所有bean初始化之后。應用時需要實現(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注解實現(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è)務需要。
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())); } }
既不要實現(xiàn)接口,也不要特定注解。通過注解定義Bean:
@Bean(initMethod="init") public InitMethodExampleBean initMethodExampleBean() { return new InitMethodExampleBean(); }
對應xml配置:
<bean id="initMethodExampleBean" class="com.baeldung.startup.InitMethodExampleBean" init-method="init"> </bean>
1.5 構造函數(shù)注入
如果使用構造器注入屬性,可以簡單地在構造函數(shù)中包括業(yè)務:
@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接口,重寫run方法,可以在應用啟動時Spring應用上下文實例化之后調用。
@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在相同上下文中可以定義多個,通過使用Ordered 接口或@Ordere注解確定順序。
1.7 Spring Boot ApplicationRunner
與CommandLineRunner類似,Spring Boot 也提供了ApplicationRunner接口,重寫run方法可以實現(xiàn)應用啟動時執(zhí)行自定義業(yè)務。另外其回調方法沒有使用String參數(shù),而是使用ApplicationArguments類的實例。
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í)行順序
多種方法對bean同時進行控制,對應執(zhí)行順序如下:
- 構造函數(shù)
- @PostConstruct注解方法
- InitializingBean的afterPropertiesSet()
- @Bean或xml中標注的初始化方法
讀者可以自行測試進行驗證。
3. 總結
本文介紹多種方式實現(xiàn)在Spring啟動時實現(xiàn)自定義業(yè)務。通過對比不同方式實現(xiàn)加深對Spring的理解,掌握更多控制bean實例化過程的方式。以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java Runtime類詳解_動力節(jié)點Java學院整理
Runtime類封裝了運行時的環(huán)境。每個 Java 應用程序都有一個 Runtime 類實例,使應用程序能夠與其運行的環(huán)境相連接。下面通過本文給大家分享Java Runtime類詳解,需要的朋友參考下吧2017-04-04Spring Boot緩存實戰(zhàn) Caffeine示例
本篇文章主要介紹了Spring Boot緩存實戰(zhàn) Caffeine示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02如何使用IntelliJ IDEA的HTTP Client進行接口驗證
這篇文章主要介紹了如何使用IntelliJ IDEA的HTTP Client進行接口驗證,本文給大家分享最新完美解決方案,感興趣的朋友跟隨小編一起看看吧2024-06-06