概述
用过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 中动态生成代理类的方式有两种
两者具体怎样使用,这个本文不做论述
准备demo
首先是一个spring 入口
@ComponentScan("bean")@EnableAspectJAutoProxypublic class Application { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class); ComService bean = context.getBean(ComService.class); bean.testAop(); }}被代理的对象
@Servicepublic class ComService { public void testAop(){ System.out.println("===== ComService ===="); }}切面定义
@Aspect@Componentpublic 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 aspectNames = this.aspectBeanNames;// 这个方法不只调用一次if (aspectNames == null) { List 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
最后
欢迎大家在下方留言你们的源码阅读技巧,我也想学习下 |