前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring-aop 之 aop:config

spring-aop 之 aop:config

作者头像
MickyInvQ
发布2021-10-13 17:48:11
1.1K0
发布2021-10-13 17:48:11
举报
文章被收录于专栏:InvQ的专栏InvQ的专栏

文章目录

开头

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的链式调用,不再详细展开。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-10-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 开头
  • aop:config
    • 解析
      • proxy-target-class & expose-proxy
      • aop:pointcut
      • aop:advisor
      • aop:aspect
    • 代理子类生成
      • 入口
      • postProcessBeforeInstantiation
      • postProcessAfterInitialization
相关产品与服务
云顾问
云顾问(Tencent Cloud Smart Advisor)是一款提供可视化云架构IDE和多个ITOM领域垂直应用的云上治理平台,以“一个平台,多个应用”为产品理念,依托腾讯云海量运维专家经验,助您打造卓越架构,实现便捷、灵活的一站式云上治理。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档