博客 【Spring从成神到升仙系列 三】2023年再不会 AOP 源码,就要被淘汰了

【Spring从成神到升仙系列 三】2023年再不会 AOP 源码,就要被淘汰了

   数栈君   发表于 2023-06-19 17:07  220  0

Spring AOP配置
首先创建我们的核心类:BeanA

public class BeanA {
public void do1() {
System.out.println("I am a do1, I start....");
}
}

我们日志配置类:LogUtil

public class LogUtil {

private void before(JoinPoint joinPoint) {
//获取方法签名
Signature signature = joinPoint.getSignature();
//获取参数信息
Object[] args = joinPoint.getArgs();
System.out.println("log---" + signature.getName() + "I am before");
}

private void after(JoinPoint joinPoint) {
//获取方法签名
Signature signature = joinPoint.getSignature();
//获取参数信息
Object[] args = joinPoint.getArgs();
System.out.println("log---" + signature.getName() + "I am before");
}
}

我们 xml 配置:application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<bean id="beanA" class="com.hls.aop.BeanA"/>

<bean id="logUtil" class="com.hls.aop.LogUtil"/>

<aop:config>
<aop:aspect ref="logUtil">
<aop:pointcut id="myPoint" expression="execution(* com.hls.aop.BeanA.do*(..))"/>
<aop:before method="before" pointcut-ref="myPoint"/>
<aop:after method="after" pointcut-ref="myPoint"/>
</aop:aspect>
</aop:config>
</beans>

编写我们的测试类:AOPTest

public class AOPTest {
public static void main(String[] args) {
ApplicationContext context = new GenericXmlApplicationContext("application.xml");

BeanA beanA = context.getBean(BeanA.class);

beanA.do1();
}
}

运行输出结果:

log---do1I am before
I am a do1, I start....
log---do1I am before
1
2
3
四、Spring AOP 组件分析
AOP 的组件在源码中比较重要的有四个:

Pointcut:定义切面的匹配点,主要是类和方法
Advice:定义切面的行为,即在匹配点执行的操作。
Advisor:将 Pointcut 和 Advice 组合成一个对象,表示一个完整的切面。
Aspect:使用注解或 XML 配置方式定义切面,通常包含多个 Advisor
我们依次讲解这些组件是什么意思

1、Pointcut
一般情况下我们的 Pointcut 都是以这种形式出现:

<aop:pointcut id="myPoint" expression="execution(* com.mashibing.hls.aop.BeanA.do*(..))"/>
1
主要就是指明切入点的表达式,比如上述表达式:* com.hls.aop.BeanA.do*(..)

代表 com.hls.aop 包下的 BeanA 类以 do 开头的方法都可以进行切入

我们看下源码中的形式:

