(分析基于Spring的版本:version = 4.1.6.RELEASE)

SpringAOP 和 AspectJ 的关系:
它们是两种不同的编程风格, SpringAOP 使用 xml 配置的形式配置 aop。而 AspectJ 使用 AspectJ 的注解来配置 aop

aspect、JoinPoint、Pointcut、Weaving、Advice
JoinPoint: 连接点。表示目标对象中的方法
Pointcut: 切点。表示连接点的集合
Weaving: 织入。把代理逻辑加入到目标对象上的过程叫织入
Advice: 通知。包括 “around”, “before” and “after 等

this、target
this: 产生的代理对象
target: 被代理的原始对象

JoinPoint、ProceedingJoinPoint
JoinPoint 接口可以拿到连接点的相关信息,比如:方法签名、方法参数、this、target
ProceedingJoinPoint 继承自 JoinPoint,它是用来支持环绕(around)通知的,多暴露了一个 proceed() 方法,用来执行目标对象的方法。

Spring AOP工厂: org.springframework.aop.framework.DefaultAopProxyFactory

  @Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}

从代码可以看出:
1. 如果AdvisedSupport中没有进行自定义设置,Spring去创建代理的时候,会默认使用JDK动态代理。
2. 如果AdvisedSupport中进行了设置,那么就去判断被代理的类是不是接口,如果是接口的话就使用JDK动态代理,否则才使用Cglib的代理。

(2020.1.17 更新)
从源码看, proxyTargetClass=true 时,如果 targetClass 是接口 or targetClass 是一个代理类型,也有可能走 jdk 动态代理。但是绝大多数情况是不会的。
首先,在源码中找到 targetClass 赋值的地方,可以发现 targetClass 是 bean 的实例的 class,所以通常是一个实体类,不是接口。至于 targetClass 什么时候会是代理类,还不太清楚。

那既然 targetClass 是 bean 的实例的 class 的话,它并不是一个接口,那么当 proxyTargetClass=false 的时候, jdk 怎么来产生代理呢?
答案是,spring 会找到 targetClass 对应的接口来生成代理,如果这个类没有接口的话,会将 proxyTargetClass 设置成 true,从而使用 cglib 来生成代理

Spring 基于JDK动态代理的AOP实现解析:
我们知道,使用JDK的动态代理,主要是实现 java.lang.reflect.InvocationHandler#invoke(Object obj, Method method, Object[] aobj)方法。
Spring相关的源码如下:JdkDynamicAopProxy.class

  @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null; try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
} Object retVal; if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
} // May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
} // Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
} // Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}

从上面可以看出,spring 会先拿到一个 MethodInterceptor 的 chain 。

如果chain为空的话,就直接反射调用原方法;否则,就会根据这个 chain 来生成一个 MethodInvocation ,然后去执行 invocation.proceed() 。
invocation.proceed() 方法的实现,是递归调用 chain 中的 MethodInterceptor。

也就是说,我们所有的AOP拦截,都是通过一连串的 MethodInterceptor 来实现的。
而这个 MethodInterceptor 的 chain 是通过 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 来生成的。
也就是基于 AdvisedSupport 来生成的。

Spring 对 AdvisedSupport 的注释是:Base class for AOP proxy configuration managers. 也就是说,AdvisedSupport 是产生AOP代理的配置管理者。

AdvisedSupport 中保存了一个List<Advisor> advisors 的集合。
满足条件的Advisor(即:能对此method进行拦截的Advisor),最终会被转换成 MethodInterceptor ,从而添加到chain中。

Spring 基于Cglib的AOP实现解析:
我们知道,使用Cglib实现AOP时,最主要的是要设置Callback来对Method进行拦截,即:void net.sf.cglib.proxy.Enhancer.setCallback(Callback callback) 。

spring生成Callback的源码如下:CglibAopProxy.class

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimisation choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic(); // Choose an "aop" interceptor (used for AOP calls).
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); ..............
}

从上面的代码可以看出,spring在生成 Callback 时,也是基于 AdvisedSupport 来的(同JDK动态代理生成 MethodInterceptor 的chain一样)。

CglibAopProxy$DynamicAdvisedInterceptor.class

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

        private final AdvisedSupport advised;

        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
} @Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
retVal = methodProxy.invoke(target, args);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
...............
}

不难看出,最终JDK动态代理 与 Cglib代理 对AOP的实现都是基于 AdvisedSupport 的 MethodInterceptor 链。

参考:

http://www.jianshu.com/p/0734ec855183

http://www.cnblogs.com/digdeep/p/4528353.html

