志达IT
做快乐程序员

springaop的实现原理(springaop应用实例)

springaop的实现原理

最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的完成原理。这篇博客就来简略地聊一聊Spring的AOP是如何完成的,并经过一个简略的测验用例来验证一下。废话不多说,直接开始。
2|0二、正文
2|12.1SpringAOP的完成原理
Spring的AOP完成原理其实很简略,就是经过动态署理完成的。假如咱们为Spring的某个bean装备了切面,那么Spring在创立这个bean的时候,实际上创立的是这个bean的一个署理目标,咱们后续对bean中办法的调用,实际上调用的是署理类重写的署理办法。而Spring的AOP运用了两种动态署理,分别是JDK的动态署理,以及CGLib的动态署理。
(一)JDK动态署理
Spring默许运用JDK的动态署理完成AOP,类假如完成了接口,Spring就会运用这种办法完成动态署理。熟悉Java言语的应该会对JDK动态署理有所了解。JDK完成动态署理需求两个组件,首要第一个就是InvocationHandler接口。咱们在运用JDK的动态署理时,需求编写一个类,去完成这个接口,然后重写invoke办法,这个办法其实就是咱们提供的署理办法。然后JDK动态署理需求运用的第二个组件就是Proxy这个类,咱们能够经过这个类的newProxyInstance办法,返回一个署理目标。生成的署理类完成了本来那个类的一切接口,并对接口的办法进行了署理,咱们经过署理目标调用这些办法时,底层将经过反射,调用咱们完成的invoke办法。
(二)CGLib动态署理
JDK的动态署理存在约束,那就是被署理的类有必要是一个完成了接口的类,署理类需求完成相同的接口,署理接口中声明的办法。若需求署理的类没有完成接口,此刻JDK的动态署理将没有办法运用,于是Spring会运用CGLib的动态署理来生成署理目标。CGLib直接操作字节码,生成类的子类,重写类的办法完成署理。
以上就是Spring完成动态的两种办法,下面咱们具体来谈一谈这两种生成动态署理的办法。springaop
2|22.2JDK的动态署理
(一)完成原理
JDK的动态署理是基于反射完成。JDK经过反射,生成一个署理类,这个署理类完成了本来那个类的悉数接口,并对接口中界说的一切办法进行了署理。当咱们经过署理目标履行本来那个类的办法时,署理类底层会经过反射机制,回调咱们完成的InvocationHandler接口的invoke办法。并且这个署理类是Proxy类的子类(记住这个定论,后边测验要用)。这就是JDK动态署理大致的完成办法。
(二)长处
JDK动态署理是JDK原生的,不需求任何依赖即可运用;
经过反射机制生成署理类的速度要比CGLib操作字节码生成署理类的速度更快;
(三)缺陷
假如要运用JDK动态署理,被署理的类有必要完成了接口,不然无法署理;
JDK动态署理无法为没有在接口中界说的办法完成署理,假定咱们有一个完成了接口的类,咱们为它的一个不属于接口中的办法装备了切面,Spring仍然会运用JDK的动态署理,可是由于装备了切面的办法不属于接口,为这个办法装备的切面将不会被织入。
JDK动态署理履行署理办法时,需求经过反射机制进行回调,此刻办法履行的功率比较低;
2|32.3CGLib动态署理
(一)完成原理
CGLib完成动态署理的原理是,底层采用了ASM字节码生成结构,直接对需求署理的类的字节码进行操作,生成这个类的一个子类,偏重写了类的一切能够重写的办法,在重写的过程中,将咱们界说的额定的逻辑(简略理解为Spring中的切面)织入到办法中,对办法进行了增强。而经过字节码操作生成的署理类,和咱们自己编写并编译后的类没有太大差异。
(二)长处
运用CGLib署理的类,不需求完成接口,由于CGLib生成的署理类是直接承继自需求被署理的类;
CGLib生成的署理类是本来那个类的子类,这就意味着这个署理类能够为本来那个类中,一切能够被子类重写的办法进行署理;
CGLib生成的署理类,和咱们自己编写并编译的类没有太大差异,对办法的调用和直接调用普通类的办法一致,所以CGLib履行署理办法的功率要高于JDK的动态署理;
(三)缺陷
由于CGLib的署理类运用的是承继,这也就意味着假如需求被署理的类是一个final类,则无法运用CGLib署理;
由于CGLib完成署理办法的办法是重写父类的办法,所以无法对final办法,或者private办法进行署理,由于子类无法重写这些办法;
CGLib生成署理类的办法是经过操作字节码,这种办法生成署理类的速度要比JDK经过反射生成署理类的速度更慢;
2|42.4经过代码进行测验
(一)测验JDK动态署理
下面咱们经过一个简略的例子,来验证上面的说法。首要咱们需求一个接口和它的一个完成类,然后再为这个完成类的办法装备切面,看看Spring是否真的运用的是JDK的动态署理。假定接口的名称为Human,而完成类为Student:
publicinterfaceHuman{voiddisplay();}@ComponentpublicclassStudentimplementsHuman{@Overridepublicvoiddisplay(){System.out.println(“Iamastudent”);}}
然后咱们界说一个切面,将这个display办法作为切入点,为它装备一个前置告诉,代码如下:
@Aspect@ComponentpublicclassHumanAspect{//为Student这个类的一切办法,装备这个前置告诉@Before(“execution(*cn.tewuyiang.pojo.Student.*(..))”)publicvoidbefore(){System.out.println(“beforestudent”);}}
下面能够开始测验了,咱们经过Java类的办法进行装备,然后编写一个单元测验办法:
//装备类@Configuration@ComponentScan(basePackages=”cn.tewuyiang”)@EnableAspectJAutoProxypublicclassAOPConfig{}//测验办法@TestpublicvoidtestProxy(){ApplicationContextcontext=newAnnotationConfigApplicationContext(AOPConfig.class);//留意,这儿只能经过Human.class获取,而无法经过Student.class,由于在Spirng容器中,//由于运用JDK动态署理,Ioc容器中,存储的是一个类型为Human的署理目标Humanhuman=context.getBean(Human.class);human.display();//输出署理类的父类,以此判别是JDK还是CGLibSystem.out.println(human.getClass().getSuperclass());}
留意看上面代码中,最长的那一句注释。由于咱们需求署理的类完成了接口,则Spring会运用JDK的动态署理,生成的署理类会完成相同的接口,然后创立一个署理目标存储在Spring容器中。这也就是说,在Spring容器中,这个署理bean的类型不是Student类型,而是Human类型,所以咱们不能经过Student.class获取,只能经过Human.class(或者经过它的名称获取)。这也证明了咱们上面说过的另一个问题,JDK动态署理无法署理没有界说在接口中的办法。假定Student这个类有别的一个办法,它不是Human接口界说的办法,此刻就算咱们为它装备了切面,也无法将切面织入。并且由于在Spring容器中保存的署理目标并不是Student类型,而是Human类型,这就导致咱们连那个不属于Human的办法都无法调用。这也说明了JDK动态署理的局限性。
咱们前面说过,JDK动态署理生成的署理类承继了Proxy这个类,而CGLib生成的署理类,则承继了需求进行署理的那个类,于是咱们能够经过输出署理目标所属类的父类,来判别Spring运用了何种署理。下面是输出成果:
beforestudentIamastudentclassjava.lang.reflect.Proxy//留意看,父类是Proxy
经过上面的输出成果,咱们发现,署理类的父类是Proxy,也就意味着公然运用的是JDK的动态署理。
(二)测验CGLib动态署理
好,测验完JDK动态署理,咱们开始测验CGLib动态署理。咱们前面说过,只有当需求署理的类没有完成接口时,Spring才会运用CGLib动态署理,于是咱们修改Student这个类的界说,不让他完成接口:
@ComponentpublicclassStudent{publicvoiddisplay(){System.out.println(“Iamastudent”);}}
由于Student没有完成接口,所以咱们的测验办法也需求做一些修改。之前咱们是经过Human.class这个类型从Spring容器中获取署理目标,可是现在,由于没有完成接口,所以咱们不能再这么写了,而是要写成Student.class,如下:
@TestpublicvoidtestProxy(){ApplicationContextcontext=newAnnotationConfigApplicationContext(AOPConfig.class);//修改为Student.classStudentstudent=context.getBean(Student.class);student.display();//同样输出父类System.out.println(student.getClass().getSuperclass());}
由于CGLib动态署理是生成了Student的一个子类,所以这个署理目标也是Student类型(子类也是父类类型),所以能够经过Student.class获取。下面是输出成果:
beforestudentIamastudentclasscn.tewuyiang.pojo.Student//此刻,父类是Student
能够看到,AOP成功收效,并且署理目标所属类的父类是Student,验证了咱们之前的说法。下面咱们修改一下Student类的界说,将display办法加上final修饰符,再看看效果:
@ComponentpublicclassStudent{//加上final修饰符publicfinalvoiddisplay(){System.out.println(“Iamastudent”);}}//输出成果如下:Iamastudentclasscn.tewuyiang.pojo.Student
能够看到,输出的父类仍然是Student,也就是说Spring依然运用了CGLib生成署理。可是咱们发现,咱们为display办法装备的前置告诉并没有履行,也就是署理类并没有为display办法进行署理。这也验证了咱们之前的说法,CGLib无法署理final办法,由于子类无法重写父类的final办法。下面咱们能够试着为Student类加上final修饰符,让他无法被承继,此刻看看成果。运行的成果会抛出反常,由于无法生成署理类,这儿就不贴出来了,能够自己去试试。
2|52.5强制Spring运用CGLib
经过上面的测验咱们会发现,CGLib的动态署理好像更加强大,而JDK的动态署理却约束颇多。并且前面也提过,CGLib的署理目标,履行署理办法的速度更快,仅仅生成署理类的功率较低。可是咱们运用到的bean大部分都是单例的,并不需求频频创立署理类,也就是说CGLib应该会更合适。可是为什么Spring默许运用JDK呢?这我也不太清楚,网上也没有找到相关的描述(假如有人知道,费事告诉我)。可是听说SpringBoot现在现已默许运用CGLib作为AOP的完成了。
那咱们能够强制Spring运用CGLib,而不运用JDK的动态署理吗?答案当然是能够的。咱们知道,假如要运用注解(@Aspect)办法装备切面,则需求在xml文件中装备下面一行敞开AOP:
<aop:aspectj-autoproxy/>
假如咱们期望只运用CGLib完成AOP,则能够在上面的这一行加点东西:
<aop:aspectj-autoproxyproxy-target-class=”true”/>
当然,假如咱们是运用Java类进行装备,比如说咱们上面用到的AOPConfig这个类,假如是经过这种办法装备,则强制运用CGLib的办法如下:
@Configuration@ComponentScan(basePackages=”cn.tewuyiang”)//如下:@EnableAspectJAutoProxy敞开AOP,//而proxyTargetClass=true就是强制运用CGLib@EnableAspectJAutoProxy(proxyTargetClass=true)publicclassAOPConfig{}
假如咱们是在xml文件中装备切面,则能够经过以下办法来强制运用CGLib:
<aop:configproxy-target-class=”true”>aop:config>
3|0三、总结
上面咱们就对Spring中AOP的完成原理做了一个大致的介绍。归根结底,SpringAOP的完成是经过动态署理,并且有两种完成办法,分别是JDK动态署理和CGLib动态署理。Spring默许运用JDK动态署理,只有在类没有完成接口时,才会运用CGLib。

springaop应用实例

1.AOP是什么
oop发生的问题
很对类方针和依赖方针仍存在依赖联系
乃至每个类中的所有办法都需求调用依赖方针办法
如果修正了办法需求修正N遍,不能一次修正完成
aop概念以及解决的问题
AOP:切面编程,能够通过预编译的办法和运行期动态署理完成在不修正源代码的情况下给程序动态一致添加功能的一种技能
解决的问题:AOP吧很多类方针中的横切问题点,从事务逻辑中分离出来然后到达解耦的目的,添加代码的重用性,提高开发功率
AOP的运用场景
切面类一定不是核心事务逻辑,功能是可有可无的状况能够运用AOP
经常运用场景:
日记记录
权限验证
事务处理
功率检查
反常处理
缓存处理
数据持久化
内容分发
2.AOP的运用
AOP切面
SpringAOP是根据JDK动态署理或CGLib署理在运行时期在方针初始化阶段织入代码
JDK动态署理VSCGLIb
JDK动态署理是根据接口完成
CGLib是根据类的继承完成的
告知的品种springaop
aroundadvice
最强大的环绕告知能够完成上面四种告知,能够操控方针办法的履行,能够在方针履行全过程中进行履行
步骤
1,创建切面类
@Aspect//告知Spring是一个切面类
@ComPonent//将该类放入IOC容器管理
publicclassadvice{}
2,界说切点
@Pointcut(value=”execution(*com.xx.xx.*.*(..))”)//阐明切入哪些类和办法
publicvoidmyPointCut{}
3,界说告知
@Aroud(“myPointCut()”)//告知要把代码切入到的切点(要写具体办法名包括括号)
publicObjectxxx(proceedingJointPointpjp){}作者:撕逼救不了咱们https://www.bilibili.com/read/cv16412885?jump_opus=1出处:bilibili

赞(0)
未经允许不得转载:志达IT网站 » springaop的实现原理(springaop应用实例)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

志达IT网站 每天分享编程和互联网的IT技术博客

登录/注册联系我们