springboot2.x解決執行順序及Bean物件注入順序的問題
阿新 • • 發佈:2021-01-07
1 前言
通過指定介面,重寫指定方法,可以在Bean對應的生命週期方法中執行相應的程式
2 測試
- 本文將分析幾個Bean物件,為它們設定優先順序(通過@Order),然後再打斷點除錯,測試各種生命週期方法的執行的順序
- 在專案當中最讓人頭疼的就是bean物件不被注入的問題,通過本文,你可以很好的解決這個問題。
- 先看看本程式使用的依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.wu</groupId> <artifactId>smartport</artifactId> <version>0.0.1-SNAPSHOT</version> <name>smartport</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
2.1 Order = 1
package com.wu.smartport.controller; import com.wu.smartport.test.User; import org.springframework.beans.BeansException; import org.springframework.beans.factory.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Component @Order(1) public class TestController implements BeanNameAware,BeanFactoryAware,ApplicationContextAware,BeanDefinitionRegistryPostProcessor,InitializingBean,DisposableBean { @Autowired private User user; /** * 最先執行,需要介面BeanNameAware * * @param name */ @Override public void setBeanName(String name) { // 獲取到的是testController,即注入進Spring中的名稱 System.out.println("setBeanName:" + name); } /** * 第2個執行,需要介面BeanFactoryAware * beanFactory可以獲取其他已注入的Bean物件,但無法注入其他物件 * * @param beanFactory * @throws BeansException */ @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { } /** * 第3個執行,需要ApplicationContextAware介面 * 可以獲取到上下文物件applicationContext,允許容器通過應用程式上下文環境建立、獲取、管理bean * * @param applicationContext * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 可以通過上下文物件獲取工廠並注入物件 AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); } /** * 第4個執行,依賴於InitializingBean介面 * * @throws Exception */ @Override public void afterPropertiesSet() throws Exception { } /** * 第5個執行,依賴於BeanDefinitionRegistryPostProcessor介面 * 當完成本方法後,物件就會完成注入 * @param registry * @throws BeansException */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { } /** * 第6個執行,來源於BeanFactoryPostProcessor介面,該介面被BeanDefinitionRegistryPostProcessor介面實現了。 * 一般的Bean物件在此方法前都被注入到了容器中,如果本物件沒有獲取到,則可以在這裡獲取。 * @param beanFactory * @throws BeansException */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Object user = beanFactory.getBean("user"); } /** * 第7個執行,本方法將被多次執行,依賴於BeanPostProcessor介面 * 每注入一個物件的時候,都會呼叫該方法 * @param bean * @param beanName * @return * @throws BeansException */ public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException { return null; } /** * 第8個執行,本方法將被多次執行,依賴於BeanPostProcessor介面 * 每注入一個物件之後,都會呼叫該方法 * @param bean * @param beanName * @return * @throws BeansException */ public Object postProcessAfterInitialization(Object bean,String beanName) throws BeansException { return null; } /** * 在本物件被銷燬後執行,依賴於DisposableBean介面 * @throws Exception */ @Override public void destroy() throws Exception { } }
2.2 Order = 15
package com.wu.smartport.test; import org.springframework.beans.BeansException; import org.springframework.beans.factory.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; /** * @Author :吳用 * @Date :2021-01-05 8:40 * @Version :1.0 */ @Service @Order(15) public class User implements BeanNameAware,DisposableBean { @Value("張三") private String name; @Value("12") private int age; public User() { } public User(String name,int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * 最先執行,需要介面BeanNameAware * * @param name */ @Override public void setBeanName(String name) { System.out.println("setBeanName:" + name); } /** * 第2個執行,需要介面BeanFactoryAware * beanFactory可以獲取其他已注入的Bean物件,但無法注入其他物件 * * @param beanFactory * @throws BeansException */ @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { } /** * 第3個執行,需要ApplicationContextAware介面 * 可以獲取到上下文物件applicationContext,允許容器通過應用程式上下文環境建立、獲取、管理bean * * @param applicationContext * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { } /** * 第4個執行,依賴於InitializingBean介面 * * @throws Exception */ @Override public void afterPropertiesSet() throws Exception { } /** * 第5個執行,依賴於BeanDefinitionRegistryPostProcessor介面 * 當完成本方法後,物件就會完成注入 * @param registry * @throws BeansException */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { } /** * 第6個執行,來源於BeanFactoryPostProcessor介面,該介面被BeanDefinitionRegistryPostProcessor介面實現了。 * 一般的Bean物件在此方法前都被注入到了容器中,如果本物件沒有獲取到,則可以在這裡獲取。 * @param beanFactory * @throws BeansException */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Object user = beanFactory.getBean("user"); } /** * 第7個執行,本方法將被多次執行,依賴於BeanPostProcessor介面 * 每注入一個物件的時候,都會呼叫該方法 * @param bean * @param beanName * @return * @throws BeansException */ public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException { return null; } /** * 在本物件被銷燬後執行,依賴於DisposableBean介面 * @throws Exception */ @Override public void destroy() throws Exception { } }
2.3 Order = 30
package com.wu.smartport.test; import org.springframework.beans.BeansException; import org.springframework.beans.factory.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * @Author :吳用 * @Date :2021-01-05 9:48 * @Version :1.0 */ @Order(30) @Component public class User2 implements BeanNameAware,DisposableBean { @Value("張三") private String name; @Value("12") private int age; public User2() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * 最先執行,需要介面BeanNameAware * * @param name */ @Override public void setBeanName(String name) { System.out.println("setBeanName:" + name); } /** * 第2個執行,需要介面BeanFactoryAware * beanFactory可以獲取其他已注入的Bean物件,但無法注入其他物件 * * @param beanFactory * @throws BeansException */ @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { } /** * 第3個執行,需要ApplicationContextAware介面 * 可以獲取到上下文物件applicationContext,允許容器通過應用程式上下文環境建立、獲取、管理bean * * @param applicationContext * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { } /** * 第4個執行,依賴於InitializingBean介面 * * @throws Exception */ @Override public void afterPropertiesSet() throws Exception { } /** * 第5個執行,依賴於BeanDefinitionRegistryPostProcessor介面 * 當完成本方法後,物件就會完成注入 * @param registry * @throws BeansException */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { } /** * 第6個執行,來源於BeanFactoryPostProcessor介面,該介面被BeanDefinitionRegistryPostProcessor介面實現了。 * 一般的Bean物件在此方法前都被注入到了容器中,如果本物件沒有獲取到,則可以在這裡獲取。 * @param beanFactory * @throws BeansException */ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Object user = beanFactory.getBean("user"); } /** * 第7個執行,本方法將被多次執行,依賴於BeanPostProcessor介面 * 每注入一個物件的時候,都會呼叫該方法 * @param bean * @param beanName * @return * @throws BeansException */ public Object postProcessBeforeInitialization(Object bean,String beanName) throws BeansException { return null; } /** * 在本物件被銷燬後執行,依賴於DisposableBean介面 * @throws Exception */ @Override public void destroy() throws Exception { } }
2.4 實驗結果
- 如下圖所示
- TestController 的Order=1,最高優先順序
- User 的 Order = 15,中等優先順序
- User2 的 Order = 30,低優先順序
- 前四個方法會按照優先順序的順序先後執行,然後按優先順序順序執行postProcessBeanDefinitionRegistry方法,然後再按優先順序順序執行postProcessBeanFactory方法
- postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 方法很不錯,給了beanFactory。不僅如此,在此方法以前,所有的Bean物件已經被注入完畢了,所以如果之前你的物件沒有注入進去,你就可以在這裡通過beanFactroy獲取物件,然後把物件重新注入進去。
銷燬的順序
3 @Dependon 和 @Order
- @Dependon 和@Order 共同決定Bean物件的注入順序,
- 如果A物件 @Dependon B物件,則無論A物件和B物件的@Order的值是什麼,都以@Dependon標籤為主
- 例如A物件@Dependon(“b”),@Order(1) ,B物件@Order(15),則B物件將先於A物件注入spring容器中
4 利用BeanFactoryPostProcessor 初始化
4.1 一般初始化
- 根據上面的實驗我們知道實現BeanFactoryPostProcessor介面後,重寫void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
- 在該方法執行時,所有的bean物件都已經被注入到了spring容器中,此時可以利用觀察者模式將你需要初始化的物件進行初始化。
- 首先,需要寫一個ObserverForInit介面
package com.wu.smartport.controller; /** * 觀察者,用於初始化 * @Author :吳用 * @Date :2021-01-05 11:02 * @Version :1.0 */ public interface ObserverForInit { /** * 觀察者初始化 */ void observerInit(); }
再讓需要初始化操作的bean物件實現該介面
package com.wu.smartport.test; import com.wu.smartport.controller.ObserverForInit; import org.springframework.stereotype.Component; /** * @Author :吳用 * @Date :2021-01-05 11:11 * @Version :1.0 */ @Component public class User implements ObserverForInit { @Override public void observerInit() { System.out.println("我要進行初始化了!"); } }
然後再寫一個管理bean初始化的類 通過遍歷所有的Bean物件,然後查詢指定的介面或者自定義註解,執行相應的操作(比如初始化操作。)
package com.wu.smartport.manager; import com.wu.smartport.controller.ObserverForInit; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; import java.util.Iterator; /** * @Author :吳用 * @Date :2021-01-05 11:06 * @Version :1.0 */ @Component public class ControllerInitManager implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Iterator<String> its = beanFactory.getBeanNamesIterator(); while (its.hasNext()) { String beanName = its.next(); try { Object bean = beanFactory.getBean(beanName); analysisObject(bean); } catch (BeanCreationException e) { } catch (Exception e) { e.printStackTrace(); } } } /** * 分析bean物件 * * @param bean */ private void analysisObject(Object bean) { analysisInterface(bean); analysisAnnotation(bean); } /** * 分析介面 * * @param bean */ private void analysisInterface(Object bean) { Class<?> beanClass = bean.getClass(); // 分析Interface 和 Annotation Class<?>[] interfaces = beanClass.getInterfaces(); // 分析Class for (Class infs : interfaces) { if (infs.equals(ObserverForInit.class)) { ((ObserverForInit) bean).observerInit(); } } } /** * 分析註解 * * @param bean */ private void analysisAnnotation(Object bean) { } }
4.2 按序初始化
- 如果對初始化的順序有自己要求,可以採取如下的方法。至於4.1中的遍歷順序,讀者可以自行研究,但建議自己寫順序,否則一旦springboot框架內部有變,咱們程式內部的初始順序可能會出問題。
- 設定一個TreeSet 並給定排序策略,遍歷介面的時候,先將找到的物件放入TreeSet中排序,再遍歷完成之後再執行相應的操作
- 修改後的介面
package com.wu.smartport.controller; /** * 觀察者,用於初始化 * @Author :吳用 * @Date :2021-01-05 11:02 * @Version :1.0 */ public interface ObserverForInit { /** * 觀察者初始化 */ void observerInit(); /** * 初始化的順序,數越小,優先順序越高 * @return */ int getInitOrder(); }
bean物件
/** * @Author :吳用 * @Date :2021-01-04 22:51 * @Version :1.0 */ @Component public class PageManager implements ObserverForInit { @Override public void observerInit() { System.out.println("PageManager初始化"); } @Override public int getInitOrder() { return 1000; } }
管理初始化的類
package com.wu.smartport.manager; import com.wu.smartport.controller.ObserverForInit; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; import java.util.Comparator; import java.util.Iterator; import java.util.TreeSet; /** * @Author :吳用 * @Date :2021-01-05 11:06 * @Version :1.0 */ @Component public class ControllerInitManager implements BeanFactoryPostProcessor { private TreeSet<ObserverForInit> inits = new TreeSet<>(Comparator.comparingInt(ObserverForInit::getInitOrder)); @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Iterator<String> its = beanFactory.getBeanNamesIterator(); while (its.hasNext()) { String beanName = its.next(); try { Object bean = beanFactory.getBean(beanName); analysisObject(bean); } catch (BeanCreationException e) { } catch (Exception e) { e.printStackTrace(); } } // 遍歷完成後的操作 analysisByAfterIterator(); } /** * 分析bean物件 * * @param bean */ private void analysisObject(Object bean) { analysisInterface(bean); analysisAnnotation(bean); } /** * 遍歷之後的操作 */ private void analysisByAfterIterator() { // 按照指定的順序遍歷 // 升序遍歷,order小的先執行初始化方法 for (ObserverForInit o : inits) { o.observerInit(); } } /** * 分析介面 * * @param bean */ private void analysisInterface(Object bean) { Class<?> beanClass = bean.getClass(); // 分析Interface 和 Annotation Class<?>[] interfaces = beanClass.getInterfaces(); // 分析Class for (Class infs : interfaces) { if (infs.equals(ObserverForInit.class)) { inits.add((ObserverForInit) bean); } } } /** * 分析註解 * * @param bean */ private void analysisAnnotation(Object bean) { } }
5 基於 Springboot 實現馮諾依曼結構
- 下面講解一個騷操作
- 馮諾依曼結構:將程式和資料分別儲存在記憶體當中
- 基於這種思想,我們不光可以把資料儲存在容器當中,我們還可以把程式碼也儲存在springboot容器當中。
- 建立一個Bean物件,實現BeanFactoryPostProcessor介面,在執行到該方法的時候,大部分的物件已經被裝入了spring容器中。你可以在之後必要的地方從spring容器中提取該程式碼執行。
package com.wu.smartport.test; import com.wu.smartport.SmartportApplication; import javafx.stage.Stage; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import java.io.IOException; import java.util.ArrayList; import java.util.function.Function; import static com.wu.smartport.config.BeanInitOrder.INIT_METHODS; import static com.wu.smartport.config.BeanInitOrder.STAGE_BEAN; /** * @Author :吳用 * @Date :2021-01-05 19:27 * @Version :1.0 */ public class Manager implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // 執行方法告訴javaFx ArrayList<Function> arr; try{ // 嘗試從容器中獲取程式碼塊 Object bean = beanFactory.getBean("function"); arr = (ArrayList<Function>)bean; }catch (Exception e2){ // 如果不存在則建立程式碼塊 arr = new ArrayList<>(); } // 書寫一段程式碼 Function<Void,Void> f = aVoid -> { // 相關的業務 return null; }; arr.add(f); // 將程式碼塊注入到spring容器中 DefaultListableBeanFactory factory = (DefaultListableBeanFactory)beanFactory; factory.registerSingleton(INIT_METHODS,arr); } }
在之後一定會執行到的程式碼,比如啟動類之後,可以執行指定的程式碼段。
package com.wu.smartport; import javafx.application.Application; import javafx.stage.Stage; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import java.util.ArrayList; import java.util.function.Function; import static com.wu.smartport.config.BeanInitOrder.INIT_METHODS; import static com.wu.smartport.config.BeanInitOrder.STAGE_BEAN; /** * 主啟動類 * @author Wu_Sir */ @SpringBootApplication public class SmartportApplication{ private static volatile ConfigurableApplicationContext run; /** * 主程式入口 * * @param args */ public static void main(String[] args) { run = SpringApplication.run(SmartportApplication.class,args); // 檢視是否有要處理的方法 try{ Object bean = beanFactory.getBean("function"); ArrayList<Function> arr = (ArrayList<Function>) bean; for (int i = 0; i < arr.size(); i++) { //如果有,則執行該方法。 Function f = arr.get(i); f.apply(null); } }catch (Exception e){ e.printStackTrace(); } } }
到此這篇關於springboot2.x解決執行順序及Bean物件注入順序的問題的文章就介紹到這了,更多相關springboot注入Bean順序內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!