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

一 JDK动态代理拦截器调用的实现:

我们知道,在生成代理对象时,相关的拦截器已经配置到代理对象中了。Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 这行代码是用于生成代理对象的,this代表InvocationHandler接口,此处是指JdkDynamicAopProxy类,

因为JdkDynamicAopProxy实现了InvocationHandler接口。当生成的代理对象调用代理方法时,会触发InvocationHandler接口中invoke方法的调用,而增强行为的发生或者说对目标对象方法的拦截动作就是在这个方法中。我们进入JdkDynamicAopProxy

类的invoke方法:

  1. /**
  2. * Implementation of {@code InvocationHandler.invoke}.
  3. * <p>Callers will see exactly the exception thrown by the target,
  4. * unless a hook method throws an exception.
  5. */
  6. @Override
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8. MethodInvocation invocation;
  9. Object oldProxy = null;
  10. boolean setProxyContext = false;
  11.  
  12. TargetSource targetSource = this.advised.targetSource;
  13. Class<?> targetClass = null;
  14. Object target = null;
  15.  
  16. try {
    // 如果目标对象没有实现Object的equals方法
  17. if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
  18. // The target does not implement the equals(Object) method itself.
  19. return equals(args[0]);
  20. }
    // 如果目标对象没有实现Object的hashCode方法
  21. else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
  22. // The target does not implement the hashCode() method itself.
  23. return hashCode();
  24. }
  25. else if (method.getDeclaringClass() == DecoratingProxy.class) {
  26. // There is only getDecoratedClass() declared -> dispatch to proxy config.
  27. return AopProxyUtils.ultimateTargetClass(this.advised);
  28. }
  29. else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
  30. method.getDeclaringClass().isAssignableFrom(Advised.class)) {
  31. // Service invocations on ProxyConfig with the proxy config...
  32. return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
  33. }
  34.  
  35. Object retVal;
  36.  
  37. if (this.advised.exposeProxy) {
  38. // Make invocation available if necessary.
  39. oldProxy = AopContext.setCurrentProxy(proxy);
  40. setProxyContext = true;
  41. }
  42.  
  43. // May be null. Get as late as possible to minimize the time we "own" the target,
  44. // in case it comes from a pool.
    // 获取目标对象
  45. target = targetSource.getTarget();
  46. if (target != null) {
  47. targetClass = target.getClass();
  48. }
  49.  
  50. // Get the interception chain for this method.
    // 获取拦截器链
  51. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
  52.  
  53. // Check whether we have any advice. If we don't, we can fallback on direct
  54. // reflective invocation of the target, and avoid creating a MethodInvocation.
  55. if (chain.isEmpty()) {
    // 如果拦截器为空,那么直接调用目标对象的目标方法 进入该方法
  56. // We can skip creating a MethodInvocation: just invoke the target directly
  57. // Note that the final invoker must be an InvokerInterceptor so we know it does
  58. // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
  59. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
  60. retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
  61. }
  62. else {
    // 否则,需要先调用拦截器然后再调用目标方法,通过构造一个ReflectiveMethodInvocation来实现
  63. // We need to create a method invocation...
  64. invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
  65. // Proceed to the joinpoint through the interceptor chain.
    // 沿着拦截器链前行 进入该方法
  66. retVal = invocation.proceed();
  67. }
  68.  
  69. // Massage return value if necessary.
  70. Class<?> returnType = method.getReturnType();
  71. if (retVal != null && retVal == target &&
  72. returnType != Object.class && returnType.isInstance(proxy) &&
  73. !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
  74. // Special case: it returned "this" and the return type of the method
  75. // is type-compatible. Note that we can't help if the target sets
  76. // a reference to itself in another returned object.
  77. retVal = proxy;
  78. }
  79. else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
  80. throw new AopInvocationException(
  81. "Null return value from advice does not match primitive return type for: " + method);
  82. }
  83. return retVal;
  84. }
  85. finally {
  86. if (target != null && !targetSource.isStatic()) {
  87. // Must have come from TargetSource.
  88. targetSource.releaseTarget(target);
  89. }
  90. if (setProxyContext) {
  91. // Restore old proxy.
  92. AopContext.setCurrentProxy(oldProxy);
  93. }
  94. }
  95. }

