Spring AOP 的实现过程

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 变成了对应的代理类。

(至于 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 继承的类图,不然后面走方法会有点懵。

image-20210622101946537

接下来看下代理类是怎么生成的

// 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 获取切面信息

继续往下看代码,简单的逻辑就不解释了,直接看注释吧。

// 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();
}

继续往下走…

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;
}

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

// 这里一定要进到 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();
    
  2. 将目标对象和它的增强器都 set 到 代理工厂中。

    proxyFactory.setTargetSource(targetSource);
    proxyFactory.addAdvisors(advisors);
    
  3. 代理工厂创建代理对象,并返回。

    proxyFactory.getProxy(getProxyClassLoader())
    

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

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

再加一个小点,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 中已经包含了所有的增强器。

image-20210623193717330

当执行目标方法 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 个。

image-20210623200200243

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);
    }
}

这里肯定看懵了吧。

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

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 越小,优先级越高。

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring AOP(面向切面编程)是Spring框架中的一个重要模块,它提供了一种在程序运行期间动态地将额外的行为织入到代码中的方式。通过使用Spring AOP,我们可以将与业务逻辑无关的横切关注点(如日志记录、性能统计、事务管理等)从业务逻辑中分离出来,使得代码更加清晰、可维护和可扩展。 Spring AOP实现主要依赖于以下几个核心概念: 1. 切面(Aspect):切面是一个模块化的单元,它封装了与横切关注点相关的行为。在Spring AOP中,切面可以包含通知(Advice)和切点(Pointcut)。 2. 通知(Advice):通知定义了在切面的特定位置执行的代码。在Spring AOP中,有以下几种类型的通知: - 前置通知(Before):在目标方法执行之前执行。 - 后置通知(After):在目标方法执行之后执行,无论是否发生异常。 - 返回通知(After-returning):在目标方法正常返回之后执行。 - 异常通知(After-throwing):在目标方法抛出异常后执行。 - 环绕通知(Around):包围目标方法的执行,在前后都可以添加额外的逻辑。 3. 切点(Pointcut):切点定义了在哪些连接点(Joinpoint)上应用通知。通过使用切点表达式,我们可以指定需要拦截的方法或类。 4. 连接点(Joinpoint):连接点是在应用程序执行过程中能够插入切面的点,如方法调用、异常抛出等。 5. 织入(Weaving):织入是将切面应用到目标对象并创建代理对象的过程Spring AOP支持编译时织入、类加载时织入和运行时织入三种方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值