spring-aop 之 aop:config

2021-10-13 17:48:11 浏览数 (1)

文章目录

  • 开头
  • aop:config
    • 解析
      • proxy-target-class & expose-proxy
      • aop:pointcut
      • aop:advisor
      • aop:aspect
        • aop:declare-parents
        • 其它
          • MethodLocatingFactoryBean
          • SimpleBeanFactoryAwareAspectInstanceFactory
        • 总结
    • 代理子类生成
      • 入口
      • postProcessBeforeInstantiation
        • 调用时机
        • 源码
        • 应该代理 ?
          • 基础类检测
          • 跳过类检测
          • AOP逻辑
          • Advisor寻找
          • 适用性检测
          • 检测结果缓存
        • TargetSource
      • postProcessAfterInitialization
        • Advisor寻找
          • 适用性判断
          • 引入
          • 其它
          • Advisor扩展
          • 排序
        • 创建
          • JDK动态代理 or Cglib
          • JDK动态代理
          • equals & hashCode
          • 链式调用
          • Cglib

开头

aop部分的解析器由AopNamespaceHandler注册,其init方法:

代码语言:javascript复制
@Override
public void init() {
    registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
}

aop:config

此标签用以配置pointcut, advisor, aspect,实例:

代码语言:javascript复制
<aop:config>
    <aop:pointcut expression="execution(* exam.service..*.*(..))" id="transaction"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="transaction"/>
    <aop:aspect ref="" />
</aop:config>

ConfigBeanDefinitionParser.parse:

代码语言:javascript复制
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    CompositeComponentDefinition compositeDef =
            new CompositeComponentDefinition(element.getTagName(), 
            parserContext.extractSource(element));
    parserContext.pushContainingComponent(compositeDef);
    // 是否生成代理类
    configureAutoProxyCreator(parserContext, element);
    List<Element> childElts = DomUtils.getChildElements(element);
    for (Element elt: childElts) {
        String localName = parserContext.getDelegate().getLocalName(elt);
        if (POINTCUT.equals(localName)) {
            parsePointcut(elt, parserContext);
        } else if (ADVISOR.equals(localName)) {
            parseAdvisor(elt, parserContext);
        } else if (ASPECT.equals(localName)) {
            parseAspect(elt, parserContext);
        }
    }
    parserContext.popAndRegisterContainingComponent();
    return null;
}

解析

解析的过程主要分为以下几个部分。

proxy-target-class & expose-proxy

对应着aop:config的两个属性,前者代表是否为被代理这生成CGLIB子类,默认false,只为接口生成代理子类(话说如果不生成子类那么怎么拦截?)。后者代表是否将代理bean暴露给用户,如果暴露,可以通过Spring AopContext类获得,默认不暴露。

解析的过程无非就是属性的读取,不再详细说明。

aop:pointcut

pointcut的解析是一个生成一个BeanDefinition并将其id, expression等属性保存在BeanDefinition中。注意以下几点:

  • BeanDefinition的ID来自于id属性,如果没有,那么自动生成。
  • BeanDefinition的class是AspectJExpressionPointcut。
  • BeanDefinition的scope为prototype。

AspectJExpressionPointcut类图:

aop:advisor

首先是其所有属性的示例:

代码语言:javascript复制
<aop:advisor id="" order="" advice-ref="aopAdvice" pointcut="" pointcut-ref="" />

advisor概念是Spring独有的,来自于上古时代,应该是较早时候的aop概念的实现: AOP Alliance (Java/J2EE AOP standards)。Spring官方的说法: aop-schema-advisors。

其相关的包/类就在spring-aop下:

advice-ref是必须的属性,并且这里的advice必须实现org.aopalliance.aop.Advice的子接口。这些子接口指的什么呢,见Spring官方文档: aop-api-advice-types。比如org.aopalliance.intercept.MethodInterceptor。

最常见的用途就是结合事务使用:

代码语言:javascript复制
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED"/>
        <tx:method name="find*" read-only="true" propagation="NOT_SUPPORTED"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut expression="execution(* exam.service..*.*(..))" id="transaction"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="transaction"/>
</aop:config>

解析的套路和楼上类似,只不过此处的beanClass是DefaultBeanFactoryPointcutAdvisor,其类图:

另外注意对于pointcut和pointcut-ref两者处理的区别,对于pointcut属性,Spring会同样创建一个AspectJExpressionPointcut类型的BeanDefinition,对于pointcut-ref会生成一个RuntimeBeanReference对象指向原pointcut的引用。此类的类图:

可以看出,这种aop的实现需要实现各种接口,所以不应该再使用此种方式进行aop,除了Spring内部的实现。

aop:aspect

配置举例:

