1. 程式人生 > >Spring開閉原則的表現-BeanPostProcessor擴充套件點-2

Spring開閉原則的表現-BeanPostProcessor擴充套件點-2

四、BeanPostProcessor介面及回撥方法圖


 從圖中我們可以看出一共五個介面,共十個回撥方法,即十個擴充套件點,但我們之前的文章只分析了其中八個,另外兩個稍候也會解析一下是幹什麼的。

===================================================================

===================================================================

五、五個介面十個擴充套件點解析

1、InstantiationAwareBeanPostProcessor:例項化Bean後置處理器(繼承BeanPostProcessor)

postProcessBeforeInstantiation :在例項化目標物件之前執行,可以自定義例項化邏輯,如返回一個代理物件等,3.1處執行;如果此處返回的Bean不為null將中斷後續Spring建立Bean的流程,且只執行postProcessAfterInitialization回撥方法,如當AbstractAutoProxyCreator的實現者註冊了TargetSourceCreator(建立自定義的TargetSource)將改變執行流程,不註冊TargetSourceCreator我們預設使用的是SingletonTargetSource(即AOP代理直接保證目標物件),此處我們還可以使用如ThreadLocalTargetSource(執行緒繫結的Bean)、CommonsPoolTargetSource(例項池的Bean)等等,大家可以去spring官方文件瞭解TargetSource詳情;

