浅析 Spring Aop
aop也是Spring里比较重要的一个点,最近把源码看了下,这里总结一下

使用上主要就下面的点注意下:
- 相关的Annotaion
- Around
- Before
- After
- AfterReturning
- AfterThrowing
- 执行顺序
- expression表达式
- execution
- args
- @args()
- this()
- target()
- within()
- @within()
- @annotation
- 处理函数的相关参数
- ProceedingJoinPoint
- returning (AfterReturning 可以设置)
- Exception (AfterThrowing 可以设置)
- 然后其他的参数,或者target,annotaion之类的,可以通过expression拿到(除了execution)
然后主要针对源码总结一下
1. 注册逻辑
正常通过下面的配置来实现配置
<aop:aspectj-autoproxy proxy-target-class="true"/>
然后最后会加载AnnotationAwareAspectJAutoProxyCreator
AopNamespaceHandler 会解析aspectj-autoproxy
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
AspectJAutoProxyBeanDefinitionParser 会调用
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
最后会注册一个AnnotationAwareAspectJAutoProxyCreator, 这个就是
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
注入proxy-target-class (默认false,true的化,直接走cglib动态代理,不走jdk)
注入expose-proxy
解决 自己调用自己不能被spring监控的问题
设为true之后,可以使用 (MyService)(AopContext.currentProxy()).doAnotherFunction()
2. 加强的时机
加强逻辑在AbstractAutoProxyCreator中
proxy逻辑在是在wrapIfNecessary(bean, beanName, cacheKey);
可以看到加强的地方是在
- SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference
- InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
这个调用的地方,和为什么这么使用可以看下 浅述 Spring 代理如何在循环依赖中解决的
3. 具体加强的判断
主要在wrapIfNecessary中
- 判断是否需要代理
过滤Advice等不需要代理的类 - 获取所以可以适用于这个类的Advice
- 根绝advice生成代理类
// 判断是否这个bean需要被代理
// isInfrastructureClass 如果是基础类,就不需要被代理 Advice, Advisor, AopInfrastructureBean, 或者实现了@Aspect
// shouldSkip 这里处理配置 Advice配置的aspectName
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 获取所以可以适用于这个类的Advice
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 这里创建代理类
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
3.1 获取所以可以适用于这个类的Advice
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//找到所有的Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 找到适应于这个bean的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// 排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
}
- 找到所有的Advisor
- 找到所有的Advisor类
- 然后找到@Aspect注解的Class,通过advisorFactory.getAdvisors(factory)获取
- 得到该class下除了PointCut外所有的函数
- 按照 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 排序
- 获取没个函数对应的annotaion 获取该函数的annotaion : Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class
- 对于每个标类注解的InstantiationModelAwarePointcutAdvisorImpl, 这是一个advisor
- 会针对不同的annotaion返回不通的Advice (atAspectJAdvisorFactory.getAdvice)
- Before : AspectJMethodBeforeAdvice
- After : AspectJAfterAdvice
- Around : AspectJAroundAdvice
- AfterReturning : AspectJAfterReturningAdvice
- AfterThrowing : AspectJAfterThrowingAdvice
- 找出可以Apply的Advisor
- 这里就是把Advisor的表达式取出来和bean的class的每个函数match一遍,判断是否符合
- 排序Advisor
- AspectJPrecedenceComparator进行排序
- 会根绝Aspect类对应的order值来进行排序
- 同一个类里的会根据Annotaion的类型来排序
- afterThrow
- afterReturning
- after
- around
- before
- 同一个annotaion就是根据名字排序了
- around, before 是名字在前的在前
- after系列是 名字在后的在前
- 这里的目的就是名字在前的先执行
- AspectJPrecedenceComparator进行排序
这里画了一个排序的图:
Advice1 的优先级 高于Advice2 
3.2 生成代理
通过cglib或者jdk生产动态代理
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
JdkDynamicAopProxy
实现了InvocationHandler
获取适用的advices
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
然后生成ReflectiveMethodInvocation执行
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
ObjenesisCglibAopProxy
生成对应的callbacks(MethodInterceptor),设置到Enhancer里
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
enhancer.setInterceptDuringConstruction(false);
enhancer.setCallbacks(callbacks);
return (this.constructorArgs != null ?
enhancer.create(this.constructorArgTypes, this.constructorArgs) :
enhancer.create());
}
浅析 Spring Aop的更多相关文章
- 浅析Spring AOP
在正常的业务流程中,往往存在着一些业务逻辑,例如安全审计.日志管理,它们存在于每一个业务中,然而却和实际的业务逻辑没有太强的关联关系. 图1 这些逻辑我们称为横切逻辑.如果把横切的逻辑代码写在业务代码 ...
- 做一个合格的程序员之浅析Spring AOP源代码(十八) Spring AOP开发大作战源代码解析
事实上上一篇文章价值非常小,也有反复造轮子的嫌疑,网上AOP的实例非常多,不胜枚举,事实上我要说的并非这个,我想要说的就是上一节中spring的配置文件: 我们这边并没实用到我们上几节分析的哪几个AO ...
- Spring Boot -- Spring AOP原理及简单实现
一.AOP基本概念 什么是AOP,AOP英语全名就是Aspect oriented programming,字面意思就是面向切面编程.面向切面的编程是对面向对象编程的补充,面向对象的编程核心模块是类, ...
- 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- Spring Aop 应用实例与设计浅析
0.代码概述 代码说明:第一章中的代码为了突出模块化拆分的必要性,所以db采用了真实操作.下面代码中dao层使用了打印日志模拟插入db的方法,方便所有人运行demo. 1.项目代码地址:https:/ ...
- 02 浅析Spring的AOP(面向切面编程)
1.关于AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.O ...
- 深入浅析Spring的AOP实现原理
转载来源:https://www.jb51.net/article/81788.htm AOP(Aspect-OrientedProgramming,面向切面编程),可以说是OOP(Object-Or ...
- jdk动态代理与cglib代理、spring Aop代理原理-代理使用浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- 浅析Spring中AOP的实现原理——动态代理
一.前言 最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ...
随机推荐
- PAT L1 - 046 整除光棍
https://pintia.cn/problem-sets/994805046380707840/problems/994805084284633088 这里所谓的“光棍”,并不是指单身汪啦~ 说的 ...
- 一个form表单,多个提交按钮
技巧就是把提交的input的类型改成button!这样就可以实现多个按钮提交! 以下是案例: <form action="" id="tijiao"> ...
- Codeforces 627D Preorder Test(二分+树形DP)
题意:给出一棵无根树,每个节点有一个权值,现在要让dfs序的前k个结点的最小值最大,求出这个值. 考虑二分答案,把>=答案的点标记为1,<答案的点标记为0,现在的任务时使得dfs序的前k个 ...
- 转---秒杀多线程第八篇 经典线程同步 信号量Semaphore
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...
- BZOJ5072 小A的树(树形dp)
容易猜到能选择的黑点个数是一个连续区间.那么设f[i][j]为i子树内选j个点形成包含根的连通块,最多有几个黑点,g[i][j]为最少有几个黑点,暴力dp是O(n2)的,求出每个连通块大小对应的黑点数 ...
- Contest 1
A:注意到模数是要求lcm的数的倍数,直接先取模就可以了.考场脑抽,对其质因数分解判了一下每个因子有没有,当然也行. #include<iostream> #include<cstd ...
- Java日期格式转换
Java时间格式转换大全 import java.text.*;import java.util.Calendar;public class VeDate {/** * 获取现在时间 * ...
- HDU 4383 To The Moon 解题报告
HDU 4383 To The Moon 题意翻译 已知一个长为\(n\)的序列\(a\),你需要进行下面的四种操作. C l r d 将区间\([l,r]\)中的数加上\(d\),同时时间加\(1\ ...
- YII2 model where 条件拼接
熟悉Yii2的查询条件后,用Active Record查询数据非常方便. 以下我们介绍where()方法当中,条件的拼装方式. #某个值为null,会用IS NULL来生成语句: ['type' =& ...
- selenium测试 - open Firefox
环境:Python2.7+selenium3+Firefox47 问题1: 在打开火狐浏览器时报错:‘geckodriver‘ executable needs to be in PATH fro ...