代码语言:javascript复制
<bean id="aopAdvice" class="base.aop.AopDemoAdvice" />
<!-- 必须配置,因为被代理的对象必须在Spring容器中 -->
<bean id="aopDemo" class="base.aop.AopDemo" />
<aop:config>
    <aop:pointcut id="pointcut" expression="execution(* base.aop.AopDemo.send())" />
    <aop:aspect ref="aopAdvice">
        <aop:before method="beforeSend" pointcut-ref="pointcut" />
        <aop:after method="afterSend" pointcut-ref="pointcut" />
    </aop:aspect>
</aop:config>

解析形成的BeanDefinition结构如下:

代码语言:javascript复制
AspectComponentDefinition
    beanRefArray
        RuntimeBeanReference(aop:aspect的ref属性)
    beanDefArray
        // 被注册
        RootBeanDefinition(aop:declare-parents)
            beanClass: DeclareParentsAdvisor
            ConstructorArg
                implement-interface
                types-matching
                default-impl
                delegate-ref
        // 被注册
        RootBeanDefinition(aop:before,aop:after...)
            beanClass: AspectJPointcutAdvisor
            ConstructorArg
                RootBeanDefinition
                    beanClass: 由子标签决定
                    ConstructorArg
                        RootBeanDefinition
                            beanClass: MethodLocatingFactoryBean
                            properties
                                targetBeanName: aspectName
                                methodName: method属性
                        RootBeanDefinition
                            beanClass: SimpleBeanFactoryAwareAspectInstanceFactory
                            properties
                                aspectBeanName: aspectName
                        //还有pointcut定义和引用...

结构图里面的aspectName来自于aop:aspect的ref属性,此属性是必须配置的,因为Spring要知道aop:before等标签指定的方法是哪个bean/类/对象的方法。

aop:declare-parents

对于aop:declare-parents子标签,其决定的是代理子类应该实现哪些接口:

代码语言:javascript复制
<aop:declare-parents types-matching="" implement-interface="" />

此标签最终被解析成为beanClass为DeclareParentsAdvisor的BeanDefinition,并注册到容器中。其类图:

其它

此处的其它指的是aop:before, aop:after等最核心的标签。其最终被解析为beanClass为AspectJPointcutAdvisor的BeanDefinition,类图:

正如上面结构图里所描述的,其构造参数为一个BeanDefintion,此对象的beanClass是不确定的,由aop:before/after中的before和after决定,代码:

代码语言:javascript复制
private Class<?> getAdviceClass(Element adviceElement, ParserContext parserContext) {
    String elementName = parserContext.getDelegate().getLocalName(adviceElement);
    if (BEFORE.equals(elementName)) {
        return AspectJMethodBeforeAdvice.class;
    } else if (AFTER.equals(elementName)) {
        return AspectJAfterAdvice.class;
    } else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
        return AspectJAfterReturningAdvice.class;
    } else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
        return AspectJAfterThrowingAdvice.class;
    } else if (AROUND.equals(elementName)) {
        return AspectJAroundAdvice.class;
    }
}

而此BeanDefintion的构造参数又由以下三个部分组成:

MethodLocatingFactoryBean

第一个便是beanClass为此类型的BeanDefinition。其内部有一个methodName属性,存储的便是标签的method属性的值。其类图:

这个东西是干什么用的呢?其实是用于在指定的advice(aop:aspect的ref属性)中得到Method对象。入口在setBeanFactory方法:

代码语言:javascript复制
@Override
public void setBeanFactory(BeanFactory beanFactory) {
    Class<?> beanClass = beanFactory.getType(this.targetBeanName);
    this.method = BeanUtils.resolveSignature(this.methodName, beanClass);
}
SimpleBeanFactoryAwareAspectInstanceFactory

其类图:

此类用于在BeanFactory中定位aspect bean,这个bean指的是谁?

代码语言:javascript复制
<bean id="aopAdvice" class="base.aop.AopDemoAdvice" />

就是它!查找很简单:

代码语言:javascript复制
@Override
public Object getAspectInstance() {
    return this.beanFactory.getBean(this.aspectBeanName);
}
总结

从整个aop:aspect标签最终被解析为一个AspectJPointcutAdvisor来看,Spring在实现上仍将其作为Advisor的概念。

代理子类生成

关键在于AspectJAwareAdvisorAutoProxyCreator,此对象在ConfigBeanDefinitionParser的configureAutoProxyCreator方法中注册,其类图:

那么子类生成的入口在哪里呢?

入口

从AspectJAwareAdvisorAutoProxyCreator的类图中可以看出,此类实现了SmartInstantiationAwareBeanPostProcessor接口,所以很容易想到入口应该位于此接口及其父接口(BeanPostProcessor)的相关方法中。实际上确实是这样的。

postProcessBeforeInstantiation

调用时机

