关于spring-aop:Spring-AOP-动态代理

本文剖析Spring AOP和动静代理原理和实现,Spring版本: 5.2.3.RELEASE 动静代理1.1 代理模式代理模式也叫做委托模式,它是一种根本的设计模式,其作用是提供委派角色以管制对实在对象的拜访。典型的代理模式通常蕴含一下三类角色 形象角色:它的作用是定义一组行为规范。形象角色个别出现为接口或抽象类实在角色:也叫做被委托角色、被代理角色代理角色:也叫做委托类,代理角色须要实现形象角色所定义的行为(即代理类须要实现形象角色所定义的接口),并且在实现接口办法的时候须要调用实在角色的相应办法动态代理类依赖于实在类,对每一个实在类作代理都须要对应一个代理类,动态代理大量应用会导致类的急剧收缩,区别于动态代理在编译期确定代理类和实在类的关系并且生成代理类。动静代理则是在运行期利用JVM的反射机制生成代理类的字节码,并通过类加载器载入执生成代理对象 1.2 JDK动静代理JDK动静代理是在运行时借助Proxy工具类静态方法创立实现了一组特定接口的代理类和对象,Proxy类是所有被创立的动静代理类的父类,每一个动静代理对象都会与特定的InvocationHandler对象绑定,对任意代理对象实现的接口办法的调用都会最终委派给InvocationHandler#invoke办法 // 定义形象角色接口public interface Subject { void sayHello(String name);}// 接口实现public class RealSubject implements Subject { @Override public void sayHello(String name){ System.out.println("hello, "+ name); }}// 编写调用解决类,实现invoke办法(加强逻辑的入口)public class MyInvocationHandler implements InvocationHandler { private Subject subject; MyInvocationHandler(Subject subject){ this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ //前置解决 System.out.println("Before method invoke..."); Object obj = method.invoke(subject, args); //后置解决 System.out.println("After method invoke..."); return obj; }}// 测试方法public class Client{ public static void main(String[] args) { Subject realSubject = new RealSubject(); MyInvocationHandler handler = new MyInvocationHandler(realSubject); Subject proxy = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); proxy.sayHello("world"); }运行后果:Before method invoke...hello, worldAfter method invoke...从运行后果能够看出,对代理对象办法的调用会进入到自定义MyInvocationHandler#invoke办法,并在指标办法执行前后打印出了日志。将Proxy类生成的代理类通过反编译失去如下类文件,能够看到RealSubjectProxy继承自Proxy且实现了Subject接口,它定义了4个办法m0,m1,m2,m3,其中办法m3是对Subject接口sayHello办法的实现 ...

July 7, 2023 · 12 min · jiezi

关于spring-aop:被面试官问烂的Spring-AOP原理你是怎么答的

