天天看點

beanfactorypostprocessor_用BeanPostProcessor和BeanFactoryPostProcessor修改bean的屬性BeanFactoryPostProcessorBeanPostProcessor

分享自己在Java方面的所思所想,希望你看完之後能有更多更深入的了解
beanfactorypostprocessor_用BeanPostProcessor和BeanFactoryPostProcessor修改bean的屬性BeanFactoryPostProcessorBeanPostProcessor

BeanFactoryPostProcessor和BeanPostProcessor這兩個接口都是初始化bean時對外暴露的入口之一,和Aware類似(PS:關于spring的hook可以看看Spring鈎子方法和鈎子接口的使用詳解講的蠻詳細)本文也主要是學習具體的鈎子的細節,以便于實際開發中我們能有效率,例如如何在scala中如何擷取springboot的啟動類等等,一些中間件為了監控整個系統的服務,也需要擷取到spring容器資料和狀态。

接下來具體學習和了解下BeanFactoryPostProcessor和BeanPostProcessor。

BeanFactoryPostProcessor

bean工廠的bean屬性處理容器,說通俗一些就是可以管理我們的bean工廠内所有的beandefinition(未執行個體化)資料,可以随心所欲的修改屬性。

使用方法

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor { /** * 主要是用來自定義修改持有的bean * ConfigurableListableBeanFactory 其實就是DefaultListableBeanDefinition對象 * @param beanFactory * @throws BeansException */ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("調用了自定義的BeanFactoryPostProcessor " + beanFactory); Iterator it = beanFactory.getBeanNamesIterator(); String[] names = beanFactory.getBeanDefinitionNames(); // 擷取了所有的bean名稱清單 for(int i=0; i
           

輸出結果

beanfactorypostprocessor_用BeanPostProcessor和BeanFactoryPostProcessor修改bean的屬性BeanFactoryPostProcessorBeanPostProcessor

源碼分析

毫無疑問肯定已經解析xml了,繼續看refresh函數

AbstractApplicationContext 檔案

public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // 解析xml完成,存儲在一個具體的bean工廠中 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // bean工廠的初始化操作 prepareBeanFactory(beanFactory); try { // 由子類繼承去實作該類,目前該方法為空 postProcessBeanFactory(beanFactory); // invoke 其中存在的BeanFactoryPostProcessors,也就是我們現在說的 invokeBeanFactoryPostProcessors(beanFactory); ...
           

PostProcessorRegistrationDelegate 檔案

invokeBeanFactoryPostProcessors方法的參數為bean工廠ConfigurableListableBeanFactory和目前已知的postprocessor對象,函數分為了好幾部分去處理,截取其中我們關心的部分即可(其實還包含了優先級、屬性等類似處理過程)

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // 篩選出bean工程中存在的所有實作BeanFactoryPostProcessor類的類名稱 List priorityOrderedPostProcessors = new ArrayList(); List orderedPostProcessorNames = new ArrayList(); List nonOrderedPostProcessorNames = new ArrayList(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // 已經存在了,不再處理 } else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); // 為PriorityOrdered類型 } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); // 為Ordered類型 } else { nonOrderedPostProcessorNames.add(ppName); // 這個就是我們目前需要關心的PostProcessors } } ... List nonOrderedPostProcessors = new ArrayList(); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); // 擷取自定義的BeanFactoryPostProcessor // 這裡有一點需要注意到!!!! } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
           

上述代碼中nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));中使用了getBean,起初沒注意到以為是生成具體的對象然後修改,其實不是,getBean後面還有一個參數BeanFactoryPostProcessor.class,注意看這個函數,會發現傳回的是一個抽象類,結論就是nonOrderedPostProcessors添加的不是bean執行個體,而是beandefinition,在執行個體化前,這點很重要

private static void invokeBeanFactoryPostProcessors( Collection extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { for (BeanFactoryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanFactory(beanFactory); // 調用每一個自定義的BeanFactoryPostProcessor的方法 // 在本文章中就會去調用自定義的CustomBeanFactoryPostProcessor的postProcessBeanFactory方法 }}
           

再多說一點關于上面的getBeanNamesForType函數,從名字肯定很容易了解了,根據傳遞的類型擷取容器中的beanName。了解下其内部的實作原理

DefaultListableBeanFactory 檔案的 getBeanNamesForType函數

// type:類的類型名稱// includeNonSingletons:傳回資料包含了非單例beanName// allowEagerInit: 可以提前加載初始化public String[] getBeanNamesForType(Class> type, boolean includeNonSingletons, boolean allowEagerInit) { if (!isConfigurationFrozen() || type == null || !allowEagerInit) { // 不可用緩存、類型無效、不允許提前加載初始化 // 需要擷取目前type的原始類型,繼續擷取資料 return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit); } Map, String[]> cache = (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType); String[] resolvedBeanNames = cache.get(type); // 如果緩存已經存儲了該資料,則無需再計算,直接傳回即可 if (resolvedBeanNames != null) { return resolvedBeanNames; } resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true); // 這一步就是真正的擷取資料,周遊beanDefinitionNames的每一個資料,符合要求的就會加入到傳回的清單中  if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) { cache.put(type, resolvedBeanNames); // 便于下一次擷取,加入緩存中 } return resolvedBeanNames;}
           

BeanPostProcessor

從範圍上來說,從上面的所有的bean成為了特定的bean,其次BeanFactoryPostProcessor可以在初始化前修改bean的屬性等情況,但是BeanPostProcessor隻能在初始化後(注意初始化不包括init方法)執行一些操作。

網上很多文章都說BeanPostProcessor不能修改bean屬性,其實我看來未必,當其執行個體化之後,完全可以拿到執行個體化後的對象,對對象進行一些改值操作也完全可以的

使用方法

public class Student { @Value("${name}") private String className; public Student() { System.out.println("constructor loading"); } public void init(){ System.out.println("init loading"); }}public class CustomBeanPostProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof Student){ // 如果目前的bean是Student,則列印日志 System.out.println("postProcessBeforeInitialization bean : " + beanName); } return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(bean instanceof Student){ System.out.println("postProcessAfterInitialization bean : " + beanName); } return bean; }}
           

輸出結果

beanfactorypostprocessor_用BeanPostProcessor和BeanFactoryPostProcessor修改bean的屬性BeanFactoryPostProcessorBeanPostProcessor
  • 先列印出了構造器内部執行的話,意味着這個時候執行個體化了Student類,
  • 在init方法前執行了postProcessBeforeInitialization
  • 在init方法後執行了postProcessAfterInitialization

源碼分析

入口依舊是refresh函數,在完成初始化之後,進入到finishBeanFactoryInitialization(beanFactory)執行BeanPostProcessor的相關操作,中間的流程過長,包含了getBean操作,genBean操作過于繁瑣,後續再介紹。

beanfactorypostprocessor_用BeanPostProcessor和BeanFactoryPostProcessor修改bean的屬性BeanFactoryPostProcessorBeanPostProcessor

AbstractAutowireCapableBeanFactory 檔案

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); // aware同樣是對外提供的鈎子 } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); // 這一步就是執行自定義的beanpostprocessor的before操作 } try { invokeInitMethods(beanName, wrappedBean, mbd); // 執行init方法 } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed
           

繼續閱讀