先来回顾一下此方法在Bean创建的过程中的调用时机。

AbstractAutowireCapableBeanFactory.createBean部分源码:

代码语言:javascript复制
 Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
    return bean;
}
Object beanInstance = doCreateBean(beanName, mbdToUse, args);

可以看出,调用发生在Bean实例的创建之前。

源码

AbstractAutoProxyCreator.postProcessBeforeInstantiation:

代码语言:javascript复制
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);
    if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }
    if (beanName != null) {
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            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;
}

下面分部分对其进行说明。

应该代理 ?

Spring首先会对当前的beanClass进行检查(是否应该/可以对其进行代理)。

不应该代理的类分为两种情况:

  • 用于实现AOP的Spring基础类,此种情况在isInfrastructureClass方法中完成检测(单词Infrastructure正是基础设施的意思)。
  • 子类定义的应该跳过的类,默认AbstractAutoProxyCreator的实现直接返回false,即都不应该跳过。
基础类检测

AbstractAutoProxyCreator.isInfrastructureClass:

代码语言:javascript复制
protected boolean isInfrastructureClass(Class<?> beanClass) {
    boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
            Pointcut.class.isAssignableFrom(beanClass) ||
            Advisor.class.isAssignableFrom(beanClass) ||
            AopInfrastructureBean.class.isAssignableFrom(beanClass);
    return retVal;
}

可以看出,任何Advice、Pointcut、Advisor、AopInfrastructureBean的子类都被当做Spring实现AOP的基础设施类。

跳过类检测

即shouldSkip方法。前面提到了,AbstractAutoProxyCreator的默认实现直接返回fasle,这一特性被子类AspectJAwareAdvisorAutoProxyCreator重写:

代码语言:javascript复制
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
        if (advisor instanceof AspectJPointcutAdvisor) {
            if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {
                return true;
            }
        }
    }
    return super.shouldSkip(beanClass, beanName);
}

那么此方法跳过的是谁呢?

其实就是我们通过aop:aspect标签配置的切面,即:

代码语言:javascript复制
<bean id="aopAdvice" class="base.aop.AopDemoAdvice" />
<aop:config>
    <aop:aspect ref="aopAdvice">
    </aop:aspect>
</aop:config>

里的aopAdvice。

从前面的aop:aspect一节中可以知道,Spring对于aop:config的解析其实是把aop:before/after等标签解析成为了AspectJPointcutAdvisor类型的BeanDefinition,而aopAdvice以AbstractAspectJAdvice的类型保存在其中。

所以可以得出结论: Spring跳过的是适用于当前bean的Advisor的Advice/Aspect对象

AOP逻辑

那么Spring又是如何找到适用于当前bean的Advisor的呢?

Advisor寻找

关键便是findCandidateAdvisors方法,此方法将逻辑委托给BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans:

代码语言:javascript复制
public List<Advisor> findAdvisorBeans() {
    String[] advisorNames = null;
    synchronized (this) {
         // 结果缓存
        advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
             // 去容器中寻找
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            this.cachedAdvisorBeanNames = advisorNames;
        }
    }
    if (advisorNames.length == 0) {
        return new LinkedList<Advisor>();
    }
    List<Advisor> advisors = new LinkedList<Advisor>();
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            if (!this.beanFactory.isCurrentlyInCreation(name)) {
                advisors.add(this.beanFactory.getBean(name, Advisor.class));
            }
        }
    }
    return advisors;
}

可以看出,首先是从容器中获取到所有的Advisor示例,然后调用isEligibleBean方法逐一判断Advisor是否适用于当前bean。

适用性检测

指的便是isEligibleBean方法。最终调用的是AbstractAdvisorAutoProxyCreator的同名方法:

代码语言:javascript复制
protected boolean isEligibleAdvisorBean(String beanName) {
    return true;
}

而AbstractAdvisorAutoProxyCreator的子类AspectJAwareAdvisorAutoProxyCreator并没有覆盖此方法,所以此处会对容器中所有的Advisor的Advice进行跳过

检测结果缓存

因为postProcessBeforeInstantiation方法会在每个bean初始化之前被调用,所以没有必要每次都真的进行基础类检测和跳过类检测,Spring使用了advisedBeans作为缓存用以提高性能。

TargetSource

从源码中可以看出,对于自定义的TargetSource,Spring会立即执行代理子类的创建。Spring的代理其实是针对TargetSource的,其类图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C8HxE1LE-1632904951652)(images/TargetSource.jpg)]

关于此接口在此不展开叙述。

postProcessAfterInitialization

AbstractAutoProxyCreator.postProcessAfterInitialization:

代码语言:javascript复制
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

关键便在于wrapIfNecessary方法:

代码语言:javascript复制
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //自定义TargetSource,已经进行过代理子类生成
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    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;
}

