1. 程式人生 > 程式設計 >Spring如何實現AOP,請不要再說cglib了!

Spring如何實現AOP,請不要再說cglib了!

1. 從註解入手找到對應核心類

最近工作中我都是基於註解實現AOP功能,常用的開啟AOP的註解是@EnableAspectJAutoProxy,我們就從它入手。


上面的動圖的流程的步驟就是:
@EnableAspectJAutoProxy
--> AspectJAutoProxyRegistrar
-->AopConfigUtils .registerAspectJAnnotationAutoProxyCreatorIfNecessary
-->AnnotationAwareAspectJAutoProxyCreator.class

AnnotationAwareAspectJAutoProxyCreator檢視其中文註釋(如下),確定它就是AOP的核心類!--溫安適 20191020

/**
1.AspectJAwareAdvisorAutoProxyCreator的子類
,用於處理當前應用上下文中的註解切面
2.任何被AspectJ註解的類將自動被識別。
3.若SpringAOP代理模式可以識別,優先使用Spring代理模式。
4.它覆蓋了方法執行連線點
5.如果使用<aop:include>元素,
  則只有名稱與include模式匹配的@aspectj bean才被視為切面
  ,並由spring自動代理。
6. Spring Advisors的處理請查閱,
org.springframework.aop
.framework.autoproxy.AbstractAdvisorAutoProxyCreator
 */
@SuppressWarnings("serial"
) public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator { //...省略實現 }註解切面 複製程式碼

雖然找到了核心類,但是並沒有找到核心方法!下面我們嘗試畫類圖確定核心方法。

2.畫核心類類圖,猜測核心方法

AnnotationAwareAspectJAutoProxyCreator的部分類圖。

AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator

從類圖看到了AnnotationAwareAspectJAutoProxyCreator實現了BeanPostProcessor,而AOP功能應該在建立完Bean之後執行,猜測AnnotationAwareAspectJAutoProxyCreator實現BeanPostProcessor的postProcessAfterInitialization(例項化bean後處理)是核心方法。 檢視AnnotationAwareAspectJAutoProxyCreator實現的postProcessAfterInitialization方法,實際該方法在其父類AbstractAutoProxyCreator中。

//AbstractAutoProxyCreator中的postProcessAfterInitialization實現
@Override
public Object postProcessAfterInitialization(Object bean,String beanName)
 throws BeansException {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(),beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
         return wrapIfNecessary(bean,beanName,cacheKey);
      }
   }
   return bean;
}
複製程式碼

發現發現疑似方法wrapIfNecessary,檢視其原始碼如下,發現createProxy方法。確定找對了地方。

protected Object wrapIfNecessary
        (Object bean,String beanName,Object cacheKey) {
   if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   if (isInfrastructureClass(bean.getClass()) 
                   || shouldSkip(bean.getClass(),beanName)) {
      this.advisedBeans.put(cacheKey,Boolean.FALSE);
      return bean;
   }

   // 建立代理
   Object[] specificInterceptors = 
       getAdvicesAndAdvisorsForBean(bean.getClass(),null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey,Boolean.TRUE);
      Object proxy = createProxy(
            bean.getClass(),specificInterceptors,new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey,proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey,Boolean.FALSE);
   return bean;
}
複製程式碼

即AnnotationAwareAspectJAutoProxyCreator實現BeanPostProcessor的postProcessAfterInitialization方法,在該方法中由wrapIfNecessary實現了AOP的功能。 wrapIfNecessary中有2個和核心方法

  • getAdvicesAndAdvisorsForBean獲取當前bean匹配的增強器
  • createProxy為當前bean建立代理
    要想明白核心流程還需要分析這2個方法。

3.讀重點方法,理核心流程

3.1 getAdvicesAndAdvisorsForBean獲取當前bean匹配的增強器

檢視原始碼如下,預設實現在AbstractAdvisorAutoProxyCreator中。

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass,@Nullable TargetSource targetSource) {
   List<Advisor> advisors = findEligibleAdvisors(beanClass,beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}
複製程式碼

查閱findEligibleAdvisors方法,就幹了3件事

  • 找所有增強器,也就是所有@Aspect註解的Bean
  • 找匹配的增強器,也就是根據@Before,@After等註解上的表示式,與當前bean進行匹配,暴露匹配上的。
  • 對匹配的增強器進行擴充套件和排序,就是按照@Order或者PriorityOrdered的getOrder的資料值進行排序,越小的越靠前。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass,String beanName) {
   //找所有增強器
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   //找所有匹配的增強器
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors,beanClass,beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
       //排序
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}
複製程式碼

