spring aop 都是动态代理,分为jdk代理和cglib代理。默认的情况下,如果类有实现了接口,使用jdk代理。如果没有实现接口,则使用cglib代理。在下面的代码中,我会标明对应的这段代码。

和以前一样,关键代码我会标红色。

首先,分析jdk代理。之前一直找不到jdk代理的源码入口。后来网上看大神的源码分析,在结合自己写的例子,终于是发现了入口,入口的关键点,是InvocationHandler。由于jdk代理也实现了这个接口,所以也实现了自己的invoke方法。代码如下:

/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
@Nullable
//实现invoke方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource;
Object target = null; try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
//不实现equals方法

return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
                //不实现hasCode方法                
return hashCode();
            }
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
// 循环调用,最终返回单例的目标类
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...

//使用代理配置对ProxyConfig的服务调用

return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
} Object retVal; if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// 提供调用

oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
} // Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null); // 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.
// 检查是否有通知。如果没有,则直接反射调用目标类,不会创建方法调用(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.
// 允许直接执行目标类,而不创建方法调用invocation。注意,最终的调用者依然是InvokerInterceptor,尽管只是直接反射操作,没有任何的拦截。

Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); //适配器模式
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); //直接使用反射调用
}
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 != Object.class && 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);
}
}
}

先跟进 getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)

/**
* Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
* for the given method, based on this configuration.
* @param method the proxied method
* @param targetClass the target class
* @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
*/
//基于方法的配置,获取它的拦截器 集合。这里可以看出,拦截器 集合是一个list,放在map中,map的key则是方法名称和hascode组成

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) { // This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); //检查是否有匹配配置的类
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); for (Advisor advisor : config.getAdvisors()) { //遍历advisor
if (advisor instanceof PointcutAdvisor) { //如果是切入点的的advisor(方法级别)
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { //检查经过过滤的类是否还匹配advisor
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) { //检查匹配的方法
MethodInterceptor[] interceptors = registry.getInterceptors(advisor); //获取方法拦截器
if (mm.isRuntime()) { //如果是运行时起作用的拦截器
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); //添加到拦截器链中
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors)); //如果不是运行时起作业给作用的拦截器,则直接添加到拦截器链中
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) { //类级别的拦截器
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} return interceptorList;
}

下面看下:invocation.proceed() 。这个方法真正地使拦截器起作用。代码如下:

@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
} Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
 //org\springframework\aop\framework\ReflectiveMethodInvocation.class
public Object proceed() throws Throwable {
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { //如果是自后一个匹配的拦截器,则直接调用
return this.invokeJoinpoint(); //可以看出,这里有反射和cglig两种实现方法,也就是,对应jdk和cglib
} else {
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { //如果是匹配的拦截器&方法
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed(); //如果类的方法完全匹配,则直接执行,否则执行回调
} else {
return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
}
}
}

jdk和cglib其实都是一样的意思,简单看下cglib的invokeJoinPoint()

protected Object invokeJoinpoint() throws Throwable {
return this.publicMethod ? this.methodProxy.invoke(this.target, this.arguments) : super.invokeJoinpoint();
}
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if (this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}

在这里看到 fci.f1.invoke(fci.i1, obj, args)。也就是我们的拦截器织入的方法执行。这里又个有意义的现象,就是使用fci.f1.invoke ,这中fastclass的方式来执行,之所以使用这种方式,是因为cglib生成动态代理的子类的时候,已经为每一个方法增加了一个独一无二的索引,有了索引之后,这样执行会比直接调用快,具体的可以去百度下。

-------------------------------------------------------------------------------------------------

总结:spring aop的执行流程,分为两步:1、生成拦截器链  2、切入点(方法)织入代码,进行拦截

spring aop 源码解读之我见的更多相关文章

  1. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

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

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

  3. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

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

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

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

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

  6. Spring AOP 源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  7. Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理

    AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...

  8. 最简 Spring AOP 源码分析!

    前言 最近在研究 Spring 源码,Spring 最核心的功能就是 IOC 容器和 AOP.本文定位是以最简的方式,分析 Spring AOP 源码. 基本概念 上面的思维导图能够概括了 Sprin ...

  9. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

随机推荐

  1. java集合框架中contains(),containsKey()和containsValue()的用法:

    List集合的contains()方法用于判断集合中包不包含某个元素,返回值是boolean. Map集合的containsKey()和containsValue()方法和上面的相同. 示例: pub ...

  2. shell脚本读取命令行的参数

    转至:https://www.cnblogs.com/eternityz/p/13879836.html 前提 在编写shell程序时经常需要处理命令行参数 选项与参数: 如下命令行: ./test. ...

  3. java 读取xlsx文件

    public class ReadExcel { public static void main(String[] args) { Workbook wb = null; Sheet sheet = ...

  4. Qt:QJsonObject

    0.说明 QJsonObject在逻辑上就是一个Map或Dict!记住这一点对理解它的方法.说明很有帮助. QJsonObject类封装了JSON Object. JSON Object是一个Key- ...

  5. if 、whitch、for、while

    if 用于过程的判断  whitch用于值得判断 if { } else if { } else if //当此前的过程满足条件时,程序便不再判断其他过程,但是if{}if{}这样的写法程序会将每个过 ...

  6. 《手把手教你》系列技巧篇(七十一)-java+ selenium自动化测试-自定义类解决元素同步问题(详解教程)

    1.简介 前面宏哥介绍了几种关于时间等待的方法,也提到了,在实际自动化测试脚本开发过程,百分之90的报错是和元素因为时间不同步而发生报错.本文介绍如何新建一个自定义的类库来解决这个元素同步问题.这样, ...

  7. 【有奖调研】来,聊聊TTS音色定制这件事儿

    音色个性化定制,一个能让文字转语音服务(TTS)在用户交互过程中注入温度的技术. 文能在营销及内容交付中让品牌保持一致性,武能让开发者"音"量加持,创新开发. 这个100%钢铁纯技 ...

  8. Web应用程序自动化测试工具Selenium的主要功能有哪些?

    Selenium是一个用于Web应用程序测试的工具.是一个开源的Web的自动化测试工具,最初是为网站自动化测试而开发的,类型像我们玩游戏用的按键精灵,可以按指定的命令自动操作,不同是Selenium可 ...

  9. 【故障公告】没有龙卷风,k8s集群翻船3次,投用双集群恢复

    今天没有龙卷风(异常的高并发请求),故障却依然出现,问题非常奇怪. 某种异常情况会造成短时间内, k8s 集群中大量 pod (超过60%)因健康检查失败而处于 CrashLoopBackOff 状态 ...

  10. 10 分钟讲完 QUIC 协议。

    建议阅读本文需要搭配作者 HTTP 相关文章食用. 历史 HTTP 系列文章: 看完这篇HTTP,跟面试官扯皮就没问题了 HTTP 2.0 ,有点炸 ! 这里先来回顾一下 HTTP 的发展过程.首先, ...