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)的补充,它提供了另一种关于程序结构的思考方式)

这个解释听起来很抽象,如何理解呢?

我们知道面向对象编程关注的是对象和对象的行为,相同类似行为或属性的对象可以通过继承来实现,但是如果我们关注的是毫不相干的一组对象的特定方法 怎么办

《Spring源码分析-深入浅出AOP(图文分析)》

如图,我们要关注的方法 散布在不同类中,如果想要在这些方法中加入相同的处理逻辑,过去我们采用的方法只能是硬编码,但是这样会造成大量的重复代码,不便于维护,AOP的出现就是解决针对这种问题的一种实现模式。

首先我们看下AOP中涉及到的几个重要的基本概念

Aspect:字面意思是切面,官方解释 a modularization of a concern that cuts across multiple classes ( 横切多个类的一个关注点模块),简单说就是对类具体行为的关注点集合,这样解释还是太抽象

《Spring源码分析-深入浅出AOP(图文分析)》

上图中 可以看到 对多个类关注的方法 形成的一个切面,就是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中,一个连接点通常代表一个方法的调用,我们可以认为一个方法的执行就是一个连接点

《Spring源码分析-深入浅出AOP(图文分析)》

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)

《Spring源码分析-深入浅出AOP(图文分析)》

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).(切入点,匹配连接点的断言,通知与切入点表达式相关联,并且在任何匹配该断言的连接点上执行(比如一个特定名称的方法执行))

用来描述我们要在哪些地方执行,也可以说成是 用表达式匹配(正则断言)的切入点

《Spring源码分析-深入浅出AOP(图文分析)》

Target object: object being advised by one or more aspects. Also referred to as the advised object(目标对象 ,被一个或多个切面通知的对象,通常被称为被通知对象)

《Spring源码分析-深入浅出AOP(图文分析)》

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.
(定义一个单独的代理类,用来提供对目标对象的代理访问 如下图所示 代理类会默认实现目标对象的接口,并在此基础上提供其他功能)

《Spring源码分析-深入浅出AOP(图文分析)》

Spring AOP Revolution(进化史)

《Spring源码分析-深入浅出AOP(图文分析)》

详细内容可以参考 这里不再详述
http://cxis.me/2017/04/10/Spring%E4%B8%ADAOP%E7%9A%84%E9%85%8D%E7%BD%AE%E4%BB%8E1.0%E5%88%B05.0%E7%9A%84%E6%BC%94%E8%BF%9B/

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源码分析-深入浅出AOP(图文分析)》

spring加载完xml配置文件,会对配置文件中的标签进行解析,spring默认会加载自己的解析器,这些解析器散布在各个不同的jar包中

《Spring源码分析-深入浅出AOP(图文分析)》

这些配置文件中的handlers就是spring对默认标签的处理器,我们看下context包下的spring.handlers文件

《Spring源码分析-深入浅出AOP(图文分析)》

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

《Spring源码分析-深入浅出AOP(图文分析)》

我们进入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

《Spring源码分析-深入浅出AOP(图文分析)》

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源码分析-深入浅出AOP(图文分析)》

下边我们重点分析第二步流程,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.
    //获取目标对象上当前方法的的所有拦截器链(已经注入到代理类的属性中,直接可以获取)
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      if (chain.isEmpty()) {
         // We can skip creating a MethodInvocation: just invoke the target directly
         // Note that the final invoker must be an InvokerInterceptor so we know it does
         // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
        //如果没有任何拦截器,则直接调用目标对象的切点方法
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {

         // We need to create a method invocation…
        //执行拦截器的proceed方法
         invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // Proceed to the joinpoint through the interceptor chain.
         retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      Class<?> returnType = method.getReturnType();
      if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
         // Special case: it returned "this" and the return type of the method
         // is type-compatible. Note that we can't help if the target sets
         // a reference to itself in another returned object.
         retVal = proxy;
      }
      else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
         throw new AopInvocationException(
               "Null return value from advice does not match primitive return type for: " + method);
      }
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

进入拦截器的proceed方法

