你知道Spring是怎麼將AOP應用到Bean的生命週期中的嗎?

聊一聊Spring是怎麼將AOP應用到Bean的生命週期中的?html

本系列文章:程序員

據說你還沒學Spring就被源碼編譯勸退了?30+張圖帶你玩轉Spring編譯web

讀源碼,咱們能夠從第一行讀起面試

你知道Spring是怎麼解析配置類的嗎?正則表達式

配置類爲何要添加@Configuration註解?spring

談談Spring中的對象跟Bean,你知道Spring怎麼建立對象的嗎?微信

這篇文章,咱們來談一談Spring中的屬性注入 app

Spring中AOP相關的API及源碼解析,原來AOP是這樣子的編輯器

推薦閱讀:源碼分析

Spring官網閱讀 | 總結篇

Spring雜談

本系列文章將會帶你一行行的將Spring的源碼吃透,推薦閱讀的文章是閱讀源碼的基礎!

前言

在上篇文章中(Spring中AOP相關的API及源碼解析,原來AOP是這樣子的)咱們已經分析過了AOP的實現的源碼,那麼Spring是如何將AOP應用到Bean的生命週期的呢?這篇文章就帶着你們來探究下這個問題。本文咱們要分析的代碼仍是位於org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean這個方法中,在《咱們來談一談Spring中的屬性注入 》這篇文章中,咱們已經分析過了populateBean這個方法,

image-20200703202825887
image-20200703202825887

因此本文咱們接着來看看initializeBean這個方法,它主要乾了這麼幾件事

  1. 執行 Aware接口中的方法
  2. 執行 生命週期回調方法
  3. 完成 AOP代理

對應源碼以下:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
 if (System.getSecurityManager() != null) {  AccessController.doPrivileged((PrivilegedAction<Object>) () -> {  invokeAwareMethods(beanName, bean);  return null;  }, getAccessControlContext());  }  else {   // 執行Aware接口中的方法  invokeAwareMethods(beanName, bean);  }   Object wrappedBean = bean;  if (mbd == null || !mbd.isSynthetic()) {   // 調用InitDestroyAnnotationBeanPostProcessor  // 的postProcessBeforeInitialization方法  // 處理@PostContructor註解標註的方法  // 另外有一部分aware方法也是在這裏調用的  wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);  }   try {  // 若是實現了InitializingBean,會調用afterPropertiesSet方法  // 若是XML中配置了init-method屬性,會調用對應的初始化方法  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()) {  // 在這裏完成AOP  wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);  }   return wrappedBean;  } 複製代碼

由於在Spring官網閱讀(九)Spring中Bean的生命週期(上)文章中咱們已經對這個方法作過度析了,而且這個方法自己也比較簡單,因此再也不對這個方法作過多贅述,咱們主要關注的就是Spring是如何將AOP應用到Bean的生命週期中的,對應的就是applyBeanPostProcessorsAfterInitialization這個方法,其源碼以下:

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; } 複製代碼

實際上就是調用了全部後置處理器的postProcessAfterInitialization方法,在Spring中AOP相關的API及源碼解析,原來AOP是這樣子的一文中已經提到過了,@EnableAspectJAutoProxy註解實際上就是向容器中註冊了一個AnnotationAwareAspectJAutoProxyCreator,這個類自己就是一個後置處理器,AOP代理就是由它在這一步完成的。

Bean生命週期中AOP的流程

一、@EnableAspectJAutoProxy

經過@EnableAspectJAutoProxy註解向容器中註冊一個AnnotationAwareAspectJAutoProxyCreatorBeanDefinition,它自己也是一個BeanPostProcessor,這個BeanDefinition會在org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors這個方法中完成建立,以下圖所示

image-20200704112937846
image-20200704112937846

二、postProcessBeforeInstantiation方法執行

執行AnnotationAwareAspectJAutoProxyCreatorpostProcessBeforeInstantiation方法,實際上就是父類AbstractAutoProxyCreatorpostProcessBeforeInstantiation被執行

對應源碼以下:

