1. 什么是 aop

aop 的作用是在书写上将辅助业务逻辑从主业务的逻辑中拆出来,实现主业务和辅助业务的解耦,但在执行主业务逻辑的时候,辅助业逻辑业也会执行。从而保证拆前拆后功能不变。

那辅助业务拆出来放在哪里?

辅助业务封装在切面中,所以 面向切面编程

什么是切面?

切面就是一个类,并且被 @Aspect 注释。

辅助业务在切面的哪里?

辅助业务封装在切面的方法里,不同的方法封装不同的辅助业务逻辑。

切面中的方法是怎么触发的呢?

给封装辅助业务逻辑所在的方法标注 @Before 、@After、@AfterRunning 、@AfterThrowing、@Around 其中一个或几个注解。这样当主业务方法执行时,辅助业务的逻辑所在的方法就知道何时触发了。

切面中的辅助业务逻辑所在的方法由两个作用:1. 辅助业务内容是什么?2. 辅助业务何时触发。切面中具有这两个作用的方法叫做:通知 。

主业主逻辑执行时,通知也会在恰当时机执行,那到底是哪个主业执行时会触发通知?

总不能随便执行一个类的方法,通知就跟着触发一遍吧,这不乱套了咩。

在切面里还有一样东西,它规定了哪几个类的哪些方法执行时,触发当前切面中的通知。

这样东西就叫切点,一个被@Pointcut 注释切面中的方法。

所以:

aop 是啥?面向切面编程,将辅助业务和主业务解耦,但要保证功能不变。

切面是啥?通知和切点同时所在的那个类,就叫切面,脑门上还被@Aspect 注释了。

通知和切点有都是啥?自己去上面重新看一遍,放心,不长。

切面长什么样子?

// 一个简单的切面。
@Aspect
public class LoggerAspect {
    
  // 切点,“切” com.forum.controller 包下的所有类的所有方法。
  @Pointcut("execution(public * com.forum.controller.*.*(..))")
  public void log() {
  }

  // 通知1,在目标方法开始执行时,执行该通知。  
  @Before("log()") 
  public void doBefore(JoinPoint joinPoint) {
        // .......
      	// 辅助业务1
      	// .......
  }
  
  // 通知2,在目标方法开始执行结束后,执行该通知。  
  @After("log()")
  public void doAfter() {
        // .......
      	// 辅助业务2
      	// .......
  }
}

2. aop的实现过程

主业务在 A 类中,辅助业务在切面 B 类中,在 Spring 项目的启动过程中,会将 A 和 B 类“组合”成一个新的类 C,C 在执行 A 的主业务时,在恰当的时机,会调用 B 中的方法。C 就是就是常说的代理类。

重点:当且仅当 代理对象执行目标方法时,aop 功能才会触发!

先规定几个术语:

  • 主业务类 A 叫做目标类,A 中的方法叫做目标方法。
  • 辅助业务类 B 叫做切面,B中的方法要么是通知、要么是切点。
  • C 类叫做代理类。

所以,AOP 的实现过程有两个阶段:

  1. 目标类是怎么变成代理类的?
  2. 代理类是怎么执行目标方法的?

2.1 目标类变成代理类的过程

这个准确的说是目标对象变成代理对象的过程,因为类不实例化不能使用。所以首先是目标类实例化成目标对象,这是 IOC 的构成,这里就不详细描述了。

// AbstractAutoWireCapableBeanFactory.java 的 doCreateBean() 方法中。

// A 的 bean 被创建出来,仅仅是一个内存空间,属性值都是默认的初始值。
Object bean = instanceWrapper.getWrappedInstance();

// 一直往下走....

// 如果 bean 被 aop ”切“了,在 initializeBean 方法中 bean 会被变成代理对象返回。
exposedObject = initializeBean(beanName, exposedObject, mbd);

先有的 bean ,再为其创建代理对象。