可以看出,在此方法的开头又进行了基础类以及跳过类的检测,再次不再赘述。

Advisor寻找

即getAdvicesAndAdvisorsForBean方法,这里进行的便是去容器中寻找适用于当前bean的Advisor,最终调用的是

AbstractAdvisorAutoProxyCreator.findEligibleAdvisors:

代码语言:javascript复制
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

findCandidateAdvisors前面已经说过了。

适用性判断

findAdvisorsThatCanApply最终调用AopUtils.findAdvisorsThatCanApply:

代码语言:javascript复制
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    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;
}

关键在于canApply方法,从源码中可以看出,对于Advisor的判断分为了IntroductionAdvisor以及非IntroductionAdvisor两种情况。

这种分开处理导致了IntroductionAdvisor在Advisor链中总是位于非IntroductionAdvisor前面

canApply(candidate, clazz)其实等价于canApply(candidate, clazz, false):

代码语言:javascript复制
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;
    }
}

很明显,对于引入Advisor与其它Advisor是两种不同的判断方式。

引入

引入的概念在下面aop:scoped-proxy中有提到。因为引入的目的在于动态地向一个类添加另一种功能(接口),所以只要判断给定的类是否是要引入到的类即可。

其它

AopUtils.canApply:

代码语言:javascript复制
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    //是否Pointcut可以匹配当前类
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    //是否Pointcut可以匹配所有方法
    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<Class<?>>
        (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;
}

Spring的Pointcut由ClassFilter和MethodMatcher两部分组成,其中前者用以判断给定的类是否在Pointcut的匹配范围内,后者用以在ClassFilter匹配满足的情况下判断给定的方法是否在Pointcut匹配的范围内。

从源码中可以看出,如果ClassFilter匹配得到满足并且Pointcut并不能匹配此类的任意方法,便会用反射的方法获取targetClass(被检测类)的全部方法逐一交由Pointcut的MethodMatcher进行检测

关于Pointcut表达式是如何解析及存储的在此不再展开。

Advisor扩展

AbstractAdvisorAutoProxyCreator.extendAdvisors允许子类向Advisor链表中添加自己的Advisor。子类AspectJAwareAdvisorAutoProxyCreator重写了此方法,其逻辑是:

如果Advisor链表中的Advisor含有AspectJ Advice,那么将会把一个ExposeInvocationInterceptor添加到链表的表头,目的在于将MethodInvocation以ThreadLocal的方式暴露给后面所有的Advisor,暂不知道具体的用途。

排序

即sortAdvisors方法,用于对实现了Ordered接口的Advisor进行排序。

创建

AbstractAutoProxyCreator.createProxy(略去非关键代码):

代码语言:javascript复制
protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
    //将interceptor适配为Advisor
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    for (Advisor advisor : advisors) {
        proxyFactory.addAdvisor(advisor);
    }
    return proxyFactory.getProxy(getProxyClassLoader());
}
JDK动态代理 or Cglib

由DefaultAopProxyFactory.createAopProxy方法决定使用何种方式创建代理子类。

代码语言:javascript复制
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() ||
            hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    } else {
        return new JdkDynamicAopProxy(config);
    }
}

逻辑很明显,如果指定了(proxy-target-classs设为true)使用Cglib,那么就会使用Cglib的方式,如果没有指定(或为false),那么先回检测被代理类是否实现了自己的接口,如果实现了,那么就采用JDK动态代理的方式。

JDK动态代理

JdkDynamicAopProxy.getProxy:

代码语言:javascript复制
@Override
public Object getProxy(ClassLoader classLoader) {
    //找到可以用来进行代理的接口
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    //用来代理的接口中是否定义了equals或者是hashCode方法?
    //结果保存在内部equalsDefined和hashCodeDefined两个成员变量中
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

可以看出,关键的InvocationHandler参数其实就是JdkDynamicAopProxy自身。

其invoke方法较长,源码就不贴了,下面进行分部分说明。

equals & hashCode

如果被代理类实现了equals或者是hashCode方法,那么生成的代理子类的equals、hashCode方法实际上执行的是JdkDynamicAopProxy相应方法的逻辑。

invoke方法部分源码:

代码语言:javascript复制
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
    // The target does not implement the equals(Object) method itself.
    return equals(args[0]);
}
链式调用

对于切点方法,比如前面aop:aspect示例配置中的beforeSend

代码语言:javascript复制
<aop:before method="beforeSend" pointcut-ref="pointcut" />

Spring会创建一个MethodInvocation对象对所有相关的Advisor进行链式调用。invoke相关源码:

代码语言:javascript复制
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
Object retVal = invocation.proceed();
Cglib

同样是对于Advisor的链式调用,不再详细展开。

0 人点赞