通知是对目标对象方法的增强,而引入可以动态为目标对象实现新的接口,实现对类的增强。

引入的使用

目标类

public class DogService {public void hi() {System.out.println("wangwang");}
}

增强接口

public interface CatService {void eat();
}

增强接口实现类

public class CatServiceImpl implements CatService {@Overridepublic void eat() {System.out.println("cat eat");}
}

切面类

package com.morris.spring.config;import com.morris.spring.service.CatService;
import com.morris.spring.service.CatServiceImpl;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@EnableAspectJAutoProxy
@Aspect
public class IntroduceConfig {@DeclareParents(value = "com.morris.spring.service.*+", defaultImpl = CatServiceImpl.class)private CatService catService;}

测试类

package com.morris.spring.demo.annotation;import com.morris.spring.config.IntroduceConfig;
import com.morris.spring.service.CatService;
import com.morris.spring.service.DogService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class IntroduceDemo {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();applicationContext.register(DogService.class);applicationContext.register(IntroduceConfig.class);applicationContext.refresh();DogService dogService = applicationContext.getBean(DogService.class);dogService.hi();CatService catService = (CatService) dogService;catService.eat();}
}

运行结果如下:

cat eat
wangwang

从运行结果可以发现DogService拥有了CatService接口的能力。

源码实现

注解的扫描

解析带有@Aspect注解类下面的有@DeclareParents注解的属性:

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {... ...// Find introduction fields.// 引入for (Field field : aspectClass.getDeclaredFields()) {Advisor advisor = getDeclareParentsAdvisor(field);if (advisor != null) {advisors.add(advisor);}}return advisors;
}

遍历所有带有@DeclareParents注解的属性,封装成为DeclareParentsAdvisor对象。

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getDeclareParentsAdvisor

private Advisor getDeclareParentsAdvisor(Field introductionField) {DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);if (declareParents == null) {// Not an introduction fieldreturn null;}if (DeclareParents.class == declareParents.defaultImpl()) {throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");}return new DeclareParentsAdvisor(introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}

DeclareParentsAdvisor包含两部分:

  • ClassFilter:TypePatternClassFilter,根据@DeclareParents中配置的表达式进行匹配
  • Advice:DelegatePerTargetObjectIntroductionInterceptor,增强逻辑

目标类的匹配

调用IntroductionAdvisor.getClassFilter().matches()进行类的匹配,上面注入的DeclareParentsAdvisor对象是IntroductionAdvisor的子类,也就是会根据@DeclareParents注解中指定的表达式与类的全限定名进行匹配,这里只匹配类,不会匹配方法。

org.springframework.aop.support.AopUtils#canApply

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {if (advisor instanceof IntroductionAdvisor) {return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}
}

与方法的增强一样,如果匹配到了目标类就会为目标类生成代理对象。

目标方法的调用

可以使用arthas工具导出内存中动态代理生成的代理类:

public class DogService$$EnhancerBySpringCGLIB$$13feac43 extends DogService implements CatService, SpringProxy, Advised, Factory {private boolean CGLIB$BOUND;public static Object CGLIB$FACTORY_DATA;private static final ThreadLocal CGLIB$THREAD_CALLBACKS;private static final Callback[] CGLIB$STATIC_CALLBACKS;private MethodInterceptor CGLIB$CALLBACK_0;private MethodInterceptor CGLIB$CALLBACK_1;private NoOp CGLIB$CALLBACK_2;private Dispatcher CGLIB$CALLBACK_3;private Dispatcher CGLIB$CALLBACK_4;private MethodInterceptor CGLIB$CALLBACK_5;private MethodInterceptor CGLIB$CALLBACK_6;private static Object CGLIB$CALLBACK_FILTER;private static final Method CGLIB$hi$0$Method;private static final MethodProxy CGLIB$hi$0$Proxy;private static final Object[] CGLIB$emptyArgs;private static final Method CGLIB$equals$1$Method;private static final MethodProxy CGLIB$equals$1$Proxy;private static final Method CGLIB$toString$2$Method;private static final MethodProxy CGLIB$toString$2$Proxy;private static final Method CGLIB$hashCode$3$Method;private static final MethodProxy CGLIB$hashCode$3$Proxy;private static final Method CGLIB$clone$4$Method;private static final MethodProxy CGLIB$clone$4$Proxy;private static final Method CGLIB$eat$5$Method;private static final MethodProxy CGLIB$eat$5$Proxy;static void CGLIB$STATICHOOK3() {CGLIB$THREAD_CALLBACKS = new ThreadLocal();CGLIB$emptyArgs = new Object[0];Class var0 = Class.forName("com.morris.spring.service.DogService$$EnhancerBySpringCGLIB$$13feac43");Class var1;Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());CGLIB$equals$1$Method = var10000[0];CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");CGLIB$toString$2$Method = var10000[1];CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");CGLIB$hashCode$3$Method = var10000[2];CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");CGLIB$clone$4$Method = var10000[3];CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");CGLIB$eat$5$Method = ReflectUtils.findMethods(new String[]{"eat", "()V"}, (var1 = Class.forName("com.morris.spring.service.CatService")).getDeclaredMethods())[0];CGLIB$eat$5$Proxy = MethodProxy.create(var1, var0, "()V", "eat", "CGLIB$eat$5");CGLIB$hi$0$Method = ReflectUtils.findMethods(new String[]{"hi", "()V"}, (var1 = Class.forName("com.morris.spring.service.DogService")).getDeclaredMethods())[0];CGLIB$hi$0$Proxy = MethodProxy.create(var1, var0, "()V", "hi", "CGLIB$hi$0");}public final void hi() {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if (var10000 != null) {var10000.intercept(this, CGLIB$hi$0$Method, CGLIB$emptyArgs, CGLIB$hi$0$Proxy);} else {super.hi();}}public final void eat() {MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);var10000 = this.CGLIB$CALLBACK_0;}if (var10000 != null) {var10000.intercept(this, CGLIB$eat$5$Method, CGLIB$emptyArgs, CGLIB$eat$5$Proxy);} else {super.eat();}}
... ....
}

