Spring AOP 的实现过程

Spring AOP 的实现过程原文 Spring AOP 的实现过程 1 什么是 aop aop 的作用是在书写上将辅助业务逻辑从主业务的逻辑中拆出来 实现主业务和辅助业务的解耦 但在执行主业务逻辑的时候 辅助业逻辑业也会执行 从而保证拆前拆后功能不变 那辅助业务拆出来放在哪里

大家好,我是讯享网,很高兴认识大家。

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

注意类中的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 继承的类图,不然后面走方法会有点懵。
在这里插入图片描述
讯享网
接下来看下代理类是怎么生成的,通过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:在 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接口,它有两个实现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); } } 

这里肯定看懵了吧。

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

在这里插入图片描述

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

小讯
上一篇 2025-04-02 22:10
下一篇 2025-03-02 23:00

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/57606.html