public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.
    //如果已经执行到最后一个拦截器,就执行切点上的方法
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

    //获取下一个拦截器
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // Evaluate dynamic method matcher here: static part will already have
      // been evaluated and found to match.
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        //匹配当前方法
      if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // Dynamic matching failed.
         // Skip this interceptor and invoke the next in the chain.
            //不匹配直接跳过
         return proceed();
      }
   }
   else {
      // It's an interceptor, so we just invoke it: The pointcut will have
      // been evaluated statically before this object was constructed.
    //普通拦截器直接调用
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

在这里我们看到spring会循环递归调用代理的invoke方法 这里从代码上看不够直观 下边我们用图形来进行分析 初始状态
《Spring源码分析-深入浅出AOP(图文分析)》

这里 proxy bean就是上边的JdkDynamicAopProxy ,里边包含已经解析完的拦截器链和 目标对象 ,当调用目标对象的指定方法时,其实就是调用代理类的processed方法,processed方法先取出拦截器链中第一个拦截器,然后执行第一个拦截器的拦截方法

《Spring源码分析-深入浅出AOP(图文分析)》

进入after拦截器后,会先调用目标对象的方法,现在对于目标对象的调用,都有代理对象来处理

《Spring源码分析-深入浅出AOP(图文分析)》

这样会再次递归调用代理的invoke方法,下一个拦截器Around开始被调用

《Spring源码分析-深入浅出AOP(图文分析)》

进入Around拦截器方法,会默认先执行around前置逻辑,然后在调用目标对象方法,最后执行后置逻辑

《Spring源码分析-深入浅出AOP(图文分析)》

当执行Around拦截器的目标对象方法时,会再次递归调用代理的invoke方法,这时会执行最后一个拦截器Before

《Spring源码分析-深入浅出AOP(图文分析)》
《Spring源码分析-深入浅出AOP(图文分析)》

最后进入Before拦截器,会先执行before逻辑方法,最后调用目标对象方法

《Spring源码分析-深入浅出AOP(图文分析)》

调用目标对象的方法会调用代理的invoke方法,这时候代理的所有拦截器都已经调用完毕,所以会直接执行目标对象的方法

《Spring源码分析-深入浅出AOP(图文分析)》
《Spring源码分析-深入浅出AOP(图文分析)》
《Spring源码分析-深入浅出AOP(图文分析)》

执行完目标对象的方法后,会依次回归调用之前的方法

《Spring源码分析-深入浅出AOP(图文分析)》
《Spring源码分析-深入浅出AOP(图文分析)》

从上边的分析,我们可以知道 最后执行的顺序其实交给了每个要执行的拦截器,通过拦截器中的调用来决定拦截顺序,至此spring aop jdk代理就分析完了,有关CGLib代理方式,这里就不再展开分析,流程大同小异,有兴趣的可以自行研究

文字版

SPRING 通过注解实现AOP的过程的文字描述 – xml配置文件中声明自动发现注解(),spring默认启动时会注册注解解析器(AspectJAutoProxyBeanDefinitionParser),在注解解析器解析(parse)的过程中,会注册一个实现了BeanPostProcessor的后处理器(AspectJAwareAdvisorAutoProxyCreator),这个后处理器在目标对象实例化后进行拦截处理,拦截的流程是,先搜索所有已经注册的BeanDefiniton,从中找到标记了注解(@Aspect)的切面组成拦截器链,选择那些可以应用到目标对象的拦截器(过滤),如果拦截器链不为空,则为目标对象生成代理(JdkDynamicAopProxy或CglibAopProxy),当调用目标对象的指定拦截方法时,就会默认调用对应代理类的代理方法(invoke),这样就完成了AOP的整个流程

PS 其实在使用过程中,我们都是基于接口进行编程,所以用的最多的也是jdk动态代理,但是当我们目标对象没有实现任何接口,但是我们还想给它增加新的行为,这时候我们就需要CGLib方式,所以具体选择哪种代理方法还要看具体的业务场景

spring AOP应用场景

企业开发里边用的最后的就是 事务拦截器,日志拦截器 性能拦截器 还有 权限拦截器 等等 后边我们分析spring mvc还会介绍spring 拦截器的使用 尽请关注

参考

Spring源码深度解析 https://book.douban.com/subject/25866350/
Spring Partten https://en.wikipedia.org/wiki/Proxy_pattern#cite_note-2
AOP Git source https://github.com/spring-projects/spring-framework
GOF Porxy wiki https://en.wikipedia.org/wiki/Aspect-oriented_programming

    原文作者:Spring MVC
    原文地址: https://blog.csdn.net/c_unclezhang/article/details/78769426
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