// AbstractAutoWireCapableBeanFactory.java。

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 {
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        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()) {
        // 这个方法将 bean 变成了代理对象。
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

那怎么变的呢?

bean 被创建后,接下来会调用所有的BeanPostProcessor 作用在 bean 上,其中有一个名叫 AnnotationAwareAspectJAutoProxyCreator 的 BeanPostProcessor,是它将 bean 变成了对应的代理类。

注意类中的Aspect,意思是处理切面的处理器

(至于 AnnotationAwareAspectJAutoProxyCreator 是怎么来的,在后面会写到。)

// AbstractAutoWireCapableBeanFactory.java。

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    // 遍历所有的 beanPostProcessor。
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 在 bean 上执行每一个 beanPostProcessor 的 postProcessAfterInitialization() 方法。
        Object current = processor.postProcessAfterInitialization(result, beanName);
        // 下面的代码就是将结果返回。
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

先看下 AnnotationAwareAspectJAutoProxyCreator 继承的类图,不然后面走方法会有点懵。

java aop是异步吗_前端


接下来看下代理类是怎么生成的,通过ctrl+F12,找到方法postProcessAfterInitialization(@Nullable Object bean, String beanName)

// AbstractAutoProxyCreator.java

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 缓存 cacheKey 类似 bean 的 id。 
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 如果 bean 已经生成了代理,那就跳过,不再重复生成。
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 为 bean 创建代理对象并返回。
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

为什么要判断 bean 是否已经生成了代理?因为在工程启动过程中有两个地方可以将目标对象变成 aop 代理,一个在这里,另一个是目标对象从三级缓存转到二级缓存的时候,所以在这里需要判断下。

// 如果 bean 需要被代理,为他创建一个代理对象。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 如果 beanName 是有效的,且 beanName 已经被代理了。
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        // 此时不需再创建代理,直接返回 bean。
        return bean;
    }
    // 不需要处理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        // 不需要创建代理,直接返回 bean。
        return bean;
    }
    //  基础设施类(Advice、Pointcut、Advisor、AopInfrastructureBean 这些类为接口的子类)不应该为其创建代理;
    // 或者 beanName 以 ".ORIGINAL" 结尾,也不应该为其创建代理。
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // 如果 bean 有方法被"切"了,为其创建代理。

    // 获取针对 bean 的所有增强器,(每一个通知都会被构建成一个增强器。)
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    //  判断 specificInterceptors 不为空。如果是空,说明当前 bean 没有被”切“。
    if (specificInterceptors != DO_NOT_PROXY) {
        // 设置需要为 cacheKey 创建代理。
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理。
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // 保存 cacheKey 的代理。
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // 返回创建好的代理对象。
        return proxy;
    }
    // 如果拿不到 advices,标识 bean 不需要增强。(都没被切,增强个毛线)
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    // 返回 bean 或者 bean的代理对象。
    return bean;
}

这块的逻辑简单,判断有没有针对 bean 的 切面,如果没有,直接将 bean 返回。如果有,为 bean 创建代理对象。(至于代理对象是怎么创建的,这个后面再写。)

这里就有个问题了:

  1. 切面信息是怎么拿到的?(更确切的说切面是怎么被解析的)
  2. 怎么判断有没有针对当前 bean 的切面?其实找的是有没有作用在当前 bean 上的通知。

2.1.1 获取切面信息

继续往下看代码,简单的逻辑就不解释了,直接看注释吧。
通过ctrl+F12,找到方法protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource )

// AbstractAutoProxyCreator.java,
// 但是记住,因为被继承了,所以此时应该是是在 AnnotationAwareAspectJAutoProxyCreator.java 中
protected Object[] getAdvicesAndAdvisorsForBean(
												Class<?> beanClass,
												String beanName,
												@Nullable TargetSource targetSource
												) {
    // 为 beanClass 寻找符合条件的增强器(advisors),增强器就是通知,这个后面有解释。
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    // 如果找不到
    if (advisors.isEmpty()) {
        // 返回 null
        return DO_NOT_PROXY;
    }
    // 找到了增强器,以数组格式将这些增强器返回。
    return advisors.toArray();
}

继续往下走…,找到findEligibleAdvisors(Class<?> beanClass, String beanName)方法

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 找到所有的 advisors,包括事务的 advisor 和 普通 aop 的 advisor。。
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 在所有的 advisors 中筛选符合 beanClass 使用的 advisor。(通过切点那块的表达式判定有没有切到 beanClass)
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 向 eligibleAdvisors 中额外再添加一个 ExposeInvocationInterceptor 类型的 advisor。
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 如果有多个 advisor,谁先谁后,在这里排序。
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    //  返回符合条件的 advisor
    return eligibleAdvisors;
}

