1. 程式人生 > >Spring原始碼分析-深入淺出AOP(圖文分析)

Spring原始碼分析-深入淺出AOP(圖文分析)

這篇文章主要解決三個問題

  1. 什麼是AOP
  2. Spring 中怎麼實現的AOP
  3. AOP的應用場景



首先我們看下 到底什麼是AOP

AOP的基本概念

AOP 官方定義

    Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure.
( 面向方面的程式設計(AOP)是面向物件程式設計(OOP)的補充,它提供了另一種關於程式結構的思考方式)

這個解釋聽起來很抽象,如何理解呢?

我們知道面向物件程式設計關注的是物件和物件的行為,相同類似行為或屬性的物件可以通過繼承來實現,但是如果我們關注的是毫不相干的一組物件的特定方法 怎麼辦

一組物件和物件的行為

如圖,我們要關注的方法 散佈在不同類中,如果想要在這些方法中加入相同的處理邏輯,過去我們採用的方法只能是硬編碼,但是這樣會造成大量的重複程式碼,不便於維護,AOP的出現就是解決針對這種問題的一種實現模式。

首先我們看下AOP中涉及到的幾個重要的基本概念

Aspect:字面意思是切面,官方解釋 a modularization of a concern that cuts across multiple classes

( 橫切多個類的一個關注點模組),簡單說就是對類具體行為的關注點集合,這樣解釋還是太抽象

Aspect

上圖中 可以看到 對多個類關注的方法 形成的一個切面,就是Aspect

Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution(程式執行過程中的裡連線點,比如 方法呼叫或者異常處理。在spring aop中,一個連線點通常代表一個方法的呼叫,我們可以認為一個方法的執行就是一個連線點

Join Point

Advice: action taken by an aspect at a particular join point. Different types of advice include “around,” “before” and “after” advice. (Advice types are discussed below.)(通知:切面在特定連線點上產生的動作,包括多種不同型別的通知,比如 環繞通知 aroud,前置通知before和後置通知after等)

簡單的說就是我們要在切面上做什麼事情(動作-action)

Advice

Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name).(切入點,匹配連線點的斷言,通知與切入點表示式相關聯,並且在任何匹配該斷言的連線點上執行(比如一個特定名稱的方法執行))

用來描述我們要在哪些地方執行,也可以說成是 用表示式匹配(正則斷言)的切入點

Point cut

Target object: object being advised by one or more aspects. Also referred to as the advised object(目標物件 ,被一個或多個切面通知的物件,通常被稱為被通知物件)

Target Object

Proxy Pattern(代理模式)
GOF 定義
What problems can the Proxy design pattern solve? (代理模式能解決什麼問題)

  • The access to an object should be controlled.(控制被訪問物件)
  • Additional functionality should be provided when accessing an
    object.(給被訪問物件提供額外功能)

What solution does the Proxy design pattern describe?(代理模式的解決方案是如何描述的)
Define a separate Proxy object that can be used as substitute for another object (Subject) and implements additional functionality to control the access to this subject.
(定義一個單獨的代理類,用來提供對目標物件的代理訪問 如下圖所示 代理類會預設實現目標物件的介面,並在此基礎上提供其他功能)

Proxy Pattern

Spring AOP Revolution(進化史)

spring AOP revolution

Spring如何實現AOP

我們重點使用最新最簡潔的spring 5 自動配置模式來進行講解
這裡我們通過一個性能攔截器來演示spring aop的程式碼流程,這個攔截器可以列印服務層所有方法的執行時間

CODE

業務介面定義

/**
 * 業務服務介面
 */
public interface IBusinessService {

    //業務執行方法A
    void executeBusinessA();

    //業務執行方法B
    void executeBusinessB();


}

業務實現類

/**
 * 業務實現類
 */
@Service("businessService")
public class BusinessServiceImpl implements IBusinessService {

    private  static final Logger logger = LogManager.getLogger(BusinessServiceImpl.class);

    /**
     * 常規業務方法(不超時)
     */
    public void executeBusinessA() {
        //模擬一個執行時間
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }

        logger.info("executing business in methodA");

    }


    /**
     * 超時業務方法
     */
    public void executeBusinessB() {
        //模擬一個超時執行時間
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println(e.getMessage());
        }

        logger.info("executing business in methodB");

    }
}

效能分析器

@Component
@Aspect
public class PerformanceAnalysisInterceptor {

    private static Logger logger = LogManager.getLogger(PerformanceAnalysisInterceptor.class);
    private static final long DELAY_MINUTE = 1000;

    public static final String POINTCUT = "execution (* aop.service.impl.BusinessServiceImpl.*(..))";



    @Around(POINTCUT)
    public Object analysisAround(ProceedingJoinPoint joinPoint) {
        Object obj = null;
        long startTime = System.currentTimeMillis();
        try {
            obj = joinPoint.proceed(joinPoint.getArgs());
        } catch (Throwable e) {
            logger.error(e.getMessage());
        }

        long endTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
        long diffTime = endTime - startTime;

        logger.info("-----" + methodName + "執行時間 :" + diffTime + " ms");
        if(diffTime > DELAY_MINUTE)
        delayWarning(methodName, diffTime-DELAY_MINUTE);

        return obj;
    }

    private void delayWarning(String methodName,long delayTime) {
            logger.warn("-----" + methodName + "超時 :" + delayTime + " ms");
    }



}

xml自動化配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="aop"/>
    <aop:aspectj-autoproxy/>

</beans>

主函式呼叫執行

public class AopMain {

    public static void main(String[] args) {

        ClassPathXmlApplicationContext f = new ClassPathXmlApplicationContext("spring-aop.xml");
        IBusinessService businessService = (IBusinessService) f.getBean("businessService");
        businessService.executeBusinessA();
        businessService.executeBusinessB();

    }
}

concole列印結果

11:21:36.344 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7dc222ae: startup date [Sat Dec 09 11:21:36 CST 2017]; root of context hierarchy
11:21:36.393 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring-aop.xml]
11:21:37.245 [main] INFO  aop.service.impl.BusinessServiceImpl - executing business in methodA
11:21:37.247 [main] INFO  aop.interceptor.PerformanceAnalysisInterceptor - -----aop.service.IBusinessService.executeBusinessA執行時間 :203 ms
11:21:38.250 [main] INFO  aop.service.impl.BusinessServiceImpl - executing business in methodB
11:21:38.250 [main] INFO  aop.interceptor.PerformanceAnalysisInterceptor - -----aop.service.IBusinessService.executeBusinessB執行時間 :1003 ms
11:21:38.250 [main] WARN  aop.interceptor.PerformanceAnalysisInterceptor - -----aop.service.IBusinessService.executeBusinessB超時 :3 ms

Process finished with exit code 0

下邊我們進行程式碼的詳細分析

我們看到 xml配置檔案裡邊只有兩行配置資訊,第一行配置是context自動掃描,作用是掃描制定範圍的元件並註冊到spring,第二行是註解Aspect自動發現

這樣解釋還是有點抽象,我們從兩個問題入手(問題是揭開真相的火花)

第一個問題,spring怎麼識別出需要代理的目標物件,也就是我們這裡邊實現了IBusinessService介面的BusinessServiceImpl物件

第二問題是  我們定義的這個切面(aspect註解的bean)怎麼被發現並應用到目標物件(businessService)

第一個問題屬於spring ioc的範圍,看過ioc部分的同學基本應該能夠理解,入手點就是對 context 這個標籤的處理 我們全域性搜尋component-scan這個關鍵字,最後程式碼定位到 ContextNamespaceHandler

spring載入完xml配置檔案,會對配置檔案中的標籤進行解析,spring預設會載入自己的解析器,這些解析器散佈在各個不同的jar包中

這些配置檔案中的handlers就是spring對預設標籤的處理器,我們看下context包下的spring.handlers檔案

