日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

springboot初始化逻辑_SpringBoot——启动初始化数据

發布時間:2023/12/15 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot初始化逻辑_SpringBoot——启动初始化数据 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在我們用 springboot 搭建項目的時候,有時候會碰到在項目啟動時初始化一些操作的需求 ,針對這種需求 spring boot為我們提供了以下幾種方案供我們選擇:

ApplicationRunner 與 CommandLineRunner 接口

Spring容器初始化時InitializingBean接口和@PostConstruct

Spring的事件機制

ApplicationRunner與CommandLineRunner

我們可以實現 ApplicationRunner 或 CommandLineRunner 接口, 這兩個接口工作方式相同,都只提供單一的run方法,該方法在SpringApplication.run(…)完成之前調用,我們先來看看這兩個接口

@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方法,接下來我們來看看具體的使用

ApplicationRunner

構造一個類實現ApplicationRunner接口

@Component

@Order(1)

public class ApplicationRunner1 implements ApplicationRunner {

@Override

public void run(ApplicationArguments args) throws Exception {

System.out.println("\u001B[32m[>>> startup ApplicationRunner1 <<

}

}

很簡單,首先要使用@Component將實現類加入到Spring容器中,如果有多個的話通過@Order(1)進行排序,然后實現其run方法實現自己的初始化數據邏輯就可以了

CommandLineRunner

對于這兩個接口而言,我們可以通過Order注解或者使用Ordered接口來指定調用順序, @Order() 中的值越小,優先級越高

@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的參數是最原始的參數,沒有進行任何處理,ApplicationRunner的參數是ApplicationArguments,是對原始參數的進一步封裝,如果有多個的話通過@Order(1)進行排序

ApplicationRunner和CommandLineRunner排序規則

通過Order指定順序

Order值相同ApplicationRunner的實現優先執行

源碼分析

從SpringApplication.run方法的第8步callRunners開始

public ConfigurableApplicationContext run(String... args) {

```

// 第八步:執行Runners

callRunners(context, applicationArguments);

```

return context;

}

private void callRunners(ApplicationContext context, ApplicationArguments args) {

List runners = new ArrayList<>();

//獲取容器中所有的ApplicationRunner的Bean實例

runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());

//獲取容器中所有的CommandLineRunner的Bean實例

runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());

//對runners集合進行排序

AnnotationAwareOrderComparator.sort(runners);

for (Object runner : new LinkedHashSet<>(runners)) {

if (runner instanceof ApplicationRunner) {

//執行ApplicationRunner的run方法

callRunner((ApplicationRunner) runner, args);

}

if (runner instanceof CommandLineRunner) {

//執行CommandLineRunner的run方法

callRunner((CommandLineRunner) runner, args);

}

}

}

很明顯,是直接從Spring容器中獲取ApplicationRunner和CommandLineRunner的實例,并調用其run方法,這也就是為什么我要使用@Component將ApplicationRunner和CommandLineRunner接口的實現類加入到Spring容器中了。

ApplicationRunner和CommandLineRunner實現類的差異點

執行優先級差異

run方法入參不一致

ApplicationRunner和CommandLineRunner實現類的相同點

調用點一樣

實現方法名一樣

InitializingBean

在spring初始化bean的時候,如果bean實現了 InitializingBean 接口,在對象的所有屬性被初始化后之后才會調用afterPropertiesSet()方法

@Component

public class InitialingzingBeanTest implements InitializingBean {

@Override

public void afterPropertiesSet() throws Exception {

System.out.println("InitializingBean..");

}

}

我們可以看出spring初始化bean肯定會在 ApplicationRunner和CommandLineRunner接口調用之前。

@PostConstruct

@Component

public class PostConstructTest {

@PostConstruct

public void postConstruct() {

System.out.println("init...");

}

}

我們可以看到,只用在方法上添加@PostConstruct注解,并將類注入到Spring容器中就可以了。我們來看看@PostConstruct注解的方法是何時執行的

在Spring初始化bean時,對bean的實例賦值時,populateBean方法下面有一個initializeBean(beanName, exposedObject, mbd)方法,這個就是用來執行用戶設定的初始化操作。我們看下方法體:

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 {

// 對特殊的 bean 處理:Aware、BeanClassLoaderAware、BeanFactoryAware

invokeAwareMethods(beanName, bean);

}

Object wrappedBean = bean;

if (mbd == null || !mbd.isSynthetic()) {

// 后處理器

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

}

try {

// 激活用戶自定義的 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;

}

}

我們看到會先執行后處理器然后執行invokeInitMethods方法,我們來看下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;

}

獲取容器中所有的后置處理器,循環調用后置處理器的postProcessBeforeInitialization方法,這里我們來看一個BeanPostProcessor

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor

implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {

public CommonAnnotationBeanPostProcessor() {

setOrder(Ordered.LOWEST_PRECEDENCE - 3);

//設置初始化參數為PostConstruct.class

setInitAnnotationType(PostConstruct.class);

setDestroyAnnotationType(PreDestroy.class);

ignoreResourceType("javax.xml.ws.WebServiceContext");

}

}

在構造器中設置了一個屬性為PostConstruct.class,再次觀察CommonAnnotationBeanPostProcessor這個類,它繼承自InitDestroyAnnotationBeanPostProcessor。InitDestroyAnnotationBeanPostProcessor顧名思義,就是在Bean初始化和銷毀的時候所作的一個前置/后置處理器。查看InitDestroyAnnotationBeanPostProcessor類下的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);

//如果有就將方法添加進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);

}

}

在這里會去判斷某方法是否有PostConstruct.class注解,如果有,則添加到init/destroy隊列中,后續一一執行。@PostConstruct注解的方法會在此時執行,我們接著來看invokeInitMethods

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory

implements AutowireCapableBeanFactory {

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)

throws Throwable {

// 是否實現 InitializingBean

// 如果實現了 InitializingBean 接口,則只掉調用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 {

// 直接調用 afterPropertiesSet()

((InitializingBean) bean).afterPropertiesSet();

}

}

if (mbd != null && bean.getClass() != NullBean.class) {

// 判斷是否指定了 init-method(),

// 如果指定了 init-method(),則再調用制定的init-method

String initMethodName = mbd.getInitMethodName();

if (StringUtils.hasLength(initMethodName) &&

!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&

!mbd.isExternallyManagedInitMethod(initMethodName)) {

// 利用反射機制執行

invokeCustomInitMethod(beanName, bean, mbd);

}

}

}

}

首先檢測當前 bean 是否實現了 InitializingBean 接口,如果實現了則調用其 afterPropertiesSet(),然后再檢查是否也指定了 init-method(),如果指定了則通過反射機制調用指定的 init-method()。

我們也可以發現@PostConstruct會在實現 InitializingBean 接口的afterPropertiesSet()方法之前執行

Spring的事件機制

基礎概念

Spring的事件驅動模型由三部分組成

事件: ApplicationEvent ,繼承自JDK的 EventObject ,所有事件都要繼承它,也就是被觀察者

事件發布者: ApplicationEventPublisher 及 ApplicationEventMulticaster 接口,使用這個接口,就可以發布事件了

事件監聽者: ApplicationListener ,繼承JDK的 EventListener ,所有監聽者都繼承它,也就是我們所說的觀察者,當然我們也可以使用注解 @EventListener ,效果是一樣的

事件

在Spring框架中,默認對ApplicationEvent事件提供了如下支持:

ContextStartedEvent:ApplicationContext啟動后觸發的事件

ContextStoppedEvent:ApplicationContext停止后觸發的事件

ContextRefreshedEvent: ApplicationContext初始化或刷新完成后觸發的事件 ;(容器初始化完成后調用,所以我們可以利用這個事件做一些初始化操作)

ContextClosedEvent:ApplicationContext關閉后觸發的事件;(如 web 容器關閉時自動會觸發spring容器的關閉,如果是普通 java 應用,需要調用ctx.registerShutdownHook();注冊虛擬機關閉時的鉤子才行)

構造一個類繼承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;

}

}

創建事件監聽者

有兩種方法可以創建監聽者,一種是直接實現ApplicationListener的接口,一種是使用注解 @EventListener , 注解是添加在監聽方法上的 ,下面的例子是直接實現的接口

@Component

public class ApplicationListenerTest implements ApplicationListener {

@Override

public void onApplicationEvent(TestEvent testEvent) {

testEvent.getMessage();

}

}

事件發布

對于事件發布,代表者是 ApplicationEventPublisher 和 ApplicationEventMulticaster ,ApplicationContext接口繼承了ApplicationEventPublisher,并在AbstractApplicationContext實現了具體代碼,實際執行是委托給ApplicationEventMulticaster(可以認為是多播)

下面是一個事件發布者的測試實例:

@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事件進行初始化操作

利用 ContextRefreshedEvent 事件進行初始化,該事件是 ApplicationContext 初始化完成后調用的事件,所以我們可以利用這個事件,對應實現一個 監聽器 ,在其 onApplicationEvent() 方法里初始化操作

@Component

public class ApplicationListenerTest implements ApplicationListener {

@Override

public void onApplicationEvent(ContextRefreshedEvent event) {

System.out.println("容器刷新完成后,我被調用了..");

}

}

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的springboot初始化逻辑_SpringBoot——启动初始化数据的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。