postProcessAfterInitialization : Bean例項化完畢後執行的後處理操作,所有初始化邏輯、裝配邏輯之前執行,如果返回false將阻止其他的InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation的執行,(3.2和(9.1處執行;在此處可以執行一些初始化邏輯或依賴裝配邏輯;

postProcessPropertyValues :完成其他定製的一些依賴注入和依賴檢查等,如AutowiredAnnotationBeanPostProcessor執行@Autowired註解注入,CommonAnnotationBeanPostProcessor執行@Resource等註解的注入,PersistenceAnnotationBeanPostProcessor執行@ PersistenceContext等JPA註解的注入,RequiredAnnotationBeanPostProcessor執行@ Required註解的檢查等等,

9.3處執行;

2、MergedBeanDefinitionPostProcessor:合併Bean定義後置處理器         (繼承BeanPostProcessor)

postProcessMergedBeanDefinition:執行Bean定義的合併,在7.1處執行,且在例項化完Bean之後執行;

3、SmartInstantiationAwareBeanPostProcessor:智慧例項化Bean後置處理器(繼承InstantiationAwareBeanPostProcessor)

predictBeanType:預測Bean的型別,返回第一個預測成功的Class型別,如果不能預測返回null;當你呼叫BeanFactory.getType(name)時當通過Bean定義無法得到Bean型別資訊時就呼叫該回調方法來決定型別資訊;BeanFactory.isTypeMatch(name, targetType)用於檢測給定名字的Bean是否匹配目標型別(如在依賴注入時需要使用);

determineCandidateConstructors:檢測Bean的構造器,可以檢測出多個候選構造器,再有相應的策略決定使用哪一個,如AutowiredAnnotationBeanPostProcessor實現將自動掃描通過@Autowired/@Value註解的構造器從而可以完成構造器注入,請參考【第十二章】零配置 之12.2 註解實現Bean依賴注入 ——跟我學spring3 ,6.2.2.1處執行;

getEarlyBeanReference:當正在建立A時,A依賴B,此時通過(8將A作為ObjectFactory放入單例工廠中進行early expose,此處B需要引用A,但A正在建立,從單例工廠拿到ObjectFactory(其通過getEarlyBeanReference獲取及早暴露Bean),從而允許迴圈依賴,此時AspectJAwareAdvisorAutoProxyCreator(完成xml風格的AOP配置(<aop:config>)將目標物件(A)包裝到AOP代理物件)或AnnotationAwareAspectJAutoProxyCreator(完成@Aspectj註解風格(<aop:aspectj-autoproxy> @Aspect)將目標物件(A)包裝到AOP代理物件),其返回值將替代原始的Bean物件,即此時通過early reference能得到正確的代理物件,8.1處實施;如果此處執行了,10.3.3處的AspectJAwareAdvisorAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreatorpostProcessAfterInitialization將不執行,即這兩個回撥方法是二選一的;

4、BeanPostProcessor:Bean後置處理器

postProcessBeforeInitialization:例項化、依賴注入完畢,在呼叫顯示的初始化之前完成一些定製的初始化任務,如BeanValidationPostProcessor完成JSR-303 @Valid註解Bean驗證,InitDestroyAnnotationBeanPostProcessor完成@PostConstruct註解的初始化方法呼叫,ApplicationContextAwareProcessor完成一些Aware介面的注入(如EnvironmentAware、ResourceLoaderAware、ApplicationContextAware),其返回值將替代原始的Bean物件;10.2處執行;

postProcessAfterInitialization:例項化、依賴注入、初始化完畢時執行,如AspectJAwareAdvisorAutoProxyCreator(完成xml風格的AOP配置(<aop:config>)的目標物件包裝到AOP代理物件)、AnnotationAwareAspectJAutoProxyCreator(完成@Aspectj註解風格(<aop:aspectj-autoproxy> @Aspect)的AOP配置的目標物件包裝到AOP代理物件),其返回值將替代原始的Bean物件;10.3.3處執行;此處需要參考getEarlyBeanReference

5、DestructionAwareBeanPostProcessor:銷燬Bean後置處理器(繼承BeanPostProcessor)

postProcessBeforeDestruction:銷燬後處理回撥方法,該回調只能應用到單例Bean,如InitDestroyAnnotationBeanPostProcessor完成@PreDestroy註解的銷燬方法呼叫;12.1.1處執行。

===================================================================

===================================================================

六、內建的一些BeanPostProcessor


 部分內建的BeanPostProcessor

此圖只有內建的一部分。

1、ApplicationContextAwareProcessor

容器啟動時會自動註冊。注入那些實現ApplicationContextAware、MessageSourceAware、ResourceLoaderAware、EnvironmentAware、

EmbeddedValueResolverAware、ApplicationEventPublisherAware標識介面的Bean需要的相應例項,在postProcessBeforeInitialization回撥方法中進行實施,即10.2處實施。

2、CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor繼承InitDestroyAnnotationBeanPostProcessor,當在配置檔案有<context:annotation-config>或<context:component-scan>會自動註冊。

提供對JSR-250規範註解的支援@javax.annotation.Resource、@javax.annotation.PostConstruct和@javax.annotation.PreDestroy等的支援。

2.1、通過@Resource註解進行依賴注入:

    postProcessPropertyValues:通過此回撥進行@Resource註解的依賴注入;9.3處實施;

2.2、用於執行@PostConstruct 和@PreDestroy 註解的初始化和銷燬方法的擴充套件點:

    postProcessBeforeInitialization()將會呼叫bean的@PostConstruct方法;10.2處實施;

    postProcessBeforeDestruction()將會呼叫單例 Bean的@PreDestroy方法(此回撥方法會在容器銷燬時呼叫),12.1.1處實施。

3、AutowiredAnnotationBeanPostProcessor

當在配置檔案有<context:annotation-config>或<context:component-scan>會自動註冊。

提供對JSR-330規範註解的支援和Spring自帶註解的支援。

3.1、Spring自帶註解的依賴注入支援,@Autowired和@Value:

    determineCandidateConstructors :決定候選構造器;詳見【12.2中的構造器注入】;6.2.2.1處實施;

postProcessPropertyValues :進行依賴注入;詳見【12.2中的欄位注入和方法引數注入】;9.3處實施;

3.2、對JSR-330規範註解的依賴注入支援,@Inject:

    同2.1類似只是查詢使用的註解不一樣;

4、RequiredAnnotationBeanPostProcessor

當在配置檔案有<context:annotation-config>或<context:component-scan>會自動註冊。

4.1、提供對@ Required註解的方法進行依賴檢查支援:

    postProcessPropertyValues:如果檢測到沒有進行依賴注入時丟擲BeanInitializationException異常;9.3處實施;

5、PersistenceAnnotationBeanPostProcessor

當在配置檔案有<context:annotation-config>或<context:component-scan>會自動註冊。

5.1、通過對JPA @ javax.persistence.PersistenceUnit和@ javax.persistence.PersistenceContext註解進行依賴注入的支援;

postProcessPropertyValues : 根據@PersistenceUnit/@PersistenceContext進行EntityManagerFactory和EntityManager的支援;

6、AbstractAutoProxyCreator

AspectJAwareAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator都是繼承AbstractAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator提供對(<aop:config>)宣告式AOP的支援,AnnotationAwareAspectJAutoProxyCreator提供對(<aop:aspectj-autoproxy>)註解式(@AspectJ)AOP的支援,因此只需要分析AbstractAutoProxyCreator即可。

當使用<aop:config>配置時自動註冊AspectJAwareAdvisorAutoProxyCreator,而使用<aop:aspectj-autoproxy>時會自動註冊AnnotationAwareAspectJAutoProxyCreator。

6.1predictBeanType:預測Bean的型別,如果目標物件被AOP代理物件包裝,此處將返回AOP代理物件的型別;

Java程式碼  收藏程式碼
  1. public Class<?> predictBeanType(Class<?> beanClass, String beanName) {  
  2.         Object cacheKey = getCacheKey(beanClass, beanName);  
  3.         return this.proxyTypes.get(cacheKey); //獲取代理物件型別,可能返回null  
  4. }  

6.2postProcessBeforeInstantiation

Java程式碼  收藏程式碼
  1. public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {  
  2.     //1、得到一個快取的唯一key(根據beanClass和beanName生成唯一key)  
  3.     Object cacheKey = getCacheKey(beanClass, beanName);  
  4.     //2、如果當前targetSourcedBeans(通過自定義TargetSourceCreator建立的TargetSource)不包含cacheKey  
  5.     if (!this.targetSourcedBeans.contains(cacheKey)) {  
  6.         //2.1、advisedBeans(已經被增強的Bean,即AOP代理物件)中包含當前cacheKey或nonAdvisedBeans(不應該被增強的Bean)中包含當前cacheKey 返回null,即走Spring預設流程  
  7.         if (this.advisedBeans.contains(cacheKey) || this.nonAdvisedBeans.contains(cacheKey)) {  
  8.             return null;  
  9.         }  
  10.         //2.2、如果是基礎設施類(如Advisor、Advice、AopInfrastructureBean的實現)不進行處理  
  11.         //2.2、shouldSkip 預設false,可以生成子類覆蓋,如AspectJAwareAdvisorAutoProxyCreator覆蓋    (if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) return true;  即如果是自己就跳過)  
  12.         if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {  
  13.             this.nonAdvisedBeans.add(cacheKey);//在不能增強的Bean列表快取當前cacheKey  
  14.             return null;  
  15.         }  
  16.     }  
  17.     //3、開始建立AOP代理物件  
  18.     //3.1、配置自定義的TargetSourceCreator進行TargetSource建立  
  19.     TargetSource targetSource = getCustomTargetSource(beanClass, beanName);  
  20.     if (targetSource != null) {  
  21.         //3.2、如果targetSource不為null 新增到targetSourcedBeans快取,並建立AOP代理物件  
  22.         this.targetSourcedBeans.add(beanName);  
  23.         // specificInterceptors即增強(包括前置增強、後置增強等等)  
  24.         Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);  
  25.         //3.3、建立代理物件  
  26.         Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);  
  27.         //3.4、將代理型別放入proxyTypes從而允許後續的predictBeanType()呼叫獲取  
  28.         this.proxyTypes.put(cacheKey, proxy.getClass());  
  29.         return proxy;  
  30.     }  
  31.     return null;  
  32. }  

  從如上程式碼可以看出,當我們配置TargetSourceCreator進行自定義TargetSource建立時,會建立代理物件並中斷預設Spring建立流程。

6.3getEarlyBeanReference

Java程式碼  收藏程式碼
  1. //獲取early Bean引用(只有單例Bean才能回撥該方法)  
  2. public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {  
  3.     Object cacheKey = getCacheKey(bean.getClass(), beanName);  
  4.     //1、將cacheKey新增到earlyProxyReferences快取,從而避免多次重複建立  
  5.     this.earlyProxyReferences.add(cacheKey);  
  6.     //2、包裝目標物件到AOP代理物件(如果需要)  
  7.     return wrapIfNecessary(bean, beanName, cacheKey);  
  8. }  
 

6.4postProcessAfterInitialization

Java程式碼  收藏程式碼
  1. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
  2.     if (bean != null) {  
  3.         Object cacheKey = getCacheKey(bean.getClass(), beanName);  
  4.         //1、如果之前呼叫過getEarlyBeanReference獲取包裝目標物件到AOP代理物件(如果需要),則不再執行  
  5.         if (!this.earlyProxyReferences.contains(cacheKey)) {  
  6.             //2、包裝目標物件到AOP代理物件(如果需要)  
  7.             return wrapIfNecessary(bean, beanName, cacheKey);  
  8.         }  
  9.     }  
  10.     return bean;  
  11. }  