http://my.oschina.net/robinyao/blog/649518

http://lgbolgger.iteye.com/blog/2180251

http://www.cnblogs.com/chanedi/p/4552555.html  (原码说明)

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

class CglibAopProxy implements AopProxy, Serializable {

Spring 异步@Async也是基于Spring AOP来做的:
在bean创建完成后,会调用 AsyncAnnotationBeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName) 来生成一个代理bean

2018.07.23 add:
编程式使用aop  https://blog.csdn.net/qq_26525215/article/details/52422395/

2019.01.11 add:
spring @Transactional 的原理解析:
TransactionAspectSupport#completeTransactionAfterThrowing  对 @Transactional 事务的处理,默认回滚 RuntimeException
https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#tx-decl-explained
https://doanduyhai.wordpress.com/2011/11/20/spring-transactional-explained/

Spring AOP 由浅入深的更多相关文章

  1. Spring AOP 系列总括

    Spring有两大核心,IOC和AOP.IOC在Java Web项目中无时无刻不在使用,然而AOP用的比较少,尤其是对一些初级程序员,在架构师搭好的框架上开发应用代码,AOP几乎是透明的.然而,项目中 ...

  2. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

  3. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  4. spring aop

    什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将 ...

  5. spring aop注解方式与xml方式配置

    注解方式 applicationContext.xml 加入下面配置 <!--Spring Aop 启用自动代理注解 --> <aop:aspectj-autoproxy proxy ...

  6. 基于Spring AOP的JDK动态代理和CGLIB代理

    一.AOP的概念  在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...

  7. Spring AOP详解

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  8. Spring AOP实例——异常处理和记录程序执行时间

    实例简介: 这个实例主要用于在一个系统的所有方法执行过程中出线异常时,把异常信息都记录下来,另外记录每个方法的执行时间. 用两个业务逻辑来说明上述功能,这两个业务逻辑首先使用Spring AOP的自动 ...

  9. 从零开始学 Java - Spring AOP 实现用户权限验证

    每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...

随机推荐

  1. 第三个Sprint冲刺第十天

    讨论地点:宿舍 讨论成员:邵家文.李新.朱浩龙.陈俊金 讨论问题:做最后的工作

  2. sql server 中xml 数据类型的insert、update、delete

    近日对SQL操作XML作了如下整理: 1.插入 XML DECLARE @myDoc XMLSET @myDoc = '<Root> <ProductDescription Prod ...

  3. Python 基礎 - 集合的使用

    集合是一個無序的,不重複的數據組合,主要的作用如下 去重,把一個列表變成集合,就會自動去重了. 關係測試,測試二組數據之前的交集.差集.聯集等關係. 接下來我們來實作看看什麼是去重 #!/usr/bi ...

  4. 腾讯优测优分享 | Android性能测试工具化实现

    腾讯优测专业的移动云测试平台,自动化测试提供性能测试数据,云真机实时输出性能数据,让测试更简单! 1.Android性能测试介绍 提到Android性能测试,我们总免不了俗地要首先介绍下Android ...

  5. 评价qq拼音输入法

    我目前正在使用qq拼音输入法,从人机交互设计方面,我对qq输入法从用户界面.记住用户选择.短期刺激和长期使用的好处坏处.不让用户犯简单错误这四个方面进行了评价. 1.从用户界面方面: qq输入法用户界 ...

  6. JavaScript设计模式:读书笔记(未完)

    该篇随我读书的进度持续更新阅读书目:<JavaScript设计模式> 2016/3/30 2016/3/31 2016/4/8 2016/3/30: 模式是一种可复用的解决方案,可用于解决 ...

  7. Apache+PHP+MySQL

    Apache:是一种web服务器(与IIS类同)PHP:全称为Hypertext Preprocessor.PHP是一种HTML 内嵌式的语言,是一种网站(网页)开发语言(与ASP.JSP..NET等 ...

  8. 简单的 JSON 对象进行深拷贝最简单的方法

    var json = { a: 123, b: '456' }; var json2 = JSON.parse(JSON.stringify(json)); 只需要先使用 JSON.stringify ...

  9. java多线程基础

    多线程基础 读书练习照猫画虎 package Threadtest; import java.util.Date; import java.util.concurrent.ArrayBlockingQ ...

  10. iOS初步开发

    趁公司目前iOS整个没人管理,我折腾一下调试工具,刚好nordic也有参考demo. 然后作为helloworld级别的我... 就直接down下来,结果,还不错, 不像oschina.net和cod ...