public interface Pointcut {
/**
* 在类级别上限定joinpoint的匹配范围
*
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never {@code null})
*/
ClassFilter getClassFilter();

/**
* 在方法级别上限定joinpoint的匹配范围
*
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never {@code null})
*/
MethodMatcher getMethodMatcher();


/**
* 用于匹配上的一个实例,永远返回true
*
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;

}


// Pointcut 的实现
public class AspectJExpressionPointcut extends AbstractExpressionPointcut implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
// 类的匹配
public boolean matches(Class<?> targetClass) {}

// 方法的匹配
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {}
}

通过源码我们可以发现,这个类提供的功能主要是用来匹配类与方法。

2、Advice
一般情况下,我们的 Advice 以这种情况出现:

<aop:before/>
<aop:after/>
1
2
主要的目的就是定义我们切面的行为,比如:方法前切入、方法后切入、环绕切入等

我们看下源码的形式:

public interface Advice {

}

// 前置切入实现
public interface BeforeAdvice extends Advice {}

// 后置切入实现
public interface AfterAdvice extends Advice {}

一般我们的 Advice 都与 Pointcut 相结合使用

3、Advisor
一般情况下,我们的 Advisor 以这种情况出现:

<aop:before method="before" pointcut-ref="myPoint"/>
<aop:after method="after" pointcut-ref="myPoint"/>
1
2
将 Advice 和 Pointcut 结合起来使用

我们看下源码的形式:

public interface Advisor {
// 返回切面对应的通知
Advice getAdvice();
}

很明显可以看出,这个类主要包装了 Advice,后续我们会继续讲到

4、Aspect
一般情况下,我们的 Aspect 以这种情况出现:

<aop:aspect ref="logUtil">
</aop:aspect>
1
2
定义一个日志配置类,其方法作为实际的切入业务逻辑

5、总结
从我们上述的描述得知,我们几个组件的关系如下:

大家这里心里有个大致的了解即可,我们后面会详细讲到。

五、Spring AOP 源码剖析
同样和我们的 IOC 类似,我们从入口开始:

ApplicationContext context = new GenericXmlApplicationContext("application.xml");
1
1、beanDefinition的注册
同样和我们 IOC 流程一样,先进行 xml 文件的解析

我们直接找到 DefaultBeanDefinitionDocumentReader 类的 parseBeanDefinitions 方法

这里从业务来说,做了两件事:

注册了 AspectJAwareAdvisorAutoProxyCreator 的类,方便之后代理的扩展
解析 xml 中 <aop:config> 的配置,最终生成 AspectJPointcutAdvisor 的 BeanDefinition 注册至 DefaultListableBeanFactory 的 BeanDefinitionMap 中
1.1 AspectJAwareAdvisorAutoProxyCreator的注册
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 正常的标签,IOC中讲过
parseDefaultElement(ele, delegate);
}
else {
// <AOP>、<dubbo> 类的标签
delegate.parseCustomElement(ele);
}
}
}
}
}

public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取对应的命名空间
String namespaceUri = getNamespaceURI(ele);

// 根据命名空间找到对应的NamespaceHandlerspring
// 因为我们这里处理的是 AOP 的标签,所以会有 AopNamespaceHandler 的执行
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

// 调用自定义的NamespaceHandler(AopNamespaceHandler)进行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取元素的解析器
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}


public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
// 注册自动代理模式创建器,AspectjAwareAdvisorAutoProxyCreator
configureAutoProxyCreator(parserContext, element);
}


private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}

public static void registerAspectJAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
// 注册名为org.springframework.aop.config.internalAutoProxyCreator的beanDefinition,其中的class类为`AspectJAwareAdvisorAutoProxyCreator`,其也会被注册到bean工厂中
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 如果指定proxy-target-class=true,则使用CGLIB代理,否则使用JDK代理
// 其实其为AspectJAwareAdvisorAutoProxyCreator类的proxyTargetClass属性
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 注册到spring的bean工厂中,再次校验是否已注册
registerComponentIfNecessary(beanDefinition, parserContext);
}

public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
// cls = AspectJAwareAdvisorAutoProxyCreator
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// key = org.springframework.aop.config.internalAutoProxyCreator
// value = org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator
// 这里正式注册了 !!!
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
有读者可能疑惑,这里为什么要大费周折注册一个 AspectJAwareAdvisorAutoProxyCreator 这个类,他有什么特殊的地方嘛

这里先卖个关子,后续我们会讲到

1.2、AspectJPointcutAdvisor 的注册
由于这一段的注册逻辑写的很绕、很晦涩难懂,这里博主带大家大致的浏览下流程

不然,直接劝退了大部分人

我们跟着上面这部分源码继续往下看:

public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
// 注册自动代理模式创建器,AspectjAwareAdvisorAutoProxyCreator
configureAutoProxyCreator(parserContext, element);
// 解析aop:config子节点下的aop:pointcut/aop:advice/aop:aspect
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);
}
// 因为我们上面是 <aop:aspect/> 的标签,会走这个业务逻辑
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}

parserContext.popAndRegisterContainingComponent();
return null;
}

private void parseAspect(Element aspectElement, ParserContext parserContext) {
// <aop:aspect> id属性
String aspectId = aspectElement.getAttribute(ID);
// aop ref属性,必须配置。代表切面
String aspectName = aspectElement.getAttribute(REF);

try {
List<BeanDefinition> beanDefinitions = new ArrayList<>();
List<BeanReference> beanReferences = new ArrayList<>();

// 解析其下的advice节点
NodeList nodeList = aspectElement.getChildNodes();
boolean adviceFoundAlready = false;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
// 是否为advice:before/advice:after/advice:after-returning/advice:after-throwing/advice:around节点
// 这个就是 advice 的种类,不明白的小伙伴可以百度一下
if (isAdviceNode(node, parserContext)) {
// 校验aop:aspect必须有ref属性,否则无法对切入点进行观察操作
if (!adviceFoundAlready) {
adviceFoundAlready = true;
if (!StringUtils.hasText(aspectName)) {
return;
}
beanReferences.add(new RuntimeBeanReference(aspectName));
}
// 解析advice节点并注册到bean工厂中
AbstractBeanDefinition advisorDefinition = parseAdvice(
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
beanDefinitions.add(advisorDefinition);
}
}

AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
parserContext.pushContainingComponent(aspectComponentDefinition);

// 解析aop:point-cut节点并注册到bean工厂
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
parsePointcut(pointcutElement, parserContext);
}

parserContext.popAndRegisterContainingComponent();
}
finally {
this.parseState.pop();
}
}

到这里我们先停一下,从上述源码中我们得到了一些信息,这部分源码的注册基本就三个方面:

解析 Advice 节点并注册到 bean 工厂中
解析 Advisor 节点注册到 bean 工厂中
解析 Pointcut 节点并注册到 bean 工厂
// 整体的注册方法
AbstractBeanDefinition advisorDefinition = parseAdvice(aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);


private AbstractBeanDefinition parseAdvice(String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext, List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences){

// 第一步:解析advice节点中的 method 属性,将其创建成一个 beanDefinition 对象
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
methodDefinition.setSynthetic(true);

// 第二步:关联aspectName,包装为 SimpleBeanFactoryAwareAspectInstanceFactory 对象
RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
aspectFactoryDef.setSynthetic(true);

// 第三步:涉及point-cut属性的解析,并结合上述的两个bean最终包装为AbstractAspectJAdvice通知对象
// 这里面的逻辑也很简单,主要就是通过我们的adviceElement分析是什么类型的 Advice 类型,进行对应的创建
AbstractBeanDefinition adviceDef = createAdviceDefinition( adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef, beanDefinitions, beanReferences);

// 第四步:最终包装为AspectJPointcutAdvisor对象
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);

// 第五步:注册到我们 DefaultListableBeanFactory 的 BeanDefinitionMap 中
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

// 第六步:返回生成好的advisorDefinition
return advisorDefinition;
}

public String registerWithGeneratedName(BeanDefinition beanDefinition) {
String generatedName = generateBeanName(beanDefinition);
getRegistry().registerBeanDefinition(generatedName, beanDefinition);
return generatedName;
}

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){
// 老生常谈的注册逻辑了,之前IOC的时候讲过
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}

1.3 组件源码的结构
这里有一个小细节,面试的时候吹牛逼使用,记住了

我们从我们的第五步,拿出我们的 advisorDefinition,看一下他的构造方法:



我们看到其构造方法实际是一个 AspectJAfterAdvice,我们继续看 AspectJAfterAdvice 的构造方法



其构造方法有三个传参,我们依次看一下什么意思:

MethodLocatingFactoryBean:指明了需要插入的类(logUtil)和方法(after)


BeanReference:存储了切入点的 id,由 myPoint 后续可直接找到 execution(* com.mashibing.hls.aop.BeanA.do*(..))

SimpleBeanFactoryAwareAspectInstanceFactory:内有 BeanFactory 的引用

看完这波,我们可以重新梳理一下关系:



我们最终的组件关系是这种的,但这种的组件关系又会带来一个问题,大家可以先想想

对,就是真正实例化的时候,会特别的复杂

当我们实例化 Advisor 时,需要实例化 Advice,当我们实例化 Advice 时,需要实例化 Method、Pointcut、Factory,直接崩溃

通过上述的讲述,我们终于将 AspectJAwareAdvisorAutoProxyCreator 和 AspectJPointcutAdvisor 注册到了 BeanDefinitionMap 里面

2、AspectJPointcutAdvisor 的创建
上面注册完成之后,就要到我们的创建逻辑了,这里我们直接跳到 AbstractAutowireCapableBeanFactory 的 createBean 方法的 Object bean = resolveBeforeInstantiation(beanName, mbdToUse) 这一行

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
// 如果beforeInstantiationResolved值为null或者true,那么表示尚未被处理,进行后续的处理
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
// 确认beanclass确实在此处进行处理
// 判断当前mbd是否是合成的,只有在实现aop的时候synthetic的值才为true,并且是否实现了InstantiationAwareBeanPostProcessor接口
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 获取类型
Class<?> targetType = determineTargetType(beanName, mbd);
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) {
// 拿到所有的 BeanPostProcessors 的实现类,调用其前置方法
// 主要是我们的 AspectJAwareAdvisorAutoProxyCreator 的调用
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}


public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
//查缓存,是否有处理过了,不管是不是需要通知增强的,只要处理过了就会放里面
// 当前beande
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
// 要跳过的直接设置FALSE
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}


// Advice、Pointcut、Advisor、AopInfrastructureBean直接返回 True
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;
}

// 重点到了
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// 获取所有的 Advisors 类
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}

protected List<Advisor> findCandidateAdvisors() {
// 获取所有的增强处理
return this.advisorRetrievalHelper.findAdvisorBeans();
}
public List<Advisor> findAdvisorBeans() {
// 第一步:拿到所有的 advisorBeanNames
String[] advisorNames = this.cachedAdvisorBeanNames;

// 根据每一个 BeanName 都需要使用 beanFactory 生成实例化
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}

// 得到所有的advisors实例化
return advisors;
}

2.1 Advisor 层级
到这里,我们的 AspectJPointcutAdvisor 已经创建,我们看一下其结构:


这里又回到了这种层级:


这个 Advisor 作为 Advice 和 Pointcut 的对外接口,具体功能还得看 Advice 和 Pointcut

Advice:切入的类、切入的方法、切入的时机
Pointcut:是否匹配类、是否匹配方法
2.2 创建实例的问题
如果你亲自 Debug 源码的话,你会发现它跑了好多的 GetBean() 的次数

这就是我之前提到的创建实例化的时候非常的烦人

好在 Spring 自己也做了一些判断,减少一些不必要的程序运行

比如:

if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}

if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
// 要跳过的直接设置FALSE
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}

3、创建代理对象
我们上面的 Advisor 终于创建完成了,可以开始我们的下一步了

直接跳到 AbstractAutowireCapableBeanFactory 的 initializeBean,如果有读者这里不懂为什么要跳到这里,可以先去阅读一下我的 IOC 源码文章

3.1 前置通知
大家可以直接略过,这里没有实现 AOP 接口

wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
1
3.2 后置通知
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
1
// 这里只关心一个类的后置通知就可以:AspectJAwareAdvisorAutoProxyCreator
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName){
//初始化结果对象为result,默认引用existingBean
Object result = existingBean;
//遍历该工厂创建的bean的BeanPostProcessors列表
for (BeanPostProcessor processor : getBeanPostProcessors()) {
//回调BeanPostProcessor#postProcessAfterInitialization来对现有的bean实例进行包装
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
//直接返回result,中断其后续的BeanPostProcessor处理
return result;
}
//让result引用processor的返回结果,使其经过所有BeanPostProcess对象的后置处理的层层包装
result = current;
}
//返回经过所有BeanPostProcess对象的后置处理的层层包装后的result
return result;
}

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 获取当前bean的key:如果beanName不为空,则以beanName为key,如果为FactoryBean类型,
// 前面还会添加&符号,如果beanName为空,则以当前bean对应的class为key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 判断当前bean是否正在被代理,如果正在被代理则不进行封装
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 如果它需要被代理,则需要封装指定的bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 获取当前bean的Advices和Advisors
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 对当前bean的代理状态进行缓存
if (specificInterceptors != DO_NOT_PROXY) {
// 对当前bean的代理状态进行缓存
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 根据获取到的Advices和Advisors为当前bean生成代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 缓存生成的代理bean的类型,并且返回生成的代理bean
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}

this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}

到这里,我们基本上已经找到代理的入口了,主要做了两件事情

第一:getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null) 校验当前的类、方法是否可以增强

第二:Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); 创建代理类

3.2.1 校验
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 找合适的增强器对象
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
// 若为空表示没找到
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 将当前系统中所有的切面类的切面逻辑进行封装,从而得到目标Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 对获取到的所有Advisor进行判断,看其切面定义是否可以应用到当前bean,从而得到最终需要应用的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 提供的hook方法,用于对目标Advisor进行扩展
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 对需要代理的Advisor按照一定的规则进行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}

对于怎么判断该 Advisor 可以应用到当前类,也就是 findAdvisorsThatCanApply 这个方法

通过 Advisor 内的 Pointcut 进行判断
首先匹配类是否符合规定,然后匹配方法是否符合规定
最终返回我们符合的 List<Advisor>
这里有一个细节点,大家看这行代码:eligibleAdvisors = sortAdvisors(eligibleAdvisors),这里会有一个排序的动作。

这里面主要是用了拓扑排序,如果你不了解什么是拓扑排序,可以先复习一下这篇文章:拓扑排序在顶尖风控团队的业务落地


通过 Advice 之间的联系,将其顺序进行重排,如上图所示,我们排序最终的顺序为:A -> B -> C -> D 或者 A -> C -> B -> D,两者都可

不过,正常情况下应该也没有哪个面试官会这样问,除非特别刁难你

3.2.2 生成代理
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {

// 给bean定义设置暴露属性
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}

// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 获取当前类中相关属性
proxyFactory.copyFrom(this);
// 决定对于给定的bean是否应该使用targetClass而不是他的接口代理,检查proxyTargetClass设置以及preserverTargetClass属性
if (!proxyFactory.isProxyTargetClass()) {
// 判断是 使用jdk动态代理 还是cglib代理
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 添加代理接口
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}

// 构建增强器
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
// 设置到要代理的类
proxyFactory.setTargetSource(targetSource);
// 定制代理
customizeProxyFactory(proxyFactory);

// 控制代理工程被配置之后,是否还允许修改通知,默认值是false
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 真正创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}

public Object getProxy(@Nullable ClassLoader classLoader) {
// createAopProxy() 用来创建我们的代理工厂
return createAopProxy().getProxy(classLoader);
}

Object getProxy(@Nullable ClassLoader classLoader);
3.2.3 创建动态代理对象
这里我们以 CglibAopProxy 举例,不懂动态代理的可复习这篇文章:【Spring从成神到升仙系列 一】2023年再不会动态代理,就要被淘汰了

public Object getProxy(@Nullable ClassLoader classLoader) {
try {
// 从advised中获取ioc容器中配置的target对象
Class<?> rootClass = this.advised.getTargetClass();
Class<?> proxySuperClass = rootClass;
//如果目标对象已经是CGLIB 生成代理对象(就是比较类名称中有 $$ 字符串),那么就取目标对象的父类作为目标对象的类

// Validate the class, writing log messages as necessary.
// 打印出不能代理的方法名,CGLIB 是使用继承实现的,所以final , static 的方法不能被增强
validateClassIfNecessary(proxySuperClass, classLoader);

// Configure CGLIB Enhancer...
// 创建及配置Enhancer
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
// 配置超类,代理类实现的接口,回调方法等
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

// 获取callbacks
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);

// Generate the proxy class and create a proxy instance.
// 通过 Enhancer 生成代理对象,并设置回调
return createProxyClassAndInstance(enhancer, callbacks);
}
}

这里也没有什么好讲的,都是一些配置性的东西。

4、回调
我们上述将代理对象存入到 DefaultListableBeanFactory 中,以便我们更好的获取

// 这里拿到的是代理对象,相应的执行的也是代理对象的方法
BeanA beanA = context.getBean(BeanA.class);
beanA.do1();
1
2
3
我们直接跳到 DynamicAdvisedInterceptor 的 intercept 方法

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {

// 从advised中获取配置好的AOP通知
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

else {
// 通过cglibMethodInvocation来启动advice通知
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
}

这里一共有三个比较重要的点:

getInterceptorsAndDynamicInterceptionAdvice :从 Advisor 得到相对应的 Advice
new CglibMethodInvocation():创建责任链
proceed():执行责任链
我们重点来看看 proceed() 这个方法:

public Object proceed() throws Throwable {
return super.proceed();
}

public Object proceed() {
// 从索引为-1的拦截器开始调用,并按序递增,如果拦截器链中的拦截器迭代调用完毕,开始调用target的函数,这个函数是通过反射机制完成的
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}

// 获取下一个要执行的拦截器,沿着定义好的interceptorOrInterceptionAdvice链进行处理
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 普通拦截器,直接调用拦截器,将this作为参数传递以保证当前实例中调用链的执行
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}

这个方法稍微的有点绕,这里先来说下这里的整体思路:递归 + 责任链



简单的来说一下,首先这个责任链不是我们传统那种上下文连串的责任链,比如 Netty:【Netty 从成神到升仙系列 五】Netty 的责任链真有这么神奇吗?

因为他的责任链里面的对象各不相同,所以他用了两个对象的 procesd 方法来代替并且使用下标代表当前所处的位置

这里讲一下 AspectJAfterAdvice、AspectJMethodBeforeAdvice 、invokeJoinpoint();

对于 AspectJAfterAdvice 来说,它本身属于目标方法之后才去执行的,所以当最开始到达他时,他并不能执行属于自己的方法,因为目标方法还未执行。等执行完之后,再去执行自己的后置方法 invokeAdviceMethod

对于 AspectJMethodBeforeAdvice 来说,它本身属于目标方法执行之前,所以当最开始到达他时,直接执行即可

对于 invokeJoinpoint 来说,他属于目标方法,所有的 Advice 遍历一遍之后,这个时候改 before 执行的都已经执行完了,所以直接执行即可。

这个链式结构设计的非常巧妙,利用递归的思想将各种 Advice 切到目标方法前后。

六:流程图
本文流程图可关注公众号:爱敲代码的小黄,回复:AOP 获取
贴心的小黄为大家准备的文件格式为 POS文件,方便大家直接导入 ProcessOn 修改使用

  • 免责申明:

    本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!

  • 《数据治理行业实践白皮书》下载地址:https://fs80.cn/4w2atu


  • 《数栈V6.0产品白皮书》下载地址:https://fs80.cn/cw0iw1

  • 想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs

    同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:
    https://github.com/DTStack

0条评论
社区公告
  • 大数据领域最专业的产品&技术交流社区,专注于探讨与分享大数据领域有趣又火热的信息,专业又专注的数据人园地

最新活动更多
微信扫码获取数字化转型资料
钉钉扫码加入技术交流群