Spring Aop 源码笔记和源码阅读个人技巧分享

7 篇文章 0 订阅

概述

用过spring 框架的小伙伴都知道,aop对spring 的重要性,学习源码必不可少,文本记录一些源码跟踪源码技巧学习心得,需要纠错和改正的请在下方留言

aop 原理大致分析

这个网上一搜一大堆,重复阐述没有意义,说下我个人理解,关键两个字 代理
什么叫代理,和银行一样,你干啥都要经过人家的手,这样只要你钱有变动银行都知道了

spring 也是一样,假设有个类

public class A{
	void test(){
		system.out.println("test run");
	}
}

我们需要在A中的test() 做个切面
此时spring 会生成一个代理后对象,这个对象才是你实际真正调用的,这样你调用方法时不就形成了切面
可以这样理解

class Proxy$1{
	A a;
	test(){
		a.test();
	}
}

这个也可以看做是静态代理
如果有多个代理,那么就可能如下图所示
在这里插入图片描述
但是这个代码又不是写死的,所以代理就不能是静态的,又称动态代理

spring 中动态生成代理类的方式有两种

  • cglib
  • jdk代理

两者具体如何使用,这个本文不做阐述

准备demo

首先是一个spring 入口

@ComponentScan("bean")
@EnableAspectJAutoProxy
public class Application {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
		ComService bean = context.getBean(ComService.class);
		bean.testAop();

	}
}

被代理的对象

@Service
public class ComService {


	public void testAop(){
		System.out.println("===== ComService ====");
	}
}

切面定义

@Aspect
@Component
public class Aop {
	@Pointcut("execution(public * bean.service.ComService.testAop())")
	public void businessService() {}

	@Around("businessService()")
	public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
		// start stopwatch
		Object retVal = pjp.proceed();
		System.out.println(pjp.getSignature().getName() + " === 切面");
		// stop stopwatch
		return retVal;
	}
}

源码分析

其实网上这样源码解析的帖子很多,但是都有共同点,都是上来直接告诉你这代码在哪看,然后贴上一些他们的理解注释,但是作为一个刚刚学习spring 或者对spring 源码研究不深的开发小伙伴来说,我想知道你是咋找到这代码的,本文分享我一步一步找到关键源码的地方,可能技巧很low,但是挺实用,大部分框架源码我如果不事先看博客都是这么找到的

这里主要回答两个问题

什么时候解析的切面

项目中我们表示切面是通过在类上加入注解 @Aspect 实现的,
那么肯定有代码是解析这个注解的
最简单的方式直接看看哪里调用就完了,我是直接下载了spring 的源码,这样一点就行了
在这里插入图片描述
可以看到 org.aspectj.internal.lang.reflect.AjTypeImpl#getPerClause 有get操作
然后在看看这个方法哪里有被调用
在这里插入图片描述
到这步可以在一些你认为可能的方法中打断点,看看会不会进来(方法很low但是对我这样的小白我觉得还挺实用)

// 代码简写了
List<String> aspectNames = this.aspectBeanNames;
// 这个方法不只调用一次
if (aspectNames == null) {
  List<Advisor> advisors = new ArrayList<>();
  aspectNames = new ArrayList<>();
  String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
    this.beanFactory, Object.class, true, false);
  for (String beanName : beanNames) {
    ....
    // 判断是否有切面注解
    // 上文猜测找的类不是这里调用方法,所以找这个代码有点侥幸
    if (this.advisorFactory.isAspect(beanType)) {
      aspectNames.add(beanName);
      AspectMetadata amd = new AspectMetadata(beanType, beanName);
      // 这里就会调用我之前猜测的方法
      // 下面都会调用一个方法   advisors.addAll(this.advisorFactory.getAdvisors(factory));
      if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
       ...
      }
      else {
        ...
      }
    }
  }
  this.aspectBeanNames = aspectNames;
  return advisors;
}
		

如何生成的代理类

这里找源码有些讨巧,前段时间刚刚学习了下spring bean生命周期的源码,所以直接在DefaultSingletonBeanRegistry#getSingleton()
debug,写个断点条件
在这里插入图片描述
然后一点点放行代码,看着返回值obj如果发生了变化那就找到了
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
到这方法中对象已经初步创建了
在这里插入图片描述

在这里插入图片描述
可以看到 AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization 这行代码之后
这个对象已经不是原来的对象了
找到之后就开始慢慢分析了

Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
	Object current = processor.postProcessAfterInitialization(result, beanName);
	if (current == null) {
		return result;
	}
	result = current;
}

同理也是打断点
可以找到 AbstractAutoProxyCreator#postProcessAfterInitialization --> AbstractAutoProxyCreator#wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
...

	// 创建切面代理
	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;
}

一直往下 ProxyFactory#getProxy

	// createAopProxy() 这个方法返回了 实现了 AopProxy 对象
	return createAopProxy().getProxy(classLoader);

DefaultAopProxyFactory#createAopProxy 这里决定了生成代理类的方式

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  if (!IN_NATIVE_IMAGE &&
      // 是否优化
      (config.isOptimize() || 
       // 是否直接代理以及是接口
       config.isProxyTargetClass() ||
       // 判断是否是实现了 org.springframework.aop.SpringProxy 接口的类
       hasNoUserSuppliedProxyInterfaces(config))) {
    // 需要被代理的类
    Class<?> targetClass = config.getTargetClass();
    if (targetClass == null) {
      throw new AopConfigException();
    }
    // 如果是接口
    if (targetClass.isInterface() 
        // 本身是否是代理类
        || Proxy.isProxyClass(targetClass)) {
      return new JdkDynamicAopProxy(config);
    }
    return new ObjenesisCglibAopProxy(config);
  }
  else {
    return new JdkDynamicAopProxy(config);
  }
}

简单说就是 如果被代理的类是个接口、或者本身就是个代理类那么用jdk代理方式,否则就是Cglib代理
因为jdk只能代理接口

不管是jdk代理方式还是Cglib代理方式,目的都是生层代理类
下面就想知道如果有多个切面怎么办,

org.springframework.aop.framework.JdkDynamicAopProxy#invoke
在这里插入图片描述

org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)
在这里插入图片描述
在上面的代码中debug 一下就知道一个bean有哪些方法在切面中

感悟

知道了这个aop大致的流程之后,开发中我们遇到问题就可以有个大概的思路,比如说常见的aop失效问题
大致概括有两个方面

  • 切面没有解析到
  • 代理类没有生成

大部分问题都可以通过debug解决,前提是你得知道在哪里debug

最后

欢迎大家在下方留言你们的源码阅读技巧,我也想学习下

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值