当没有拦截器时,我们进入invokeJoinpointUsingReflection这个方法,可以看到通过反射的方式直接调用目标对象方法

  1. /**
  2. * Invoke the given target via reflection, as part of an AOP method invocation.
  3. * @param target the target object
  4. * @param method the method to invoke
  5. * @param args the arguments for the method
  6. * @return the invocation result, if any
  7. * @throws Throwable if thrown by the target method
  8. * @throws org.springframework.aop.AopInvocationException in case of a reflection error
  9. */
  10. public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
  11. throws Throwable {
  12.  
  13. // Use reflection to invoke the method.
  14. try {
  15. ReflectionUtils.makeAccessible(method);
    // 通过反射的方式直接调用目标对象方法
  16. return method.invoke(target, args);
  17. }
  18. catch (InvocationTargetException ex) {
  19. // Invoked method threw a checked exception.
  20. // We must rethrow it. The client won't see the interceptor.
  21. throw ex.getTargetException();
  22. }
  23. catch (IllegalArgumentException ex) {
  24. throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
  25. method + "] on target [" + target + "]", ex);
  26. }
  27. catch (IllegalAccessException ex) {
  28. throw new AopInvocationException("Could not access method [" + method + "]", ex);
  29. }
  30. }

当有拦截器时,我们进入invocation.proceed()方法,这个方法就是AOP的核心部分了,AOP是如何完成对目标对象的增强的?这些实现封装在AOP拦截器链中,由一个个具体的拦截器实现的,就在这个方法中。

  1. @Override
  2. public Object proceed() throws Throwable {
  3. // We start with an index of -1 and increment early.
    // 从索引为-1的拦截器开始调用,按顺序递增,直到没有拦截器了,然后开始调用目标对象的方法
  4. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
  5. return invokeJoinpoint();
  6. }
  7. // 沿着定义好的拦截器链进行获取然后逐个处理
  8. Object interceptorOrInterceptionAdvice =
  9. this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  10. if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
  11. // Evaluate dynamic method matcher here: static part will already have
  12. // been evaluated and found to match.
    // 这里是触发匹配判断的地方,如果和定义的pointcut匹配,那么这个advice将得到执行
  13. InterceptorAndDynamicMethodMatcher dm =
  14. (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
  15. if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
  16. return dm.interceptor.invoke(this);
  17. }
  18. else {
  19. // Dynamic matching failed.
  20. // Skip this interceptor and invoke the next in the chain.
  21. return proceed();
  22. }
  23. }
  24. else {
  25. // It's an interceptor, so we just invoke it: The pointcut will have
  26. // been evaluated statically before this object was constructed.
    // 如果是一个interceptor,那么直接调用这个interceptor的invoke方法 进入此方法来分析通知(拦截器)是如何起作用的
  27. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  28. }
  29. }

进入MethodBeforeAdviceInterceptor类的invoke方法,就是前置通知调用的方法:

  1. @Override
  2. public Object invoke(MethodInvocation mi) throws Throwable {
  3. this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); // 此处就是发生的切面增强行为
  4. return mi.proceed(); //递归调用proceed方法,然后进入下一个拦截器的处理
  5. }

进入AspectJAfterAdvice类的invoke方法,就是后置通知调用的方法:

  1. @Override
  2. public Object invoke(MethodInvocation mi) throws Throwable {
  3. try {
  4. return mi.proceed(); //递归调用proceed方法
  5. }
  6. finally {
  7. invokeAdviceMethod(getJoinPointMatch(), null, null); //此处就是发生切面增强行为的地方
  8. }
  9. }

接下来我们考虑另外一个问题:在xml中定义的通知是如何配置到代理对象中的拦截器链中的?

我们知道,我们是通过 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);这行代码获取拦截器的,所以所有的拦截器都在interceptorsAndDynamicMethodMatchers这个list集合中,那么

这个集合从哪个地方获取的呢?我们可以进入JdkDynamicAopProxy类的invoke方法,有这么一行代码:List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);用于获取这个代理方法的拦截器链,进入此方法:

  1. public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
    // 这里使用cache获取已有的interceptor链,如果是第一次,需要手动生成,这里是使用AdvisorChainFactory来生成interceptor链的,默认是DefaultAdvisorChainFactory
  2. MethodCacheKey cacheKey = new MethodCacheKey(method);
  3. List<Object> cached = this.methodCache.get(cacheKey);
  4. if (cached == null) {
  5. cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
  6. this, method, targetClass);
  7. this.methodCache.put(cacheKey, cached);
  8. }
  9. return cached;
  10. }