Java程式碼  收藏程式碼
  1. protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {  
  2.     if (this.targetSourcedBeans.contains(beanName)) {//通過TargetSourceCreator進行自定義TargetSource不需要包裝  
  3.         return bean;  
  4.     }  
  5.     if (this.nonAdvisedBeans.contains(cacheKey)) {//不應該被增強物件不需要包裝  
  6.         return bean;  
  7.     }  
  8.     if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {//基礎設施/應該skip的不需要保證  
  9.         this.nonAdvisedBeans.add(cacheKey);  
  10.         return bean;  
  11.     }  
  12.     // 如果有增強就執行包裝目標物件到代理物件  
  13.     Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);  
  14.     if (specificInterceptors != DO_NOT_PROXY) {  
  15.         this.advisedBeans.add(cacheKey);//將cacheKey新增到已經被增強列表,防止多次增強  
  16.         Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));//建立代理物件  
  17.         this.proxyTypes.put(cacheKey, proxy.getClass());//快取代理型別  
  18.         return proxy;  
  19.     }  
  20.     this.nonAdvisedBeans.add(cacheKey);  
  21.     return bean;  
  22. }  
  

從如上流程可以看出 getEarlyBeanReferencepostProcessAfterInitialization是二者選一的,而且單例Bean目標物件只能被增強一次,而原型Bean目標物件可能被包裝多次。

