【spring】AOP引入的使用与源码分析
通知是对目标对象方法的增强,而引入可以动态为目标对象实现新的接口,实现对类的增强。
引入的使用
目标类
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引入的使用与源码分析相关推荐
- Spring Boot Dubbo 应用启停源码分析
作者:张乎兴 来源:Dubbo官方博客 背景介绍 Dubbo Spring Boot 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发.同时也整合了 Spring Boo ...
- Spring Boot 2.x 启动全过程源码分析(全)
上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入口类 SpringApplication 的源码,并知道了其构造原理,这篇我 ...
- Spring Boot 2.x 启动全过程源码分析(上)入口类剖析
转载自 Spring Boot 2.x 启动全过程源码分析(上)入口类剖析 Spring Boot 的应用教程我们已经分享过很多了,今天来通过源码来分析下它的启动过程,探究下 Spring Boo ...
- jdk、spring、mybatis、线程的源码分析
基础篇 从为什么String=String谈到StringBuilder和StringBuffer Java语法糖1:可变长度参数以及foreach循环原理 Java语法糖2:自动装箱和自动拆箱 集合 ...
- 【spring】spring异步执行的使用与源码分析
在实际的开发过程中,有些业务逻辑使用异步的方式处理更为合理.比如在某个业务逻辑中,需要把一些数据存入到redis缓存中,这个操作只是一个辅助的功能,成功或者失败对主业务并不会产生根本影响,这个过程可以 ...
- 详述 Spring MVC 启动流程及相关源码分析
文章目录 Web 应用部署初始化过程(Web Application Deployement) Spring MVC 启动过程 Listener 的初始化过程 Filter 的初始化 Servlet ...
- 深入理解Spring之九:DispatcherServlet初始化源码分析
转载 https://mp.weixin.qq.com/s/UF9s52CBzEDmD0bwMfFw9A DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请 ...
- 【Spring】Lifecycle的使用与源码分析
LifeCycle接口定义了Spring容器的生命周期,任何被Spring管理的对象都可以实现该接口.当Spring容器本身启动和停止时,会回调LifeCycle接口中定义的方法. Lifecycle ...
- Dubbo源码分析-Spring与Dubbo整合原理与源码分析(二)
Spring与Dubbo整合的整体流程(基于apache-dubbo-2.7.15) 因为dubbo有较多的兼容以前的代码比如@DubboReference 以前就有两个版本@Reference 和@ ...
最新文章
- AutoML前沿技术与实践经验分享 | 免费报名
- 西湖大学鞠峰组:环境微生物的宏基因组学实例与新发现
- php的array_multisort()使用
- Nginx的rewrite之if指令(二)
- 聊聊2019年的web前端
- Ibatis - Open quote is expected for attribute {1} associated with an element type '
- hibernate id生成策略 mysql_Hibernate中ID生成策略
- mysql中文乱码--存入mysql里的中文变成问号的解决办法
- 湘源控规计算土石方流程
- 当当卓越京东商城货物配送流程揭秘
- Android Studio 占C盘空间是什么原因?
- 明星的html模板,明星个人网页制作模板
- 2022-2028全球生物识别门锁系统行业调研及趋势分析报告
- 最简单日柱推算法_怎样推算大运、小运、流年和命宫?
- JAVA中将标准的IEEE754 4字节16进制数据转换为float类型数据
- HTML5期末大作业:仿悦世界游戏网站设计——仿悦世界游戏官网(6页) HTML+CSS+JavaScript web网页设计实例作业
- Flutter网格控件GridView
- Nginx for Mac - 苹果系统SSL证书安装
- armbian 斐讯n1_斐讯N1安装Armbian
- python计算当天零点时间
热门文章
- nn.Conv2d中的dilation
- Qt每天一个小技巧之setProperty 设置属性功能
- CV在工业界的落地场景总结
- 【实体对齐·BootEA】Bootstrapping Entity Alignment with Knowledge Graph Embedding
- javaScript的数学计算
- DeepinLinux添加Ubuntu镜像源
- python 充分利用CPU
- Leetcode.51. N 皇后
- When You Know-Hooverphonic, 芝华士广告歌完整版
- java 死锁和饥饿_死锁与活锁,死锁与饥饿的区别