概述

用过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 stopwatchObject retVal = pjp.proceed();System.out.println(pjp.getSignature().getName() + " === 切面");// stop stopwatchreturn 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

最后

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

Spring Aop 源码笔记和源码阅读个人技巧分享相关推荐

  1. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  2. 【Spring】Spring AOP源码分析-导读(一)

    文章目录 1.简介 2.AOP 原理 3.AOP 术语及相应的实现 3.1 连接点 - Joinpoint 3.2 切点 - Pointcut 3.3 通知 - Advice 3.4 切面 - Asp ...

  3. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  4. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  5. Spring AOP 源码系列(一)解析 AOP 配置信息

    在进行源码阅读之前建议先看一下这篇文章:Spring AOP 源码分析系列文章导读 by 田小波,写的非常好,推荐阅读. 关于 AOP 中常用的一些术语这里就不解释了,如果不清楚的建议先看一遍上面推荐 ...

  6. 一步一步手绘Spring AOP运行时序图(Spring AOP 源码分析)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  7. Spring AOP源码(1)—<aop:config/>AOP配置标签解析【一万字】

      基于最新Spring 5.x,对Spring AOP中的<aop:config/>标签的解析源码进行了详细分析,这是Spring AOP源码的入口!   此前我们已经详细学习了Spri ...

  8. spring AOP源码分析(一)

    spring AOP源码分析(一) 对于springAOP的源码分析,我打算分三部分来讲解:1.配置文件的解析,解析为BeanDefination和其他信息然后注册到BeanFactory中:2.为目 ...

  9. 轻轻松松看懂Spring AOP源码

    轻轻松松看懂Spring AOP源码 https://baijiahao.baidu.com/s?id=1596466083334197175&wfr=spider&for=pc 如果 ...

  10. Spring AOP源码解析-拦截器链的执行过程

    一.简介 在前面的两篇文章中,分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在得到了 bean 的代理对象,且通知也以合适的方式插在了目标方 ...

最新文章

  1. @Configurattion注解的作用及一些列组合使用
  2. 我为什么用 SQLite 和 FMDB 而不用 Core Data
  3. 开源之旅之开源企业软件采购指南
  4. 多线程下单例模式的实现_ThreadLocal_ReentrantLock
  5. 中筛选为某个列表中_关于Excel中“高级筛选”的这些技巧,必须掌握!
  6. 下面是一个python函数、哪个说法不正确_2017秋python语言程序设计(北京信息科技大学)答案...
  7. Oracle 自动生成的视图VM_NSO_1
  8. Eclipse 安装 svn
  9. 预测大盘最准确的指标_炒股天才江恩一辈子研究的指标,准确预测大盘走势
  10. python列表的负数索引
  11. python爬虫入门 之 requests 模块
  12. win7此计算机与未识别的网络连接,win7本地连接未识别的网络怎么办_win7本地连接未识别的网络怎么解决-win7之家...
  13. 遇到了 “遇到以零作除数错误” 的问题
  14. c语言开发dota,DOTA 6.70C AI简体中文修正版下载
  15. 2022危险化学品经营单位主要负责人考试试题及在线模拟考试
  16. 统计学基础之数据分布
  17. Java break语句详解
  18. C#安装Newtonsoft.Json并调用
  19. 中国区块链标准建设分析
  20. 【Docker】(八) Docker可视化工具Portainer(汉化)

热门文章

  1. oracle定时清理回收站,电脑设置定时清理回收站的操作方法
  2. kodi教程_KODI添加电视直播+修改台标教程
  3. 2018计算机本科论文,计算机科学学院关于2018届本科毕业论文(设计)的工作安排...
  4. 自动驾驶 8-3: 递归最小二乘法Recursive Least Squares
  5. 容器技术Docker K8s 11 容器服务Kubernetes版ACK详解-集群查看及管理
  6. 架构师架构蓝图《UML精粹》 UML Distilled读后感
  7. 151.翻转字符串里的单词
  8. 树中两个节点的最低公共祖先
  9. php5.6软件下载,【PHP下载】PHP for Linux 5.6.6-ZOL软件下载
  10. 现代通信原理4.3:白噪声