Spring AOP在Spring体系中深不可测,Spring AOP原理也是常常在互联网大厂面试时被问到,明天,我给大家抽丝剥茧,具体到你无奈设想。我划分为四个阶段给大家介绍:创立代理对象阶段、拦挡指标对象阶段、调用代理对象阶段、调用指标对象阶段。 我整顿了一张残缺的Spring AOP设计原理UML图,须要高清图的小伙伴能够私信我。 上面具体介绍每个阶段的执行细节。 1、第一阶段:创立代理对象阶段在Spring中,创立Bean实例都是从getBean()办法开始的(呈现getBean()动画),在实例创立之后,Spring容器将依据AOP的配置去匹配指标类的类名,看指标类的类名是否满足切面规定。如果满足满足切面规定,就会调用ProxyFactory创立代理Bean并缓存到IoC容器中。(呈现调用ProxyFactory创立代理Bean动画)依据指标对象的主动抉择不同的代理策略。(呈现抉择代理策略动画)如果指标类实现了接口,Spring会默认抉择JDK Proxy,如果指标类没有实现接口,Spring会默认抉择Cglib Proxy,当然,咱们也能够通过配置强制应用Cglib Proxy。 2、第二阶段:拦挡指标对象阶段当用户调用指标对象的某个办法时,(呈现AopProxy对象拦挡)将会被一个叫做AopProxy的对象拦挡,Spring将所有的调用策略封装到了这个对象中,它默认实现了InvocationHandler接口,也就是调用代理对象的外层拦截器。(呈现调用InvocationHandler的invoke()办法动画)在这个接口的invoke()办法中,(呈现触发MethodInvocation的proceed()办法动画)会触发MethodInvocation的proceed()办法。在这个办法中会按程序执行合乎所有AOP拦挡规定的拦截器链。 3、第三阶段:调用代理对象阶段Spring AOP拦截器链中的每个元素被命名为MethodInterceptor,(呈现反射调用Advice对象动画)其实就是切面配置中的Advice告诉。这个回调告诉能够简略地了解为是新生成的代理Bean中的办法。(呈现执行织入代码动画)也就是咱们常说的被织入的代码片段,这些被织入的代码片段会在这个阶段执行。 4、第四阶段:调用指标对象阶段MethodInterceptor接口也有一个invoke()办法,(呈现执行MethodInterceptor的invoke()办法动画)在MethodInterceptor的invoke()办法中会触发对指标对象办法的调用,也就是反射调用指标对象的办法。(呈现调用指标对象动画) Spring AOP原理就剖析到这里,最初,总结一下不迷路:(呈现代理对象动画)1、代理对象:就是由Spring代理策略生成的对象;(呈现指标对象动画)2、指标对象:就是咱们本人写的业务代码;(呈现织入代码动画)3、织入代码:就是要在咱们本人写的业务代码减少的代码片段;(呈现切面告诉动画)4、切面告诉:就是封装织入代码片段的回调办法;(呈现MethodInvocation动画)5、MethodInvocation:负责执行拦截器链,在proceed()办法中执行;(呈现MethodInterceptor动画)6、MethodInterceptor:负责执行织入的代码片段,在invoke()办法中执行。 都看到这里了,你还感觉Spirng AOP原理难吗?我再送给大家一张精简版的Spring AOP执行流程图,须要的小伙伴立马关注点个赞。 如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源。关注微信公众号『 Tom弹架构 』可获取更多技术干货! 往期视频曾经整顿成文档模式,须要的小伙伴点个关注,增加下方名片!

April 22, 2022 · 1 min · jiezi

关于spring-aop:阿里大牛亲码-Spring-AOP详解笔记全网开源学透并发只需3天

Hello,明天给各位童鞋们分享Spring AOP,连忙拿出小本子记下来吧! 概述AOP是aspect-oriented programming的缩写,译为面向切面编程。利用 AOP 能够对业务逻辑的各个局部进行隔离,从而使得 业务逻辑各局部之间的耦合度升高,进步程序的可重用性,同时进步了开发的效率。简略来说,AOP就是不批改源代码在骨干性能外面增加新性能。 底层原理AOP底层应用了动静代理:在有接口的时候应用JDK 动静代理、在没有接口的时候应用CGLIB字节码动静代理。 JDK动静代理简介 应用JDK 动静代理须要用到JDK中的Proxy类外面的newProxyInstance办法创立代理对象。办法如下: static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h); newProxyInstance办法的三个参数阐明: loader 定义代理类的类加载器 interfaces 代理类要实现的接口列表(能够多个) h 指派办法调用的处理程序(要加强的性能在这外面实现) newProxyInstance办法返回一个指定接口的代理类实例。 InvocationHandler接口中有个invoke办法,用于实现加强的性能: public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;invoke办法的三个参数阐明: proxy示意代理对象 method示意被加强的办法 args是办法的参数 若没有则为null 示例代码代码构造如下: └─src └─com └─spring5 JDKProxy.java UserDao.java UserDaoImpl.javaUserDao接口: public interface UserDao { int add(int a,int b); String update(String id);} UserDaoImpl类: JDKProxy类: 运行后果: 办法之前执行…add :传递的参数…[1, 2] I am add… ...

May 10, 2021 · 2 min · jiezi

关于spring-aop:Spring-AOP