// 這個方法的主要目的就是在不考慮通知的狀況下,確認哪些Bean不須要被代理
// 1.Advice,Advisor,Pointcut類型的Bean不須要被代理 // 2.不是原始Bean被包裝過的Bean不須要被代理,例如ScopedProxyFactoryBean // 實際上並不僅是這些Bean不須要被代理,若是沒有對應的通知須要被應用到這個Bean上的話 // 這個Bean也是不須要被代理的,只不過不是在這個方法中處理的。 public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {  Object cacheKey = getCacheKey(beanClass, beanName);  // 若是beanName爲空或者爲這個bean提供了定製的targetSource  if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {  // advisedBeans是一個map,其中key是BeanName,value表明了這個Bean是否須要被代理  // 若是已經包含了這個key,不須要在進行判斷了,直接返回便可  // 由於這個方法的目的就是在實例化前就確認哪些Bean是不須要進行AOP的  if (this.advisedBeans.containsKey(cacheKey)) {  return null;  }  // 說明尚未對這個Bean進行處理  // 在這裏會對SpringAOP中的基礎設施bean,例如Advice,Pointcut,Advisor作標記  // 標誌它們不須要被代理,對應的就是將其放入到advisedBeans中,value設置爲false  // 其次,若是這個Bean不是最原始的Bean,那麼也不進行代理,也將其value設置爲false  if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {  this.advisedBeans.put(cacheKey, Boolean.FALSE);  return null;  }  }   // 是否爲這個Bean提供了定製的TargetSource  // 若是提供了定製的TargetSource,那麼直接在這一步建立一個代理對象並返回  // 通常不會提供  TargetSource targetSource = getCustomTargetSource(beanClass, beanName);  if (targetSource != null) {  if (StringUtils.hasLength(beanName)) {  this.targetSourcedBeans.add(beanName);  }  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);  Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);  this.proxyTypes.put(cacheKey, proxy.getClass());  return proxy;  }   return null; } 複製代碼

三、postProcessAfterInitialization方法執行