代理方法都会调用MethodInterceptor,类型为DynamicAdvisedInterceptor。

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = null;TargetSource targetSource = this.advised.getTargetSource();try {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);// 获得所有合格的advice中的MethodInterceptorList<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.// 直接调用目标方法Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = methodProxy.invoke(target, argsToUse);}else {// We need to create a method invocation...// 创建CglibMethodInvocation链式调用,与jdk动态代理的逻辑一样retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}
}

此方法的逻辑与org.springframework.aop.framework.JdkDynamicAopProxy#invoke类似。

CglibMethodInvocation会调用advice中的MethodInterceptor。

先看上面DeclareParentsAdvisor的构造方法:

public DeclareParentsAdvisor(Class<?> interfaceType, String typePattern, Class<?> defaultImpl) {this(interfaceType, typePattern,new DelegatePerTargetObjectIntroductionInterceptor(defaultImpl, interfaceType));
}

将目标类封装到了DelegatePerTargetObjectIntroductionInterceptor,而DelegatePerTargetObjectIntroductionInterceptor是一个MethodInterceptor,最终目标方法的执行都会调到MethodInterceptor.invoke():

public Object invoke(MethodInvocation mi) throws Throwable {if (isMethodOnIntroducedInterface(mi)) {Object delegate = getIntroductionDelegateFor(mi.getThis());// Using the following method rather than direct reflection,// we get correct handling of InvocationTargetException// if the introduced method throws an exception.// 调用目标方法Object retVal = AopUtils.invokeJoinpointUsingReflection(delegate, mi.getMethod(), mi.getArguments());// Massage return value if possible: if the delegate returned itself,// we really want to return the proxy.if (retVal == delegate && mi instanceof ProxyMethodInvocation) {retVal = ((ProxyMethodInvocation) mi).getProxy();}return retVal;}return doProceed(mi);
}

【spring】AOP引入的使用与源码分析相关推荐

  1. Spring Boot Dubbo 应用启停源码分析

    作者:张乎兴 来源:Dubbo官方博客 背景介绍 Dubbo Spring Boot 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发.同时也整合了 Spring Boo ...

  2. Spring Boot 2.x 启动全过程源码分析(全)

    上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入口类 SpringApplication 的源码,并知道了其构造原理,这篇我 ...

  3. Spring Boot 2.x 启动全过程源码分析(上)入口类剖析

    转载自   Spring Boot 2.x 启动全过程源码分析(上)入口类剖析 Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boo ...

  4. jdk、spring、mybatis、线程的源码分析

    基础篇 从为什么String=String谈到StringBuilder和StringBuffer Java语法糖1:可变长度参数以及foreach循环原理 Java语法糖2:自动装箱和自动拆箱 集合 ...

  5. 【spring】spring异步执行的使用与源码分析

    在实际的开发过程中,有些业务逻辑使用异步的方式处理更为合理.比如在某个业务逻辑中,需要把一些数据存入到redis缓存中,这个操作只是一个辅助的功能,成功或者失败对主业务并不会产生根本影响,这个过程可以 ...

  6. 详述 Spring MVC 启动流程及相关源码分析

    文章目录 Web 应用部署初始化过程(Web Application Deployement) Spring MVC 启动过程 Listener 的初始化过程 Filter 的初始化 Servlet ...

  7. 深入理解Spring之九:DispatcherServlet初始化源码分析

    转载 https://mp.weixin.qq.com/s/UF9s52CBzEDmD0bwMfFw9A DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请 ...

  8. 【Spring】Lifecycle的使用与源码分析

    LifeCycle接口定义了Spring容器的生命周期,任何被Spring管理的对象都可以实现该接口.当Spring容器本身启动和停止时,会回调LifeCycle接口中定义的方法. Lifecycle ...

  9. Dubbo源码分析-Spring与Dubbo整合原理与源码分析(二)

    Spring与Dubbo整合的整体流程(基于apache-dubbo-2.7.15) 因为dubbo有较多的兼容以前的代码比如@DubboReference 以前就有两个版本@Reference 和@ ...

最新文章

  1. AutoML前沿技术与实践经验分享 | 免费报名
  2. 西湖大学鞠峰组:环境微生物的宏基因组学实例与新发现
  3. php的array_multisort()使用
  4. Nginx的rewrite之if指令(二)
  5. 聊聊2019年的web前端
  6. Ibatis - Open quote is expected for attribute {1} associated with an element type '
  7. hibernate id生成策略 mysql_Hibernate中ID生成策略
  8. mysql中文乱码--存入mysql里的中文变成问号的解决办法
  9. 湘源控规计算土石方流程
  10. 当当卓越京东商城货物配送流程揭秘
  11. Android Studio 占C盘空间是什么原因?
  12. 明星的html模板,明星个人网页制作模板
  13. 2022-2028全球生物识别门锁系统行业调研及趋势分析报告
  14. 最简单日柱推算法_怎样推算大运、小运、流年和命宫?
  15. JAVA中将标准的IEEE754 4字节16进制数据转换为float类型数据
  16. HTML5期末大作业:仿悦世界游戏网站设计——仿悦世界游戏官网(6页) HTML+CSS+JavaScript web网页设计实例作业
  17. Flutter网格控件GridView
  18. Nginx for Mac - 苹果系统SSL证书安装
  19. armbian 斐讯n1_斐讯N1安装Armbian
  20. python计算当天零点时间

热门文章

  1. nn.Conv2d中的dilation
  2. Qt每天一个小技巧之setProperty 设置属性功能
  3. CV在工业界的落地场景总结
  4. 【实体对齐·BootEA】Bootstrapping Entity Alignment with Knowledge Graph Embedding
  5. javaScript的数学计算
  6. DeepinLinux添加Ubuntu镜像源
  7. python 充分利用CPU
  8. Leetcode.51. N 皇后
  9. When You Know-Hooverphonic, 芝华士广告歌完整版
  10. java 死锁和饥饿_死锁与活锁,死锁与饥饿的区别