findCandidateAdvisors()这个方法中把上面两个问题都回答了,去方法里面看看。

// 这里一定要进到 AnnotationAwareAspectJAutoProxyCreator.java 中,走错地方就找不到这个方法了。

protected List<Advisor> findCandidateAdvisors() {
    // 这是是找事务相关的 advisor。因为事务相关的 advisor 少,而且是现成的。
    List<Advisor> advisors = super.findCandidateAdvisors();
    
    if (this.aspectJAdvisorsBuilder != null) {
        // 接下来才找 aspectJ 的 advisor。
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    // 返回找到的所有通知。
    return advisors;
}

事务相关的,这里不解释了。主要看自定义的切面。

2.1.2 切面是怎么被解析的。

切面刚开始也只是个 class 文件,只有被注入、解析后才能使用。先大概简述下切面被注入和解析的思路:

  1. 切面类上有@Component注解,所以在工程启动时调用ConfigurationClassPostProcessor会加载切面的 java 文件,创建对应的 BeanDefinition,最后再遍历所有的 BeanDefinition 并为每一个创建对应的 bean 保存到applicationContext 中的 beanFactory 字段的 beanDefinitionMaps字段中。(切面就这样注入了)。
  2. 接下来就是解析切面了,最终解析到切面的通知上去,因为辅助逻辑都在通知里面写着。
    2.1 去beanFactory 中拿所有的 object 的 beanName,即获取所有的 bean 的名字。
    2.2 遍历 beanNames,为每一个 beanName 创建 Class 对象。
    2.3 判断该 Class 对象上有没有 @Aspect注解,如果有,那这个类就是切面类了。
    2.4 切面类的 Class 对象都拿到了,获取类中的全部方法肯定也是能办到的。遍历类中的每一个方法,判断方法上有没有 @Before、@After、@Around、等注解,如果有,拿这个方法就是通知为每一个通知创建一个 Advisor 对象。最后将所有的 Advisor 对象 保存到 advisors 列表中。最后返回。

解析切面的流程大概就是这样,详细的源码如下,先不管缓存的事情,后面会写切面缓存,以及解析切面的执行时机。

public List<Advisor> buildAspectJAdvisors() {
    // aspectBeansNames 中保存的是已经解析的切面的名称。
    // 因为不止这一个地方有解析切面的代码,在doCreateBean() 方法之前也解析过切面。
    // 并且将解析到的通知都缓存起来。并标记该切面已被解析,下次再要解析时直接从缓存中拿。
    List<String> aspectNames = this.aspectBeanNames;
    // aspectNames == null 说明这是第一次解析,那就要执行正真的解析流程了。
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                // 用于保存所有解析出来的 Advisors 集合对象。
                List<Advisor> advisors = new ArrayList<>();
                // 用于保存所切面名称的集合。
                aspectNames = new ArrayList<>();
                /**
					 *  aop 功能在这里传入的是 Object 对象,表示从容器中获取所有组件的名称,然后遍历所有组件,
					 *  这个遍历过程是非常消耗性能的,所以 Spring 在这个过程中加入了保存切面信息的缓存。但是事务
					 *  功能不一样,事务模块的功能是直接去容器中获取 Advisor 类型的,选择范围小,且不消耗性能。
					 *  所以 Spring 在事务模块中没有加入缓存来保存事务相关的 advisor。
					 */
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Object.class, true, false);
                // 遍历从 IOC 容器中拿到的所有 beanName。
                for (String beanName : beanNames) {
                    // 判断 beanName 是合法的。
                    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.
                    // 通过 beanName 去容器中获取到 class 对象。(能拿到 class对象,bean的全部信息也就都拿到了)
                    Class<?> beanType = this.beanFactory.getType(beanName, false);
                    if (beanType == null) {
                        continue;
                    }
                    // 根据 class 对象判断是不是切面。(有没有被 @Aspect 注解)
                    if (this.advisorFactory.isAspect(beanType)) {
                        // 是切面,
                        // beanName 加到缓存中。
                        aspectNames.add(beanName);
                        // 用 class 对象和 beanName 构建一个 AspectMetadata 对象。
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            // 构建切面注解的实例工厂
                            MetadataAwareAspectInstanceFactory factory =
                                new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            // 获取切面中的所有通知。
                            /**
								 *  思想:都已经拿到 class 对象了,从中可以获取到类中的所有方法,遍历每一个方法,
								 *  判断方法上有没有 @Before、@After、@Around、等注解。如果有,就为这个方法 new 对应类型的 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();
    }
    // 能走到这里,切面已经被解析了,通知已经被转换成 advisor 缓存起来了。
    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;
}

2.1.3 解析切面的执行时机

在上面的代码中可以看到,只有当 this.aspectBeanNames为空的时候才执行解析,否则去 this.advisorsCache 中直接拿advisor,最后返回。那就说明在前面某个地方已经解析过切面了,并且将解析的结果缓存了起来。

问题1:在哪里解析的?
问题2:为什么要缓存?又将解析结果缓存到了哪里?

答案1:在 createBean() 方法中 doCreateBean()方法之前解析的。

// AbstractAutowireCapableBeanFactory.java
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// -----
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            // beanName 的 Class 对象是能拿到的。
            if (targetType != null) {
                // 在这里解析的切面。
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            // AnnotationAwareAspectJAutoProxyCreator 调用 postProcessBeforeInstantiation 方法。
            Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
            if (result != null) {
                return result;
            }
        }
    }
    return null;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        // shouldSkip(beanClass, beanName) 方法里解析了切面。
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }
    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;
}
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // 这个方法是不是很熟悉。
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
        if (advisor instanceof AspectJPointcutAdvisor &&
            ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
            return true;
        }
    }
    return super.shouldSkip(beanClass, beanName);
}
@Override
protected List<Advisor> findCandidateAdvisors() {
    // 这是是找事务相关的 advisor。因为事务相关的 advisor 少,而且是现成的。
    List<Advisor> advisors = super.findCandidateAdvisors();
    
    if (this.aspectJAdvisorsBuilder != null) {
        // 接下来才找 aspectJ 的 advisor。
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    // 返回找到的所有通知。
    return advisors;
}

答案2:自定义切面的解析过程是,从 beanFactory 中拿出所有的 bean 执行遍历,看脑门上有没有 @Aspect注解(确认是不是切面),如果有再去遍历这个类的方法,也是根据脑门上的注解判断方法是不是通知,最后为每个通知构建一个 Advisor 对象。

这就会出现两个开销和大的地方:

  1. 如果 beanFactory 中的 bean 非常多,全部遍历一遍解析本身就很花时间。(这个过程是解析了项目中全部切面)
  2. beanFactory 中的 beanDefinition 非常多时,每个 beanDefinition 变成 bean 时都要解析一遍切面,那么1过程将会被重复执行好多遍,这就更费性能了。

所以缓存是有必要的。只需要将全部切面解析一遍缓存起来,下次直接从缓存中获取,就能降低开销。

那缓存到哪里了呢?

前面说到过,AnnotationAwareAspectJAutoProxyCreator 的 bean 是已经被注入到 beanFactory 中了。 AnnotationAwareAspectJAutoProxyCreator里面有个字段叫 BeanFactoryAspectJAdvisorsBuilderBeanFactoryAspectJAdvisorsBuilder中有两个字段

// 缓存所有被解析的切面的 beanName。
List<String> aspectBeanNames
// 缓存解析的所有切面的通知。 key: 切面 beanName, value: 切面下的所有通知构成的list。
Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>()

beanFactory 中的 bean 是全局的,而且只要项目处于启动状态,bean就一直存在。所以下次如果再要解析切面,直接从缓存中拿。

2.1.4 筛选作用于当前 bean 的增强器

在上面拿到了所有项目中所有的增强器,但不是每个增强器都作用在当前bean 上的,所以还需要筛选出那些作用在当前bean上的增强器。

先补一个概念,上面提到的 为每一个通知构建一个 Advisor() 对象,增强器里不只有通知,还有切点。

// 这是项目中增强器的类型
final class InstantiationModelAwarePointcutAdvisorImpl{
    private final Pointcut pointcut; // 切点
    private Advice instantiatedAdvice; // 通知
    // 其他字段就不贴了。
}

代码中是这么筛选增强器的,candidateAdvisors 是所有的增强器,beanClass, beanName 表示当前bean,通过切点筛选有木有作用在 bean 上的增强器。

// 在所有的 advisors 中筛选符合 beanClass 使用的 advisor。(通过切点那块的表达式判定有没有切到 beanClass)
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

细节代码不贴了,套的太深…

2.1.5 创建代理对象

bean有了,作用在 bean上的增强器也被找到了,接下来就是拿着两个东西创建代理对象。

这块的思路很简单:

  1. 创建代理工厂。
ProxyFactory proxyFactory = new ProxyFactory();
  1. 将目标对象和它的增强器都 set 到 代理工厂中。
proxyFactory.setTargetSource(targetSource);
proxyFactory.addAdvisors(advisors);
  1. 代理工厂创建代理对象,并返回。
proxyFactory.getProxy(getProxyClassLoader())

代理工厂返回的是 AopProxy接口,它有两个实现CglibAopProxy JdkDynamicAopProxy。至于创建的到底是哪一个,要根据条件判断的,这篇文章不对这一部分进行描述。

(到这里,目标对象变成代理对象的流程就算是描述完了。)

再加一个小点,AnnotationAwareAspectJAutoProxyCreator 干了那么多事情,有没有好奇它是怎么来的?

2.1.7 AnnotationAwareAspectJAutoProxyCreator 怎么被加载的?

前面写到 AnnotationAwareAspectJAutoProxyCreator 解析了切面,创建了增强器,最后将目标对象变成了代理对象,那这个类是怎么被加载到的呢?

Spring 项目启动一般都会有一个配置类。

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.aop")
public class AppConfig {

}

@EnableAspectJAutoProxy表面上被翻译成 “启动自动代理”,看看它里面到底干了啥。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {}

@Import(AspectJAutoProxyRegistrar.class) 这个很关键。

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    // 注册一个 BeanDefinition。
    // 注册了 beanDefinition, 那后面肯定会有对应的 bean 产生,
    // 关键是 注册了哪个类的 beanDefinition。
    @Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 答案在这里。
		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        // 剩下的代码就不贴了。
    }
}
// -----
// 顺着上面的方法一直走下去,会走到这个方法
registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
// 看下它的方法体。
private static BeanDefinition registerOrEscalateApcAsRequired(
    Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
	// 多余的代码这里不贴了。
   
    // cls = AnnotationAwareAspectJAutoProxyCreator.class
    // 为 cls 创建 beanDefinition。
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 注册 beanDefinition。
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

到这里就清楚了:

  1. @EnableAspectJAutoProxy 注解将AnnotationAwareAspectJAutoProxyCreator 的 beanDefinition 注入了 applicationContext 中。
  2. 接下来走 beanDefinition 创建对应 bean 的过程,AnnotationAwareAspectJAutoProxyCreator的 bean 也就注入了 applicationConext 中。
  3. AnnotationAwareAspectJAutoProxyCreator 实现了 BeanPostProcessor接口,所以在对目标类的 bean 执行初始化时,会调用所有的 BeanPostProcessor,当然少不了 annotationAwareAspectJAutoProxyCreator,它执行 postProcessAfterInitialization(Object bean, String beanName) 方法,将目标类变成了代理类。(它解析切面的时机,不在赘述了)
  4. 至于@Import 注解是什么时候执行的,不用猜都知道它在 ConfigurationClassPostProcessor 类的processConfigBeanDefinitions() 方法中执行的。

2.2. 代理类执行目标方法的过程

代理对象创建好了,最关键的时刻来了:代理对对象执行目标方法并在恰当时机将通知织入。

2.2.1 一个 aop 的例子

// 目标对象。
@Component
public interface TestBean {
	void test(int numberA,int numberB);
}

@Component
public class TestBeanImpl implements TestBean {
	public void test(int numberA, int numberB) {
		System.out.println("目标方法执行: "+ numberA/numberB);
	}
}
// 切面
@Aspect
@Component
public class AspectJTest {

	@Pointcut("execution(* com.aop.domain..test(..))")
	public void test() {

	}

	@Before("test()")
	public void beforeTest() {
		System.out.println("before ...");
	}

	@After("test()")
	public void afterTest() {
		System.out.println("after ...");
	}

	@Around("test()")
	public Object aroundTest(ProceedingJoinPoint p) {
		System.out.println("around before ...");
		Object object = null;
		try {
			object = p.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("around after ....");
		return object;
	}

	@AfterReturning("test()")
	public void afterReturn() {
		System.out.println("after return ...");
	}

	@AfterThrowing("test()")
	public void afterThrow() {
		System.out.println("after throw ...");
	}
}
// 主程序
public class Main {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
				AppConfig.class);
        // 此时拿出来的 bean 已经是代理对象了。
		TestBean bean = (TestBean) context.getBean("testBeanImpl");
        // 代理对象执行目标方法。
		bean.test(6,3);
	}
}

结果:

around before ...
before ...
目标方法执行: 2
after return ...
after ...
around after ....

除了异常通知没有执行以外,其他通知都执行了。

2.2.2 底层流程

在上面的例子中代理代理对象 bean 中已经包含了所有的增强器。

当执行目标方法 bean.test(6,3); 时,代理将调用:

// JdkDynamicAopProxy.java 
// proxy 是代理对象。 method 是目标方法。 agrs 是[6,3]
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    // 获取到我们的目标对象。
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        // 如果执行代理对象的 equals 方法,不需要代理。
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
        }
        // 如果执行代理对象的 hashCode 方法,不需要代理。
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        // 如果执行的 class 对象是 DecoratingProxy,也不要执行拦截器。
        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)) {
            // “采用反射执行连接点” 意思就是采用反射的方式直接执行方法。
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
        // 暴露代理。把我们的代理对象暴露到线程中。这个跟 ThreadLocal 有关。
        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 对象。
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // Get the interception chain for this method.
        // 从目标类的所有通知中找出作用与当前方法的那些通知(增强器),保存到 chain 中。
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		
        if (chain.isEmpty()) {
            // 如果拦截器链为空,说明没有作用于当前方法的通知,则通过反射直接执行目标方法。
            // 先获取方法的入口参数。
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            // 再通过反执行方法。(JoinPoint,连接点说的就是方法)
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // We need to create a method invocation...
            // 如果拦截器不为空,就要织入了。所谓织入就是在执行目标方法的时候,将它的通知在恰当的地方一并执行。
            // 先创建 MethodInvocation 对象,将所有需要的信息都封装在里面。
            // (代理、目标对象、目标方法、方法参数、目标对象的Class对象 、拦截链)
            MethodInvocation 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);
        }
    }
}

看看 chain里面到底有多少个通知,因为自定义切面中写了 5 个,解析切面时还会额外添加 1 个。

chain 中增强器的排列顺序就是执行目标方法时通知的织入顺序。

那具体是怎么执行的呢?

public Object proceed() throws Throwable {
    // currentInterceptorIndex 的初始值是 -1.
    // interceptorsAndDynamicMethodMatchers 是个列表里面存放着那 6 个增强器。
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 当遍历完所有拦截器后,执行目标方法。
        return invokeJoinpoint();
    }
    // 根据索引拿出拦截器。
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            return proceed();
        }
    }
    else {
        // 拦截器执行 invoke 方法。
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

这里肯定看懵了吧。

画了一张图,可以直观的看看拦截器的执行顺序。

java aop是异步吗_前端_02

3. 几小问题

问题1:aop 的底层实现是什么?

底层实现是 java 动态代理 和 cglib。

问题2:为什么jdk动态代理必须是接口?

jdk 动态代理会生成一个字节码文件,也就是 xxx.class 文件。将这个文件反编译后就会看到

public final class $Proxy extends Proxy implements CustomDefinedInterface{
    // .......
}

因为生成的代理对象默认继承了 Proxy 类,又因为 java 是单继承的,所以我们的被代理对象必须是接口。

问题3:aop实现到默认是使用 jdk动态代理还是 cglib?

aop没有默认使用哪个代理,都是根据条件判断的。

注解是 @EnableAspectJAutoProxy时,如果目标类有父接口,采用 jdk 动态代理;如果目标类没有父接口,采用 cglib 代理。

注解是 @EnableAspectJAutoProxy(proxyTargetClass = true)时,采用 cglib 代理。

问题4:切面解析的顺序怎么确定?

使用 @order(number) 注解,number 越小,优先级越高。