DefaultAdvisorChainFactory 由名字可知,这是一个生成通知器链的工厂。进入getInterceptorsAndDynamicInterceptionAdvice这个方法:

  1. @Override
  2. public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
  3. Advised config, Method method, Class<?> targetClass) {
  4. // 所有的advistor在config中已经持有了,可以直接使用
  5. // This is somewhat tricky... We have to process introductions first,
  6. // but we need to preserve order in the ultimate list.
  7. List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
  8. Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
  9. boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
  10. AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
  11.  
  12. for (Advisor advisor : config.getAdvisors()) {
  13. if (advisor instanceof PointcutAdvisor) {
  14. // Add it conditionally.
  15. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
  16. if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
    //通过AdvisorAdapterRegistry注册器,把从config中获取的advistor进行适配,从而获得拦截器,再把它放入List中。这就是拦截器注册的过程。
  17. MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
  18. MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
  19. if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
  20. if (mm.isRuntime()) {
  21. // Creating a new object instance in the getInterceptors() method
  22. // isn't a problem as we normally cache created chains.
  23. for (MethodInterceptor interceptor : interceptors) {
  24. interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
  25. }
  26. }
  27. else {
  28. interceptorList.addAll(Arrays.asList(interceptors));
  29. }
  30. }
  31. }
  32. }
  33. else if (advisor instanceof IntroductionAdvisor) {
  34. IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
  35. if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
  36. Interceptor[] interceptors = registry.getInterceptors(advisor);
  37. interceptorList.addAll(Arrays.asList(interceptors));
  38. }
  39. }
  40. else {
  41. Interceptor[] interceptors = registry.getInterceptors(advisor);
  42. interceptorList.addAll(Arrays.asList(interceptors));
  43. }
  44. }
  45.  
  46. return interceptorList;
  47. }

以上就是JDK动态代理springAOP实现增强行为的过程,对于cglib这种方式,此处就不做分析。

《springAOP源码分析一》

《springAOP源码分析二》

spring AOP源码分析(三)的更多相关文章

  1. 5.2 spring5源码--spring AOP源码分析三---切面源码分析

    一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...

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

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

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

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

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

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

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

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

  6. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

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

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

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

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

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

  9. spring aop 源码分析(三) @Scope注解创建代理对象

    一.源码环境的搭建: @Component @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON,proxyMode = ScopedP ...

随机推荐

  1. 【JXOI2018】守卫

    [JXOI2018]守卫 参考题解:https://blog.csdn.net/dofypxy/article/details/80196942 大致思路就是:区间DP.对于\([l,r]\)的答案, ...

  2. linux学习笔记整理(三)

    第四章 文件的基本管理和XFS文件系统备份恢复本节所讲内容:4.1 Linux系统目录结构和相对/绝对路径.4.2 创建/复制/删除文件,rm -rf / 意外事故4.3 查看文件内容的命令4.4 实 ...

  3. Linux的目录结构--一切从根开始

    Linux目录结构的特点 # 举例-linux下面使用光盘 ###.把光盘放入到光驱中 ###.linux中使用光盘 /dev/cdrom [root@oldboyedu- ~]# ll /dev/c ...

  4. docker: read tcp 192.168.7.235:36512->54.230.212.9:443: read: connection reset by peer.

    在学习rancher的时候去下载rancher/agent镜像的时候,出现报错:docker: read tcp 192.168.7.235:36512->54.230.212.9:443: r ...

  5. Jmeter插件安装及使用

    1 安装Plugins Manager插件 1.1 下载Plugins Manager插件 插件下载官方地址:https://jmeter-plugins.org/downloads/all/ 将下载 ...

  6. Android中Bitmap对象和字节流之间的相互转换

    android 将图片内容解析成字节数组,将字节数组转换为ImageView可调用的Bitmap对象,图片缩放,把字节数组保存为一个文件,把Bitmap转Byte   import java.io.B ...

  7. PHP HMAC_SHA1 算法 生成算法签名

    HMAC_SHA1(Hashed Message Authentication Code, Secure Hash Algorithm)是一种安全的基于加密hash函数和共享密钥的消息认证协议. 它可 ...

  8. 根据成绩输出对应的等级(使用if多分支和switch语句分别实现)

    根据成绩输出对应的等级,使用if多分支和switch语句分别实现. a)        A级   [90,100] b)        B级   [80,90) c)        C级   [70, ...

  9. lightoj-1128-Greatest Parent(二分+LCA)

    传送门 首先我要实力吐槽这个lightoj 它给我的注册密码藏在不为人所见的地方 注册注册了10多分钟 qwq -------------------------------------------- ...

  10. python __call__方法的使用

    介绍一下python __call__ 方法的使用 代码如下: #!/usr/bin/env python # -*- coding: utf- -*- ''' __call__方法 普通的类定义的方 ...