spring在解析標籤時,通過實現NamespaceHandlerResolver介面的DefaultNamespaceHandlerResolver載入所有classpath下的spring.handlers檔案中的對映,並在解析標籤時,尋找這些標籤對應的處理器,然後用這些處理器來處理標籤。所以,當遇到component-scan標籤,spring 容器就會使用ComponentScanBeanDefinitionParser標籤進行解析,我們看下ComponentScanBeanDefinitionParser來的parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
    //取base-package屬性
   String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
   //解析base-packet屬性(萬用字元)
   basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
   //轉換成陣列(用,或者;分割的定義)
   String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

   // Actually scan for bean definitions and register them.
   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    //掃描basepacket下所有的bean
   Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    //註冊到spring中
   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

   return null;
}
進入掃描方法
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

這裡我們看到,spring會掃描base-package指定資源路徑下所有的java檔案,並依次解析成對應的內部對映BeanDefinition,最後註冊到spring容器中,方便後續呼叫 具體分析不再展開
可以參考 : http://www.cnblogs.com/question-sky/p/6953315.html

OK,我們已經知道spring怎麼發現bean,那麼第二問題是 我們定義的這個切面(aspect註解的bean)怎麼被發現並應用到目標物件(businessService),這也是我們
要分析的重點,這也是第二行標籤的作用

我們看到 PerformanceAnalysisInterceptor類的註解有兩個 一個是 @Component 一個是@Aspect Component標籤剛才已經解釋過,說明這是一個spring的元件,已經註冊到spring中,那Aspect註解呢?根據剛才的分析 我們知道 spring對於不同的標籤採用不同的處理器,同理,這裡Aspect也有對應的標籤處理器,進過全域性搜尋,我們發現在AopNamespaceHandler

我們進入AspectJAutoProxyBeanDefinitionParser的parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
//註冊AspectJAnnotationAutoProxyCreator
   AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
   extendBeanDefinition(element, parserContext);
   return null;
}
進入registerAspectJAutoProxyCreatorIfNecessary方法
public static void registerAspectJAutoProxyCreatorIfNecessary(
      ParserContext parserContext, Element sourceElement) {

    //把應用了註解@Aspect的bean註冊成BeanDefiniton
   BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
         parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    //處理proxy-target-class和expose-proxy屬性
   useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    //註冊元件並通知監聽器
   registerComponentIfNecessary(beanDefinition, parserContext);
}
我們重點看下第一個流程 進入方法體
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
      @Nullable Object source) {

   return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
      @Nullable Object source) {

   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

   if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
         int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
         int requiredPriority = findPriorityForClass(cls);
         if (currentPriority < requiredPriority) {
            apcDefinition.setBeanClassName(cls.getName());
         }
      }
      return null;
   }

   RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
   beanDefinition.setSource(source);
   beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
   beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
   return beanDefinition;
}

這裡我們看到,spring會註冊一個內部的BeanDefiniton key是 org.springframework.aop.config.internalAutoProxyCreator

AspectJAwareAdvisorAutoProxyCreator結構圖,我們看到它實現了BeanPostProcessor介面,意味著spring在載入整個bean時,例項化前會呼叫
postProcessAfterInitialization方法,我們進入這個方法(在父類AbstractAutoProxyCreator中)

public Object postProcessAfterInitialization(@Nullable 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,這個方法的作用就是 如果目標bean能夠被代理,則使用代理進行包裹

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //如果已經處理過,直接返回
   if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
    //無需增強
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
    //如果是基礎類比如 advice pointcut advisor 這些基礎aop類則直接返回
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // Create proxy if we have advice.
    //獲取被代理類所有的攔截器
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
    //建立代理
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

這裡我們看到了AOP邏輯的核心 ,整個流程很清晰,首先獲取這個bean對應的所有攔截器 如果攔截為空,直接返回bean,否則 建立對應的代理 然後返回代理bean
這裡有兩個重點分析部分 一個是攔截器的獲取,一個是如何代理

我們先開攔截器的獲取

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}
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;
}
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) {
     //獲取AOP註解攔截器
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
   return advisors;
}