實際上也是執行父類AbstractAutoProxyCreator中的方法,對應源碼以下:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
 if (bean != null) {  Object cacheKey = getCacheKey(bean.getClass(), beanName);  // 何時這個判斷會成立呢?  // 若是不出現循環引用的話,remove方法一定返回null  // 因此這個remove(cacheKey) != bean確定會成立  // 若是發生循環依賴的話,這個判斷就不會成立  // 這個咱們在介紹循環依賴的時候再詳細分析,  // 目前你只須要知道wrapIfNecessary完成了AOP代理  if (this.earlyProxyReferences.remove(cacheKey) != bean) {  // 須要代理的話,在這裏完成的代理  return wrapIfNecessary(bean, beanName, cacheKey);  }  }  return bean; } 複製代碼

四、wrapIfNecessary方法執行

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  // 在postProcessBeforeInstantiation方法中可能已經完成過代理了  // 若是已經完成代理了,那麼直接返回這個代理的對象  if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {  return bean;  }   // 在postProcessBeforeInstantiation方法中可能已經將其標記爲不須要代理了  // 這種狀況下,也直接返回這個Bean  if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {  return bean;  }   // 跟在postProcessBeforeInstantiation方法中的邏輯同樣  // 若是不須要代理,直接返回,同時在advisedBeans中標記成false  if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {  this.advisedBeans.put(cacheKey, Boolean.FALSE);  return bean;  }   // 獲取能夠應用到這個Bean上的通知  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);  // 若是存在通知的話,說明須要被代理  if (specificInterceptors != DO_NOT_PROXY) {  this.advisedBeans.put(cacheKey, Boolean.TRUE);  // 到這裏建立代理,實際上底層就是new了一個ProxyFactory來建立代理的  Object proxy = createProxy(  bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));  this.proxyTypes.put(cacheKey, proxy.getClass());  return proxy;  }  // 若是沒有通知的話,也將這個Bean標記爲不須要代理  this.advisedBeans.put(cacheKey, Boolean.FALSE);  return bean; } 複製代碼

關於建立代理的具體源碼分析,在Spring中AOP相關的API及源碼解析,原來AOP是這樣子的一文中已經作了詳細介紹,因此本文再也不贅述,如今咱們的重點將放在Spring是如何解析出來通知的,對應方法就是getAdvicesAndAdvisorsForBean,其源碼以下:

第一步:調用org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

protected Object[] getAdvicesAndAdvisorsForBean(
 Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {   // 經過findEligibleAdvisors方法返回對應的通知  // 這個方法回返回全部能應用在指定的Bean上的通知  List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);   if (advisors.isEmpty()) {  return DO_NOT_PROXY;  }  return advisors.toArray(); } 複製代碼

第二步:調用org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
 // 獲取到全部的通知  List<Advisor> candidateAdvisors = findCandidateAdvisors();  // 從獲取到的通知中篩選出能應用到這個Bean上的通知  List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);  extendAdvisors(eligibleAdvisors);  if (!eligibleAdvisors.isEmpty()) {  eligibleAdvisors = sortAdvisors(eligibleAdvisors);  }  return eligibleAdvisors; } 複製代碼

第三步:調用org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors獲取到全部的通知

// 這個方法的目的就是爲了獲取到全部的通知
protected List<Advisor> findCandidateAdvisors() {   // 先調用父類的方法,父類會去查找容器中全部屬於Advisor類型的Bean  List<Advisor> advisors = super.findCandidateAdvisors();   // 這個類自己會經過一個aspectJAdvisorsBuilder來構建通知  // 構建的邏輯就是解析@Aspect註解所標註的類中的方法  if (this.aspectJAdvisorsBuilder != null) {  advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());  }   // 最後返回這些通知  return advisors; } 複製代碼

第四步:org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors構建通知,這個方法比較長,咱們就只分析其中的關鍵代碼便可

public List<Advisor> buildAspectJAdvisors() {
 List<String> aspectNames = this.aspectBeanNames;   if (aspectNames == null) {  synchronized (this) {  aspectNames = this.aspectBeanNames;  if (aspectNames == null) {  List<Advisor> advisors = new ArrayList<>();  aspectNames = new ArrayList<>();  // 會獲取到容器中的全部BeanName  String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(  this.beanFactory, Object.class, true, false);  for (String beanName : beanNames) {  // 若是對beanName配置了正則匹配的話,那麼要按照正則表達式的匹配規則進行過濾  // 默認是沒有的,能夠認爲isEligibleBean始終返回true  if (!isEligibleBean(beanName)) {  continue;  }  // We must be careful not to instantiate beans eagerly as in this case they  // would be cached by the Spring container but would not have been weaved.  Class<?> beanType = this.beanFactory.getType(beanName);  if (beanType == null) {  continue;  }  // 判斷類上是否添加了@Aspect註解  if (this.advisorFactory.isAspect(beanType)) {  aspectNames.add(beanName);  AspectMetadata amd = new AspectMetadata(beanType, beanName);  // 默認就是SINGLETON,代理切面對象是單例的  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {  // 最後從這個切面實例中解析出全部的通知  // 關於通知解析的具體代碼就再也不分析了  MetadataAwareAspectInstanceFactory factory =  new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);  List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);  if (this.beanFactory.isSingleton(beanName)) {  this.advisorsCache.put(beanName, classAdvisors);  }  else {  this.aspectFactoryCache.put(beanName, factory);  }  advisors.addAll(classAdvisors);  }  // 省略部分代碼  return advisors;  }  複製代碼

第五步:org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply

protected List<Advisor> findAdvisorsThatCanApply(  List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {   ProxyCreationContext.setCurrentProxiedBeanName(beanName);  try {  return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);  }  finally {  ProxyCreationContext.setCurrentProxiedBeanName(null);  } } 複製代碼

這個方法其實沒啥好分析的,就是根據前面找出來的Advisor集合進行遍歷,而後根據每一個Advisor對應的切點來進行匹配,如何合適就返回,對應源碼也比較簡單,固然前提是你看過我以前那篇AOP源碼分析的文章了.

總結

這篇文章比較短,由於沒有作很細節的源碼分析,比較詳細的源碼分析已經放到上篇文章中了。最後我這裏畫個流程圖總結一下AOP是怎麼被應用到Bean的生命週期中的

image-20200705152704917
image-20200705152704917

Spring源碼的最後一點補充

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)  throws BeanCreationException {  // 1.實例化 ---> createBeanInstance  // 2.屬性注入 ---> populateBean  // 3.初始化 ---> 完成初始化及AOP  // exposedObject 就是完成初始化後的Bean   // 省略部分代碼,省略代碼的做用已經在上面標明瞭   // 下面的代碼實際上主要目的在於處理循環依賴  if (earlySingletonExposure) {  Object earlySingletonReference = getSingleton(beanName, false);  if (earlySingletonReference != null) {  if (exposedObject == bean) {  exposedObject = earlySingletonReference;  }  // 咱們以前早期暴露出去的Bean跟如今最後要放到容器中的Bean不是同一個  // allowRawInjectionDespiteWrapping爲false  // 而且當前Bean被當成依賴注入到了別的Bean中  else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {  // 獲取到當前Bean所從屬的Bean  String[] dependentBeans = getDependentBeans(beanName);  // 要獲得真實的從屬的Bean  Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);  for (String dependentBean : dependentBeans) {  // 移除那些僅僅爲了類型檢查而建立出來  if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {  actualDependentBeans.add(dependentBean);  }  }  if (!actualDependentBeans.isEmpty()) {  // 拋出異常  // 出現了循環依賴,而且實際存在容器中的Bean跟被看成依賴注入到別的Bean中的  // 不是同一個對象,這個時候也報錯  }  }  }  }   // 註冊bean的銷燬回調  try {  registerDisposableBeanIfNecessary(beanName, bean, mbd);  }  catch (BeanDefinitionValidationException ex) {  throw new BeanCreationException(  mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);  }   return exposedObject; } 複製代碼

實際這段代碼仍是跟循環依賴相關,循環依賴是Spring中一個比較重要的話題,不論是爲了面試仍是更好的瞭解清楚Spring的流程都頗有必要去弄懂它

關於Spring的循環依賴,我將在下篇文章專門分析!

若是本文對你有幫助的話,記得點個贊吧!也歡迎關注個人公衆號,微信搜索:程序員DMZ,或者掃描下方二維碼,跟着我一塊兒認認真真學Java,踏踏實實作一個coder。

公衆號
公衆號

我叫DMZ,一個在學習路上匍匐前行的小菜鳥!

相關文章
相關標籤/搜索