Spring AOP次要内容代理模式 代理模式在 Java 开发中是一种比拟常见的设计模式。设计目的旨在为服务类与客户类之间插入其余性能,插入的性能对于调用者是通明的,起到假装管制的作用。如租房的例子:房客、中介、房东。对应于代理模式中即:客户类、代理类 、委托类(被代理类)。 为某一个对象(委托类)提供一个代理(代理类),用来管制对这个对象的拜访。委托类和代理类有一个独特的父类或父接口。代理类会对申请做预处理、过滤,将申请调配给指定对象。 生存中常见的代理状况: 租房中介、婚庆公司等 代理模式的两个设计准则: 1. 代理类 与 委托类 具备类似的行为(独特) 2. 代理类加强委托类的行为 罕用的代理模式: 1. 动态代理 2. 动静代理 动态代理 某个对象提供一个代理,代理角色固定,以管制对这个对象的拜访。 代理类和委托类有独特的父类或父接口,这样在任何应用委托类对象的中央都能够用代理对象代替。代理类负责申请的预处理、过滤、将申请分派给委托类解决、以及委托类执行完申请后的后续解决。 代理的三要素 a、有独特的行为(结婚) - 接口 b、指标角色(新人) - 实现行为 c、代理角色(婚庆公司) - 实现行为 加强指标对象行为 动态代理的特点 1、指标角色固定 2、在应用程序执行前就失去指标角色 3、代理对象会加强指标对象的行为 4、有可能存在多个代理 引起"类爆炸"(毛病) 动态代理的实现定义行为(独特) 定义接口/** * 定义行为 */public interface Marry { public void toMarry();} 指标对象(实现行为)/** * 动态代理 ——> 指标对象 */public class You implements Marry { // 实现行为 @Override public void toMarry() { System.out.println("我要结婚了..."); }} 代理对象(实现行为、加强指标对象的行为)/** * 动态代理 ——> 代理对象 */public class MarryCompanyProxy implements Marry { // 指标对象 private Marry marry; // 通过结构器将指标对象传入 public MarryCompanyProxy(Marry marry) { this.marry = marry; } // 实现行为 @Override public void toMarry() { // 加强行为 before(); // 执行指标对象中的办法 marry.toMarry(); // 加强行为 after(); } /** * 加强行为 */ private void after() { System.out.println("新婚高兴,早生贵子!"); } /** * 加强行为 */ private void before() { System.out.println("场地正在安排中..."); }} 通过代理对象实现目标对象的性能// 指标对象You you = new You();// 结构代理角色同时传入实在角色MarryCompanyProxy marryCompanyProxy = new MarryCompanyProxy(you);// 通过代理对象调用指标对象中的办法marryCompanyProxy.toMarry(); 动态代理对于代理的角色是固定的,如dao层有20个dao类,如果要对办法的拜访权限进行代理,此时须要创立20个动态代理角色,引起类爆炸,无奈满足生产上的须要,于是就催生了动静代理的思维。 ...

December 2, 2020 · 5 min · jiezi

Spring AOP从零单排-织入时期源码分析

问题:Spring AOP代理中的运行时期,是在初始化时期织入还是获取对象时期织入?织入就是代理的过程,指目标对象进行封装转换成代理,实现了代理,就可以运用各种代理的场景模式。何为AOP简单点来定义就是切面,是一种编程范式。与OOP对比,它是面向切面,为何需要切面,在开发中,我们的系统从上到下定义的模块中的过程中会产生一些横切性的问题,这些横切性的问题和我们的主业务逻辑关系不大,假如不进行AOP,会散落在代码的各个地方,造成难以维护。AOP的编程思想就是把业务逻辑和横切的问题进行分离,从而达到解耦的目的,使代码的重用性、侵入性低、开发效率高。AOP使用场景日志记录;记录调用方法的入参和结果返参。用户的权限验证;验证用户的权限放到AOP中,与主业务进行解耦。性能监控;监控程序运行方法的耗时,找出项目的瓶颈。事务管理;控制Spring事务,Mysql事务等。AOP概念点AOP和Spring AOP的关系在这里问题中,也有一个类似的一对IOC和DI(dependency injection)的关系,AOP可以理解是一种编程目标,Spring AOP就是这个实现这个目标的一种手段。同理IOC也是一种编程目标,DI就是它的一个手段。SpringAOP和AspectJ是什么关系在Spring官网可以看到,AOP的实现提供了两种支持分别为@AspectJ、Schema-based AOP。其实在Spring2.5版本时,Spring自己实现了一套AOP开发的规范和语言,但是这一套规范比较复杂,可读性差。之后,Spring借用了AspectJ编程风格,才有了@AspectJ的方式支持,那么何为编程风格。Annotation注解方式;对应@AspectJJavaConfig;对应Schema-based AOPSpringAOP和AspectJ的详细对比,在之后的章节会在进行更加详细的说明,将会在他们的背景、织入方法、性能做介绍。Spring AOP的应用阅读官网,是我们学习一个新知识的最好途径,这个就是Spring AOP的核心概念点,跟进它们的重要性,我做了重新的排序,以便好理解,这些会为我们后续的源码分析起到作用。Aspect:切面;使用@Aspect注解的Java类来实现,集合了所有的切点,做为切点的一个载体,做一个比喻就像是我们的一个数据库。Tips:这个要实现的话,一定要交给Spirng IOC去管理,也就是需要加入@Component。Pointcut:切点;表示为所有Join point的集合,就像是数据库中一个表。Join point:连接点;俗称为目标对象,具体来说就是servlet中的method,就像是数据库表中的记录。Advice:通知;这个就是before、after、After throwing、After (finally)。Weaving:把代理逻辑加入到目标对象上的过程叫做织入。target:目标对象、原始对象。aop Proxy:代理对象 包含了原始对象的代码和增加后的代码的那个对象。Tips这个应用点,有很多的知识点可以让我们去挖掘,比如Pointcut中execution、within的区别,我相信你去针对性搜索或者官网都未必能有好的解释,稍后会再专门挑一个文章做重点的使用介绍;SpringAOP源码分析为了回答我们的一开始的问题,前面的几个章节我们做了一些简单的概念介绍做为铺垫,那么接下来我们回归正题,正面去切入问题。以码说话,我们以最简洁的思路把AOP实现,我们先上代码。项目结构介绍项目目录结构,比较简单,5个主要的文件;pom.xml核心代码;spring-content是核心jar,已经包含了spring所有的基础jar,aspectjweaver是为了实现AOP。AppConfig.java;定义一个Annotation,做为我们Spirng IOC容器的启动类。package com.will.config;@Configuration@ComponentScan(“com.will”)@EnableAspectJAutoProxy(proxyTargetClass = false)public class AppConfig { }WilAspect.java ;按照官网首推的方式(@AspectJ support),实现AOP代理。package com.will.config;/** * 定义一个切面的载体 /@Aspect@Componentpublic class WilAspect { /* * 定义一个切点 / @Pointcut(“execution( com.will.dao..(..))”) public void pointCutExecution(){ } /** * 定义一个Advice为Before,并指定对应的切点 * @param joinPoint / @Before(“pointCutExecution()”) public void before(JoinPoint joinPoint){ System.out.println(“proxy-before”); }}Dao.javapackage com.will.dao;public interface Dao { public void query();}UserDao.javapackage com.will.dao;import org.springframework.stereotype.Component;@Componentpublic class UserDao implements Dao { public void query() { System.out.println(“query user”); }}Test.javapackage com.will.test;import com.will.config.AppConfig;import com.will.dao.Dao;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Test { public static void main(String[] args) { /* * new一个注册配置类,启动IOC容器,初始化时期; / AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class); /* * 获取Dao对象,获取对象时期,并进行query打印 */ Dao dao = annotationConfigApplicationContext.getBean(Dao.class); dao.query(); annotationConfigApplicationContext.start(); }}好了,这样我们整体的AOP代理就已经完成。问题分析测试究竟是哪个时期进行对象织入的,比如Test类中,究竟是第一行还是第二行进行织入的,我们只能通过源码进行分析,假如是你,你会进行如何的分析源码解读。Spring的代码非常优秀,同时也非常复杂,那是一个大项目,里面进行了很多的代码封装,那么的代码你三天三夜也读不完,甚至于你都不清楚哪一行的该留意的,哪一行是起到关键性作用的,这里教几个小技巧。看方法返回类型;假如是void返回类型的,看都不看跳过。返回结果是对象,比如T果断进行去进行跟踪。假设法;就当前场景,我们大胆假设是第二行进行的织入。借助好的IDE;IDEA可以帮我们做很多的事情,它的debug模式中的条件断点、调用链(堆栈)会帮助到我们。假设法源码分析debug模式StepInfo(F5)后,进入 AbstractApplicationContext.getBean方法,这个是Spring应用上下文中最重要的一个类,这个抽象类中提供了几乎ApplicationContext的所有操作。这里第一个语句返回void,我们可以直接忽略,看下面的关键性代码。继续debug后,会进入到 DefaultListableBeanFactory 类中,看如下代码return new NamedBeanHolder<>(beanName, getBean(beanName, requiredType, args));在该语句中,这个可以理解为 DefaultListableBeanFactory 容器,帮我们获取相应的Bean。进入到AbstractBeanFactory类的doGetBean方法之后,我们运行完。Object sharedInstance = getSingleton(beanName);语句之后,看到 sharedInstance 对象打印出&Proxyxxx ,说明在getSingleton 方法的时候就已经获取到了对象,所以需要跟踪进入到 getSingleton 方法中,继续探究。不方便不方便我们进行问题追踪到这个步骤之后,我需要引入IDEA的条件断点,不方便我们进行问题追踪因为Spring会初始化很多的Bean,我们再ObjectsharedInstance=getSingleton(beanName);加入条件断点语句。继续debug进入到DefaultSingletonBeanRegistry的getSingleton方法。我们观察下执行完ObjectsingletonObject=this.singletonObjects.get(beanName); 之后的singletonObject已经变成为&ProxyUserDao,这个时候Spring最关键的一行代码出现了,请注意这个this.singletonObjects。this.singletonObjects就是相当IOC容器,反之IOC容器就是一个线程安全的线程安全的HashMap,里面存放着我们需要Bean。我们来看下singletonObjects存放着的数据,里面就有我们的UserDao类。这就说明,我们的初始化的时期进行织入的,上图也有整个Debug模式的调用链。源码深层次探索通过上一个环节已经得知是在第一行进行初始化的,但是它在初始化的时候是什么时候完成织入的,抱着求知的心态我们继续求证。还是那个问题,那么多的代码,我的切入点在哪里?既然singletonObjects是容器,存放我们的Bean,那么找到关键性代码在哪里进行存放(put方法)就可以了。于是我们通过搜索定位到了。我们通过debug模式的条件断点和debug调用链模式,就可以进行探索。这个时候借助上图中的调用链,我们把思路放到放到IDEA帮我定位到的两个方法代码上。DefaultSingletonBeanRegistry.getSingleton我们一步步断点,得知,当运行完singletonObject=singletonFactory.getObject();之后,singletonObject已经获得了代理。至此我们知道,代理对象的获取关键在于singletonFactory对象,于是又定位到了AbstractBeanFactorydoGetBean方法,发现singletonFactory参数是由createBean方法创造的。这个就是Spring中IOC容器最核心的地方了,这个代码的模式也值得我们去学习。sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } });这个第二个参数是用到了jdk8中的lambda,这一段的含义是就是为了传参,重点看下 createBean(beanName,mbd,args);代码。随着断点,我们进入到这个类方法里面。AbstractAutowireCapableBeanFactory.createBean中的;ObjectbeanInstance=doCreateBean(beanName,mbdToUse,args)方法;doCreateBean方法中,做了简化。Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } … return exposedObject;当运行完 exposedObject=initializeBean(beanName,exposedObject,mbd);之后,我们看到exposedObject已经是一个代理对象,并执行返回。这一行代码就是取判断对象要不要执行代理,要的话就去初始化代理对象,不需要直接返回。后面的initializeBean方法是涉及代理对象生成的逻辑(JDK、Cglib),后续会有一个专门的章节进行详细介绍。总结通过源码分析,我们得知,Spring AOP的代理对象的织入时期是在运行Spring初始化的时候就已经完成的织入,并且也分析了Spring是如何完成的织入。 ...

March 11, 2019 · 2 min · jiezi