從程式碼來看,攔截器分成兩部分,一部分來源是配置檔案,一部分是應用了註解的bean,在我們的演示案例中配置檔案中沒有對應的攔截器xml配置,只有註解的@Aspect,所以我們直接分析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 LinkedList<>();
            aspectNames = new LinkedList<>();
            //獲取所有註冊的beanName
            String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                  this.beanFactory, Object.class, true, false);
            for (String beanName : beanNames) {
                // 子類定義規則來過濾bean
               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.
                //獲取bean型別
               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);
                  if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                     MetadataAwareAspectInstanceFactory factory =
                           new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);

                        //解析Aspect註解中的攔截器方法(比如 @After @Before @Aroud等)
                     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 LinkedList<>();
   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;
}

到這裡其實已經找到了 我們的效能攔截器 PerformanceAnalysisInterceptor 因為它上邊的@Aspect註解標識了它就是一個攔截器切面 ,下邊就是解析這個切面中的規則(Pointcut定義)和攔截方法(Advice)

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
   Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
   String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
   validate(aspectClass);

   // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
   // so that it will only instantiate once.
   MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
         new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

   List<Advisor> advisors = new LinkedList<>();

// 獲取切面攔截器的所有攔截方法
   for (Method method : getAdvisorMethods(aspectClass)) {
    //獲取攔截器方法(增強)幷包裝成advisor
      Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }

   // If it's a per target aspect, emit the dummy instantiating aspect.
   if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
      Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
      advisors.add(0, instantiationAdvisor);
   }

   // Find introduction fields
    //獲取DeclareParents註解 
   for (Field field : aspectClass.getDeclaredFields()) {
      Advisor advisor = getDeclareParentsAdvisor(field);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }

   return advisors;
}
進入getAdvisor方法
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
      int declarationOrderInAspect, String aspectName) {

   validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

    //獲取Pointcut表示式
   AspectJExpressionPointcut expressionPointcut = getPointcut(
         candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
   if (expressionPointcut == null) {
      return null;
   }

//根據pointcut生成攔截器物件
   return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
         this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

這裡getPointcut方法就是如何解析 PerformanceAnalysisInterceptor中的@Around(POINTCUT)

具體看下實現

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
   AspectJAnnotation<?> aspectJAnnotation =
         AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
   if (aspectJAnnotation == null) {
      return null;
   }

   AspectJExpressionPointcut ajexp =
         new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
   ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
   if (this.beanFactory != null) {
      ajexp.setBeanFactory(this.beanFactory);
   }
   return ajexp;
}
//獲取制定方法上的註解
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
   Class<?>[] classesToLookFor = new Class<?>[] {
         Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
   for (Class<?> c : classesToLookFor) {
      AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
      if (foundAnnotation != null) {
         return foundAnnotation;
      }
   }
   return null;
}

這裡是從spring提供的所有候選註解類中尋找 當前方法上的註解 並封裝成 AspectJAnnotation物件 對於我們定義的效能攔截器PerformanceAnalysisInterceptor
上邊只有一個@Around註解,所以這裡的Advisor也就只有一個(Advisor可以看成advice和pointcut的集合),下邊就是這些找到的攔截器中哪些可以應用到目標物件的方法上

List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
   if (candidateAdvisors.isEmpty()) {
      return candidateAdvisors;
   }
   List<Advisor> eligibleAdvisors = new LinkedList<>();
   for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
         eligibleAdvisors.add(candidate);
      }
   }
   boolean hasIntroductions = !eligibleAdvisors.isEmpty();
   for (Advisor candidate : candidateAdvisors) {
      if (candidate instanceof IntroductionAdvisor) {
         // already processed
         continue;
      }
      if (canApply(candidate, clazz, hasIntroductions)) {
         eligibleAdvisors.add(candidate);
      }
   }
   return eligibleAdvisors;
}
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
   if (advisor instanceof IntroductionAdvisor) {
      return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
   }
   else if (advisor instanceof PointcutAdvisor) {
      PointcutAdvisor pca = (PointcutAdvisor) advisor;
      return canApply(pca.getPointcut(), targetClass, hasIntroductions);
   }
   else {
      // It doesn't have a pointcut so we assume it applies.
      return true;
   }
}
我們定義的是PointcutAdvisor
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
   Assert.notNull(pc, "Pointcut must not be null");
   if (!pc.getClassFilter().matches(targetClass)) {
      return false;
   }

   MethodMatcher methodMatcher = pc.getMethodMatcher();
   if (methodMatcher == MethodMatcher.TRUE) {
      // No need to iterate the methods if we're matching any method anyway...
      return true;
   }

   IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
   if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
      introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
   }

   Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
   classes.add(targetClass);
   for (Class<?> clazz : classes) {
      Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
      for (Method method : methods) {
         if ((introductionAwareMethodMatcher != null &&
               introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
               methodMatcher.matches(method, targetClass)) {
            return true;
         }
      }
   }

   return false;
}
這裡的大體邏輯是 用找到的切點斷言匹配當前bean的所有方法,過濾 找到匹配的切點 到此為止我們已經看到spring是如何一步一步的解析我們宣告的註解@Aspect的切面spring會把這些註解@Aspect的類當成攔截器 找到了目標bean(BusinessServiceImpl)的攔截器,下一步就是判斷攔截器是否為空,為空說明不需要生成代理,直接返回bean,否則就需要建立一個目標bean的代理,這也就是我們常說的代理模式的應用,如圖所示,所有對目標物件的訪問都通過代理來實現,這樣我們就可以把攔截器應用到目標物件上

