用过spring 框架的小伙伴都知道,aop对spring 的重要性,学习源码必不可少,文本记录一些源码跟踪源码技巧学习心得,需要纠错和改正的请在下方留言
这个网上一搜一大堆,重复阐述没有意义,说下我个人理解,关键两个字 代理
什么叫代理,和银行一样,你干啥都要经过人家的手,这样只要你钱有变动银行都知道了
spring 也是一样,假设有个类
public class A{
void test(){
system.out.println("test run");
}
}
我们需要在A中的test() 做个切面
此时spring 会生成一个代理后对象,这个对象才是你实际真正调用的,这样你调用方法时不就形成了切面
可以这样理解
class Proxy$1{
A a;
test(){
a.test();
}
}
这个也可以看做是静态代理
如果有多个代理,那么就可能如下图所示
但是这个代码又不是写死的,所以代理就不能是静态的,又称动态代理
spring 中动态生成代理类的方式有两种
两者具体如何使用,这个本文不做阐述
首先是一个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
欢迎大家在下方留言你们的源码阅读技巧,我也想学习下