AnnotationAwareAspectJAutoProxyCreator 重寫了findCandidateAdvisors,下面我們看看具體實現了什麼

3.1.1findCandidateAdvisors找所有增強器,也就是所有@Aspect註解的Bean

@Override
protected List<Advisor> findCandidateAdvisors() {
   // Add all the Spring advisors found according to superclass rules.
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Build Advisors for all AspectJ aspects in the bean factory.
   if (this.aspectJAdvisorsBuilder != null) {
      //@Aspect註解的類在這裡除了
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
   return advisors;
}
複製程式碼

從該方法我們可以看到處理@Aspect註解的bean的方法是:this.aspectJAdvisorsBuilder.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) {
               if (!isEligibleBean(beanName)) {
                  continue;
               }
               // 必須注意,bean會提前暴露,並被Spring容器快取,但是這時還不能織入。
               Class<?> beanType = this.beanFactory.getType(beanName);
               if (beanType == null) {
                  continue;
               }
               if (this.advisorFactory.isAspect(beanType)) {
                  //找到所有被@Aspect註解的類
                  aspectNames.add(beanName);
                  AspectMetadata amd = new AspectMetadata(beanType,beanName);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                     MetadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory,beanName);
                     //解析封裝為Advisor返回
                     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);
                  }
                  else {
                     // Per target or per this.
                     if (this.beanFactory.isSingleton(beanName)) {
                        throw new IllegalArgumentException("Bean with name '" + beanName +
                              "' is a singleton,but aspect instantiation model is not singleton");
                     }
                     MetadataAwareAspectInstanceFactory factory =
                           new PrototypeAspectInstanceFactory(this.beanFactory,beanName);
                     this.aspectFactoryCache.put(beanName,factory);
                     advisors.addAll(this.advisorFactory.getAdvisors(factory));
                  }
               }
            }
            this.aspectBeanNames = aspectNames;
            return advisors;
         }
      }
   }

   if (aspectNames.isEmpty()) {
      return Collections.emptyList();
   }
   List<Advisor> advisors = new ArrayList<>();
   for (String aspectName : aspectNames) {
      List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
      if (cachedAdvisors != null) {
         advisors.addAll(cachedAdvisors);
      }
      else {
         MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
         advisors.addAll(this.advisorFactory.getAdvisors(factory));
      }
   }
   return advisors;
}
複製程式碼

這個方法可以概括為:

  • 找到所有BeanName
  • 根據BeanName篩選出被@Aspect註解的類
  • 針對類中被Around.class,Before.class,After.class,AfterReturning.class,AfterThrowing.class註解的方法,先按上邊的註解順序排序後按方法名稱排序,每一個方法對應一個Advisor。

3.2 createProxy為當前bean建立代理。

3.2.1 建立代理的2種方式

眾所周知,建立代理的常用的2種方式是:JDK建立和CGLIB,下面我們就看看這2中建立代理的例子。

3.2.1 .1 jdk建立代理的例子

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxyMain {

    public static void main(String[] args) {
        JDKProxyTestInterface target = new JDKProxyTestInterfaceImpl();
        // 根據目標物件建立代理物件
        JDKProxyTestInterface proxy =
         (JDKProxyTestInterface) Proxy
         .newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new JDKProxyTestInvocationHandler(target));
        // 呼叫代理物件方法
        proxy.testProxy();
    }

    interface JDKProxyTestInterface {
        void testProxy();
    }
    static class JDKProxyTestInterfaceImpl 
                    implements JDKProxyTestInterface {
        @Override
        public void testProxy() {
            System.out.println("testProxy");
        }
    }
   static class JDKProxyTestInvocationHandler 
                           implements InvocationHandler {
        private  Object target;
        public JDKProxyTestInvocationHandler(Object target){
            this.target=target;
        }
        @Override
        public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
            System.out.println("執行前");
            Object result=  method.invoke(this.target,args);
            System.out.println("執行後");
            return result;
        }
    }
複製程式碼

