javascript
springboot初始化逻辑_SpringBoot——启动初始化数据
前言
在我們用 springboot 搭建項(xiàng)目的時(shí)候,有時(shí)候會(huì)碰到在項(xiàng)目啟動(dòng)時(shí)初始化一些操作的需求 ,針對(duì)這種需求 spring boot為我們提供了以下幾種方案供我們選擇:
ApplicationRunner 與 CommandLineRunner 接口
Spring容器初始化時(shí)InitializingBean接口和@PostConstruct
Spring的事件機(jī)制
ApplicationRunner與CommandLineRunner
我們可以實(shí)現(xiàn) ApplicationRunner 或 CommandLineRunner 接口, 這兩個(gè)接口工作方式相同,都只提供單一的run方法,該方法在SpringApplication.run(…)完成之前調(diào)用,我們先來(lái)看看這兩個(gè)接口
@FunctionalInterface
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
@FunctionalInterface
public interface ApplicationRunner {
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}
都只提供單一的run方法,接下來(lái)我們來(lái)看看具體的使用
ApplicationRunner
構(gòu)造一個(gè)類(lèi)實(shí)現(xiàn)ApplicationRunner接口
@Component
@Order(1)
public class ApplicationRunner1 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("\u001B[32m[>>> startup ApplicationRunner1 <<
}
}
很簡(jiǎn)單,首先要使用@Component將實(shí)現(xiàn)類(lèi)加入到Spring容器中,如果有多個(gè)的話(huà)通過(guò)@Order(1)進(jìn)行排序,然后實(shí)現(xiàn)其run方法實(shí)現(xiàn)自己的初始化數(shù)據(jù)邏輯就可以了
CommandLineRunner
對(duì)于這兩個(gè)接口而言,我們可以通過(guò)Order注解或者使用Ordered接口來(lái)指定調(diào)用順序, @Order() 中的值越小,優(yōu)先級(jí)越高
@Component
@Order(1)
public class CommandLineRunner1 implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("\u001B[32m[>>> startup runner1 <<
}
}
同樣需要加入到Spring容器中,CommandLineRunner的參數(shù)是最原始的參數(shù),沒(méi)有進(jìn)行任何處理,ApplicationRunner的參數(shù)是ApplicationArguments,是對(duì)原始參數(shù)的進(jìn)一步封裝,如果有多個(gè)的話(huà)通過(guò)@Order(1)進(jìn)行排序
ApplicationRunner和CommandLineRunner排序規(guī)則
通過(guò)Order指定順序
Order值相同ApplicationRunner的實(shí)現(xiàn)優(yōu)先執(zhí)行
源碼分析
從SpringApplication.run方法的第8步callRunners開(kāi)始
public ConfigurableApplicationContext run(String... args) {
```
// 第八步:執(zhí)行Runners
callRunners(context, applicationArguments);
```
return context;
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List runners = new ArrayList<>();
//獲取容器中所有的ApplicationRunner的Bean實(shí)例
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
//獲取容器中所有的CommandLineRunner的Bean實(shí)例
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
//對(duì)runners集合進(jìn)行排序
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
//執(zhí)行ApplicationRunner的run方法
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
//執(zhí)行CommandLineRunner的run方法
callRunner((CommandLineRunner) runner, args);
}
}
}
很明顯,是直接從Spring容器中獲取ApplicationRunner和CommandLineRunner的實(shí)例,并調(diào)用其run方法,這也就是為什么我要使用@Component將ApplicationRunner和CommandLineRunner接口的實(shí)現(xiàn)類(lèi)加入到Spring容器中了。
ApplicationRunner和CommandLineRunner實(shí)現(xiàn)類(lèi)的差異點(diǎn)
執(zhí)行優(yōu)先級(jí)差異
run方法入?yún)⒉灰恢?/p>
ApplicationRunner和CommandLineRunner實(shí)現(xiàn)類(lèi)的相同點(diǎn)
調(diào)用點(diǎn)一樣
實(shí)現(xiàn)方法名一樣
InitializingBean
在spring初始化bean的時(shí)候,如果bean實(shí)現(xiàn)了 InitializingBean 接口,在對(duì)象的所有屬性被初始化后之后才會(huì)調(diào)用afterPropertiesSet()方法
@Component
public class InitialingzingBeanTest implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean..");
}
}
我們可以看出spring初始化bean肯定會(huì)在 ApplicationRunner和CommandLineRunner接口調(diào)用之前。
@PostConstruct
@Component
public class PostConstructTest {
@PostConstruct
public void postConstruct() {
System.out.println("init...");
}
}
我們可以看到,只用在方法上添加@PostConstruct注解,并將類(lèi)注入到Spring容器中就可以了。我們來(lái)看看@PostConstruct注解的方法是何時(shí)執(zhí)行的
在Spring初始化bean時(shí),對(duì)bean的實(shí)例賦值時(shí),populateBean方法下面有一個(gè)initializeBean(beanName, exposedObject, mbd)方法,這個(gè)就是用來(lái)執(zhí)行用戶(hù)設(shè)定的初始化操作。我們看下方法體:
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction) () -> {
// 激活 Aware 方法
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
// 對(duì)特殊的 bean 處理:Aware、BeanClassLoaderAware、BeanFactoryAware
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 后處理器
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 激活用戶(hù)自定義的 init 方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 后處理器
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
}
我們看到會(huì)先執(zhí)行后處理器然后執(zhí)行invokeInitMethods方法,我們來(lái)看下applyBeanPostProcessorsBeforeInitialization
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
獲取容器中所有的后置處理器,循環(huán)調(diào)用后置處理器的postProcessBeforeInitialization方法,這里我們來(lái)看一個(gè)BeanPostProcessor
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
//設(shè)置初始化參數(shù)為PostConstruct.class
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}
}
在構(gòu)造器中設(shè)置了一個(gè)屬性為PostConstruct.class,再次觀察CommonAnnotationBeanPostProcessor這個(gè)類(lèi),它繼承自InitDestroyAnnotationBeanPostProcessor。InitDestroyAnnotationBeanPostProcessor顧名思義,就是在Bean初始化和銷(xiāo)毀的時(shí)候所作的一個(gè)前置/后置處理器。查看InitDestroyAnnotationBeanPostProcessor類(lèi)下的postProcessBeforeInitialization方法:
public class InitDestroyAnnotationBeanPostProcessor
implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
private LifecycleMetadata findLifecycleMetadata(Class> clazz) {
if (this.lifecycleMetadataCache == null) {
// Happens after deserialization, during destruction...
return buildLifecycleMetadata(clazz);
}
// Quick check on the concurrent map first, with minimal locking.
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.lifecycleMetadataCache) {
metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
metadata = buildLifecycleMetadata(clazz);
this.lifecycleMetadataCache.put(clazz, metadata);
}
return metadata;
}
}
return metadata;
}
private LifecycleMetadata buildLifecycleMetadata(final Class> clazz) {
List initMethods = new ArrayList<>();
List destroyMethods = new ArrayList<>();
Class> targetClass = clazz;
do {
final List currInitMethods = new ArrayList<>();
final List currDestroyMethods = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
//判斷clazz中的methon是否有initAnnotationType注解,也就是PostConstruct.class注解
LifecycleElement element = new LifecycleElement(method);
//如果有就將方法添加進(jìn)LifecycleMetadata中
currInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
//判斷clazz中的methon是否有destroyAnnotationType注解
currDestroyMethods.add(new LifecycleElement(method));
if (logger.isTraceEnabled()) {
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}
}
在這里會(huì)去判斷某方法是否有PostConstruct.class注解,如果有,則添加到init/destroy隊(duì)列中,后續(xù)一一執(zhí)行。@PostConstruct注解的方法會(huì)在此時(shí)執(zhí)行,我們接著來(lái)看invokeInitMethods
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 是否實(shí)現(xiàn) InitializingBean
// 如果實(shí)現(xiàn)了 InitializingBean 接口,則只掉調(diào)用bean的 afterPropertiesSet()
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 直接調(diào)用 afterPropertiesSet()
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
// 判斷是否指定了 init-method(),
// 如果指定了 init-method(),則再調(diào)用制定的init-method
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 利用反射機(jī)制執(zhí)行
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
}
首先檢測(cè)當(dāng)前 bean 是否實(shí)現(xiàn)了 InitializingBean 接口,如果實(shí)現(xiàn)了則調(diào)用其 afterPropertiesSet(),然后再檢查是否也指定了 init-method(),如果指定了則通過(guò)反射機(jī)制調(diào)用指定的 init-method()。
我們也可以發(fā)現(xiàn)@PostConstruct會(huì)在實(shí)現(xiàn) InitializingBean 接口的afterPropertiesSet()方法之前執(zhí)行
Spring的事件機(jī)制
基礎(chǔ)概念
Spring的事件驅(qū)動(dòng)模型由三部分組成
事件: ApplicationEvent ,繼承自JDK的 EventObject ,所有事件都要繼承它,也就是被觀察者
事件發(fā)布者: ApplicationEventPublisher 及 ApplicationEventMulticaster 接口,使用這個(gè)接口,就可以發(fā)布事件了
事件監(jiān)聽(tīng)者: ApplicationListener ,繼承JDK的 EventListener ,所有監(jiān)聽(tīng)者都繼承它,也就是我們所說(shuō)的觀察者,當(dāng)然我們也可以使用注解 @EventListener ,效果是一樣的
事件
在Spring框架中,默認(rèn)對(duì)ApplicationEvent事件提供了如下支持:
ContextStartedEvent:ApplicationContext啟動(dòng)后觸發(fā)的事件
ContextStoppedEvent:ApplicationContext停止后觸發(fā)的事件
ContextRefreshedEvent: ApplicationContext初始化或刷新完成后觸發(fā)的事件 ;(容器初始化完成后調(diào)用,所以我們可以利用這個(gè)事件做一些初始化操作)
ContextClosedEvent:ApplicationContext關(guān)閉后觸發(fā)的事件;(如 web 容器關(guān)閉時(shí)自動(dòng)會(huì)觸發(fā)spring容器的關(guān)閉,如果是普通 java 應(yīng)用,需要調(diào)用ctx.registerShutdownHook();注冊(cè)虛擬機(jī)關(guān)閉時(shí)的鉤子才行)
構(gòu)造一個(gè)類(lèi)繼承ApplicationEvent
public class TestEvent extends ApplicationEvent {
private String message;
public TestEvent(Object source) {
super(source);
}
public void getMessage() {
System.out.println(message);
}
public void setMessage(String message) {
this.message = message;
}
}
創(chuàng)建事件監(jiān)聽(tīng)者
有兩種方法可以創(chuàng)建監(jiān)聽(tīng)者,一種是直接實(shí)現(xiàn)ApplicationListener的接口,一種是使用注解 @EventListener , 注解是添加在監(jiān)聽(tīng)方法上的 ,下面的例子是直接實(shí)現(xiàn)的接口
@Component
public class ApplicationListenerTest implements ApplicationListener {
@Override
public void onApplicationEvent(TestEvent testEvent) {
testEvent.getMessage();
}
}
事件發(fā)布
對(duì)于事件發(fā)布,代表者是 ApplicationEventPublisher 和 ApplicationEventMulticaster ,ApplicationContext接口繼承了ApplicationEventPublisher,并在AbstractApplicationContext實(shí)現(xiàn)了具體代碼,實(shí)際執(zhí)行是委托給ApplicationEventMulticaster(可以認(rèn)為是多播)
下面是一個(gè)事件發(fā)布者的測(cè)試實(shí)例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class EventTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void publishTest() {
TestEvent testEvent = new TestEvent("");
testEvent.setMessage("hello world");
applicationContext.publishEvent(testEvent);
}
}
利用ContextRefreshedEvent事件進(jìn)行初始化操作
利用 ContextRefreshedEvent 事件進(jìn)行初始化,該事件是 ApplicationContext 初始化完成后調(diào)用的事件,所以我們可以利用這個(gè)事件,對(duì)應(yīng)實(shí)現(xiàn)一個(gè) 監(jiān)聽(tīng)器 ,在其 onApplicationEvent() 方法里初始化操作
@Component
public class ApplicationListenerTest implements ApplicationListener {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("容器刷新完成后,我被調(diào)用了..");
}
}
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的springboot初始化逻辑_SpringBoot——启动初始化数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 多头头寸和空头头寸什么
- 下一篇: yum 升级curl_CentOS 6/