7、BeanValidationPostProcessor

預設不自動註冊,Spring3.0開始支援。

提供對JSR-303驗證規範支援。

根據afterInitialization是false/true決定呼叫postProcessBeforeInitialization或postProcessAfterInitialization來通過JSR-303規範驗證Bean,預設false。

8、MethodValidationPostProcessor

Spring3.1開始支援,且只支援Hibernate Validator 4.2及更高版本,從Spring 3.2起可能將採取自動檢測Bean Validation 1.1相容的提供商且自動註冊(Bean Validation 1.1 (JSR-349)正處於草案階段,它將提供方法級別的驗證,提供對方法級別的驗證),目前預設不自動註冊。

提供對方法引數/方法返回值的進行驗證(即前置條件/後置條件的支援),通過JSR-303註解驗證,使用方式如:

public @NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2)

預設只對@org.springframework.validation.annotation.Validated註解的Bean進行驗證,我們可以修改validatedAnnotationType為其他註解型別來支援其他註解驗證。而且目前只支援Hibernate Validator實現,在未來版本可能支援其他實現。

有了這東西之後我們就不需要在進行如Assert.assertNotNull()這種前置條件/後置條件的判斷了。

9、ScheduledAnnotationBeanPostProcessor

當配置檔案中有<task:annotation-driven>自動註冊或@EnableScheduling自動註冊。

提供對註解@Scheduled任務排程的支援。

postProcessAfterInitialization:通過查詢Bean物件類上的@Scheduled註解來建立ScheduledMethodRunnable物件並註冊任務排程方法(僅返回值為void且方法是無形式引數的才可以)。

可參考Spring官方文件的任務排程章節學習@Scheduled註解任務排程。

10、AsyncAnnotationBeanPostProcessor

當配置檔案中有<task:annotation-driven>自動註冊或@EnableAsync自動註冊。

提供對@ Async和EJB3.1的@javax.ejb.Asynchronous註解的非同步呼叫支援。

postProcessAfterInitialization:通過ProxyFactory建立目標物件的代理物件,預設使用AsyncAnnotationAdvisor(內部使用AsyncExecutionInterceptor 通過AsyncTaskExecutor(繼承TaskExecutor)通過submit提交非同步任務)。

可參考Spring官方文件的非同步呼叫章節學習@Async註解非同步呼叫。

11、ServletContextAwareProcessor

在使用Web容器時自動註冊。

類似於ApplicationContextAwareProcessor,當你的Bean 實現了ServletContextAware/ ServletConfigAware會自動呼叫回撥方法注入ServletContext/ ServletConfig。

===================================================================

===================================================================