下邊我們重點分析第二步流程,spring中如何建立代理

Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }

   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);

   if (!proxyFactory.isProxyTargetClass()) {
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }

   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   proxyFactory.addAdvisors(advisors);
   proxyFactory.setTargetSource(targetSource);
   customizeProxyFactory(proxyFactory);

   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }

   return proxyFactory.getProxy(getProxyClassLoader());
}
我們看到,spring把建立代理的責任交給了**ProxyFactory**,我們看下getPorxy主流程
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);
   }
}

從這裡我們可以很清楚的看到,spring通過兩種方式建立代理,分別是JdkDynamicAopProxy和ObjenesisCglibAopProxy,這兩個就是我們常說的jdk動態代理和cglib代理(基於位元組碼)

spring怎麼區分通過哪種代理是通過三個判斷

 isOptimeiz         對CGlib代理的建立優化

 isProxyTargetClass <aop:aspectj-autoproxy proxy-target- class=true/> 表示使用CGLib進行代理

 hasNoUserSuppliedProxyInterfaces 是否存在代理介面

三個條件之一後,還有兩個判斷 是否實現了介面或是否是代理型別,通過上述判斷我們可以知道spring選擇代理的策略

 如果目標類實現了介面,預設採用jdk動態代理來實現AOP
 如果目標類實現了介面,可以通過配置檔案強行使用CGLib來實現AOP代理
 如果目標類沒有實現介面,只能使用CGlib來實現AOP代理

這說明CGlib可以為那些沒有實現介面的類增強行為,原理是為目標類生成一個子類,並覆蓋其中的方法

下邊我們重點看下jdk動態代理部分的實現,如果採用jdk動態代理獲取代理,關鍵的呼叫方法就是invoke呼叫

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   MethodInvocation invocation;
   Object oldProxy = null;
   boolean setProxyContext = false;

   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
         // The target does not implement the equals(Object) method itself.
         return equals(args[0]);
      }
      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
         // The target does not implement the hashCode() method itself.
         return hashCode();
      }
      else if (method.getDeclaringClass() == DecoratingProxy.class) {
         // There is only getDecoratedClass() declared -> dispatch to proxy config.
         return AopProxyUtils.ultimateTargetClass(this.advised);
      }
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         // Service invocations on ProxyConfig with the proxy config...
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;

      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }

      // Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
        //獲取目標物件和物件型別
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      // Get the interception chain for this method.