3.2.1 .2 cglib建立代理的例子

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyTest {

   static class CglibProxyService {
        public  CglibProxyService(){
        }
        void sayHello(){
            System.out.println(" hello !");
        }
    }

    static class CglibProxyInterceptor implements MethodInterceptor{
        @Override
        public Object intercept(Object sub,Object[] objects,MethodProxy methodProxy)
                                          throws Throwable {
            System.out.println("before hello");
            Object object = methodProxy.invokeSuper(sub,objects);
            System.out.println("after hello");
            return object;
        }
    }

    public static void main(String[] args) {
        // 通過CGLIB動態代理獲取代理物件的過程
        Enhancer enhancer = new Enhancer();
        // 設定enhancer物件的父類
        enhancer.setSuperclass(CglibProxyService.class);
        // 設定enhancer的回撥物件
        enhancer.setCallback(new CglibProxyInterceptor());
        // 建立代理物件
        CglibProxyService proxy= (CglibProxyService)enhancer.create();
        System.out.println(CglibProxyService.class);
        System.out.println(proxy.getClass());
        // 通過代理物件呼叫目標方法
        proxy.sayHello();
    }
}
複製程式碼

3.2.1 .3 jdk建立代理與cglib建立代理的區別

型別 jdk建立動態代理 cglib建立動態代理
原理 java動態代理是利用反射機制生成一個實現代理介面的匿名類,在呼叫具體方法前呼叫InvokeHandler來處理 cglib動態代理是利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理
核心類 Proxy 建立代理利用反射機制生成一個實現代理介面的匿名類InvocationHandler 方法攔截器介面,需要實現invoke方法 net.sf.cglib.proxy.Enhancer:主要增強類,通過位元組碼技術動態建立委託類的子類例項net.sf.cglib.proxy.MethodInterceptor:方法攔截器介面,需要實現intercept方法
侷限性 只能代理實現了介面的類 不能對final修飾的類進行代理,也不能處理final修飾的方法

3.2.2 Spring如何選擇的使用哪種方式

Spring的選擇選擇如何代理時在DefaultAopProxyFactory 中。

public class DefaultAopProxyFactory implements AopProxyFactory,Serializable {
   @Override
   public AopProxy createAopProxy(AdvisedSupport config) 
                                   throws AopConfigException {
      if (config.isOptimize() 
          || config.isProxyTargetClass()
          || hasNoUserSuppliedProxyInterfaces(config)) {
         Class<?> targetClass = config.getTargetClass();
         if (targetClass == null) {
            throw new AopConfigException(
            "TargetSource cannot determine target class: " 
            +"Either an interface or a target "+
           " is required for proxy creation.");
         }
         if (targetClass.isInterface() 
                 || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
         }
         return new ObjenesisCglibAopProxy(config);
      }
      else {
         return new JdkDynamicAopProxy(config);
      }
   }
   //...
   }
複製程式碼
  • config.isOptimize() 檢視原始碼註釋時發現,這個是配置使用cglib代理時,是否使用積極策略。這個值一般不建議使用!
  • config.isProxyTargetClass() 就是@EnableAspectJAutoProxy中的proxyTargetClass屬性。

//exposeProxy=true AopContext 可以訪問,proxyTargetClass=true CGLIB生成代理 @EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true)

  • hasNoUserSuppliedProxyInterfaces 是否存在可代理的介面

總結下Spring如何選擇建立代理的方式:

  1. 如果設定了proxyTargetClass=true,一定是CGLIB代理
  2. 如果proxyTargetClass=false,目標物件實現了介面,走JDK代理
  3. 如果沒有實現介面,走CGLIB代理

4.總結

Spring如何實現AOP?,您可以這樣說:

  1. AnnotationAwareAspectJAutoProxyCreator是AOP核心處理類
  2. AnnotationAwareAspectJAutoProxyCreator實現了BeanProcessor,其中postProcessAfterInitialization是核心方法。
  3. 核心實現分為2步
    getAdvicesAndAdvisorsForBean獲取當前bean匹配的增強器 createProxy為當前bean建立代理
  4. getAdvicesAndAdvisorsForBean核心邏輯如下
    a. 找所有增強器,也就是所有@Aspect註解的Bean
    b. 找匹配的增強器,也就是根據@Before,@After等註解上的表示式,與當前bean進行匹配,暴露匹配上的。
    c. 對匹配的增強器進行擴充套件和排序,就是按照@Order或者PriorityOrdered的getOrder的資料值進行排序,越小的越靠前。
  5. createProxy有2種建立方法,JDK代理或CGLIB
    a. 如果設定了proxyTargetClass=true,一定是CGLIB代理
    b. 如果proxyTargetClass=false,目標物件實現了介面,走JDK代理
    c. 如果沒有實現介面,走CGLIB代理