七、BeanPostProcessor如何註冊

1、如ApplicationContextAwareProcessor會在ApplicationContext容器啟動時自動註冊,而CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor會在當你使用<context:annotation-config>或<context:component-scan>配置時自動註冊。

2、只要將BeanPostProcessor註冊到容器中,Spring會在啟動時自動獲取並註冊。

===================================================================

===================================================================

八、BeanPostProcessor的執行順序

1、如果使用BeanFactory實現,非ApplicationContext實現,BeanPostProcessor執行順序就是新增順序。

2、如果使用的是AbstractApplicationContext(實現了ApplicationContext)的實現,則通過如下規則指定順序。

2.1PriorityOrdered(繼承了Ordered),實現了該介面的BeanPostProcessor會在第一個順序註冊,標識高優先順序順序,即比實現Ordered的具有更高的優先順序;

2.2Ordered,實現了該介面的BeanPostProcessor會第二個順序註冊;

int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;//最高優先順序

int LOWEST_PRECEDENCE = Integer.MAX_VALUE;//最低優先順序

即數字越小優先順序越高,數字越大優先順序越低,如0(高優先順序)——1000(低優先順序)

2.3、無序的,沒有實現Ordered/ PriorityOrdered的會在第三個順序註冊;

2.4、內部Bean後處理器,實現了MergedBeanDefinitionPostProcessor介面的是內部Bean PostProcessor,將在最後且無序註冊。

3、接下來我們看看內建的BeanPostProcessor執行順序

//1註冊實現了PriorityOrdered介面的BeanPostProcessor

//2註冊實現了Ordered介面的BeanPostProcessor

AbstractAutoProxyCreator              實現了Ordered,order = Ordered.LOWEST_PRECEDENCE

MethodValidationPostProcessor          實現了Ordered,LOWEST_PRECEDENCE

ScheduledAnnotationBeanPostProcessor   實現了Ordered,LOWEST_PRECEDENCE

AsyncAnnotationBeanPostProcessor      實現了Ordered,order = Ordered.LOWEST_PRECEDENCE

//3註冊無實現任何介面的BeanPostProcessor

BeanValidationPostProcessor            無序

ApplicationContextAwareProcessor       無序

ServletContextAwareProcessor          無序

//3 註冊實現了MergedBeanDefinitionPostProcessor介面的BeanPostProcessor,且按照實現了Ordered的順序進行註冊,沒有實現Ordered的預設為Ordered.LOWEST_PRECEDENCE

PersistenceAnnotationBeanPostProcessor  實現了PriorityOrdered,Ordered.LOWEST_PRECEDENCE - 4

AutowiredAnnotationBeanPostProcessor   實現了PriorityOrdered,order = Ordered.LOWEST_PRECEDENCE - 2

RequiredAnnotationBeanPostProcessor    實現了PriorityOrdered,order = Ordered.LOWEST_PRECEDENCE - 1

CommonAnnotationBeanPostProcessor    實現了PriorityOrdered,Ordered.LOWEST_PRECEDENCE

從上到下順序執行,如果order相同則我們應該認為同序(誰先執行不確定,其執行順序根據註冊順序決定)。

===================================================================

===================================================================

九、完成Spring事務處理時自我呼叫的解決方案及一些實現方式的分析分析

分析:


問題出現在5和9處:

5、使用步驟1處註冊的SingletonFactory(ObjectFactory.getObject() 使用AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference獲取迴圈引用Bean),因此此處將返回A目標物件的代理物件;

9、此處呼叫AnnotationAwareAspectJAutoProxyCreator的postProcessAfterInitialization,但發現之前呼叫過AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference獲取代理物件,此處不再建立代理物件,而是直接返回目標物件,因此使用InjectBeanSelfProcessor不能注入代理物件;但此時的Spring容器中的A已經是代理物件了,因此我使用了從上下文重新獲取A代理物件的方式注入(context.getBean(beanName))。

此處的getEarlyBeanReferencepostProcessAfterInitialization為什麼是二者選一的請參考之前介紹的AbstractAutoProxyCreator。

到此問題我們分析完畢,實際專案中的迴圈依賴應該儘量避免,這違反了“無環依賴原則”。

下一篇我將介紹一些內建BeanPostProcessor的使用和自定義一些自己的BeanPostProcessor來更好的理解這些擴充套件點。