水稻:這兩天看了BeanDefinition和BeanFactoryPostProcessor還有BeanPostProcessor的原始碼。要不要了解一下
菜瓜:six six six,大佬請講
水稻:上次我們說SpringIOC容器是一個典型的工廠模式
- 假如我們把Spring比作一個生產模型的大工廠,那麼.class檔案就是原材料。而BeanDefinition就是建立模型的模具。不管是傳統的XML還是後面的註解,Spring在啟動的時候都會建立一個掃描器去掃描指定目錄下的.class檔案,並根據檔案的註解,實現的介面以及成員變數將其封裝一個個的BeanDefinition。
- 比較重要的屬性有id,class,建構函式封裝類,屬性封裝類,factoryMethod等
- 在物件初始化之前Spring會完成BeanDefinition物件的解析並將其裝入List容器beanDefinitionNames中,然後開始遍歷該容器並根據BeanDefinition建立物件
菜瓜:sodasinei,BeanDefinition我瞭解了。它是建立bean的模板,類似於java建立物件依賴的class一樣。那還有兩個很長的單詞是啥呢?
水稻:忽略掉後面老長的字尾,我們看BeanFactory和Bean是不是很親切。PostProcessor被翻譯成後置處理器,暫且我們把它看成是處理器就行
- BeanFactory是bean工廠,它可以獲取並修改BeanDefinition的屬性,進而影響後面建立的物件。
- Bean就是Spring的物件,這些個處理器才是真正處理bean物件的各個環節的工序,包括屬性,註解,方法
菜瓜:有了模糊的概念,不明覺厲
水稻:來,看demo
package com.vip.qc.postprocessor; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; /** * 獲取初始化好的BeanFactory,此時還未進行bean的例項化 * * @author QuCheng on 2020/6/14. */ @Component public class BeanFactoryPostProcessorT implements BeanFactoryPostProcessor { public static final String BEAN_NAME = "processorT"; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinition initializingBeanT = beanFactory.getBeanDefinition(BEAN_NAME); MutablePropertyValues propertyValues = initializingBeanT.getPropertyValues(); String pName = "a"; System.out.println("BeanFactoryPostProcessor a " + propertyValues.getPropertyValue(pName) + " -> 1"); propertyValues.addPropertyValue(pName, "1"); } } package com.vip.qc.postprocessor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; /** * @author QuCheng on 2020/6/14. */ @Component public class BeanPostProcessorT implements BeanPostProcessor { public static final String beanNameT = "processorT"; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanNameT.equals(beanName)) { ProcessorT processorT = ((ProcessorT) bean); System.out.println("BeanPostProcessor BeforeInitialization a:" + processorT.getA() + "-> 3"); processorT.setA("3"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanNameT.equals(beanName)){ ProcessorT processorT = ((ProcessorT) bean); System.out.println("BeanPostProcessor AfterInitialization a:" + processorT.getA() + "-> 4"); processorT.setA("4"); } return bean; } } package com.vip.qc.postprocessor; import org.springframework.stereotype.Component; /** * @author QuCheng on 2020/6/14. */ @Component public class ProcessorT { public ProcessorT() { System.out.println("ProcessorT 無參構造 a:" + a + "-> 2" ); a = "2"; } private String a; public String getA() { return a; } public void setA(String a) { this.a = a; } @Override public String toString() { return "ProcessorT{" + "a='" + a + '\'' + '}'; } } // 測試類 @Test public void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.vip.qc.postprocessor"); ProcessorT processorT = (ProcessorT) context.getBean("processorT"); System.out.println(processorT); } // 結果 BeanFactoryPostProcessor a null -> 1 ProcessorT 無參構造 a:null-> 2 BeanPostProcessor BeforeInitialization a:1-> 3 BeanPostProcessor AfterInitialization a:3-> 4 ProcessorT{a='4'}
-
BeanFactoryPostProcessor在物件還未初始化前可以拿到物件的BeanDefinition對其設定屬性值
- 過程中我們分別對屬性a設定了1,2,3,4的值。最後我們拿到的值為4
菜瓜:好像看懂了。BeanFactoryPostProcessor可以拿到BeanFactory物件,獲取裡面所有的BeanDefinition並可對其進行干預。BeanPostProcessor其實是在bean已經被建立完成之後進行加工操作
水稻:沒錯。這是我們自己進行干預的demo。限於篇幅有限,你可以去看一下Spring自己對於這兩個介面的實現原始碼。比較重要的推薦下面幾個
- ConfigurationClassPostProcessor 實現BeanFactoryPostProcessor子介面
- 完成對@Configuration、@Component、@ComponentScan、@Bean、@Import、@ImportSource註解的蒐集和解析
- @Bean註解會被封裝成所在Bean的BeanDefinition中的factoryMethod屬性中,單獨進行例項化
- CommonAnnotationBeanPostProcessor 實現 BeanPostProcessor
- 完成@PostConstruct@PreDestroy@Resource註解的蒐集和解析工作
- @PostConstruct會在物件初始化且屬性渲染完成後進行
- @Resource註解(參照下面)
- AutowiredAnnotationBeanPostProcessor 實現 BeanPostProcessor
- 完成@Autowired@Value註解的蒐集和解析工作
- 在物件初始化完成之後會先進行註解的蒐集,然後進行屬性渲染呼叫populateBean方法,使用策略模式呼叫實現介面對註解進行解析,有@Autowired和@Value註解會呼叫getBean方法發起對依賴屬性的注入
- AbstractAutoProxyCreator的入口類也是實現的BeanPostProcessor
菜瓜:你放心,我不會看的。這麼複雜的東西,聽著都費勁
水稻:不愧是你!沒事,有機會聊bean的生命週期的時候我們們還會說到這些東西。到時候再刷一遍