SpringAop与AspectJ是什么
更新日期:
SpringAop与AspectJ是什么
理解1
根据我看spring官方文档的理解(不出意外是最正确的答案):
①选择spring的AOP还是AspectJ?
spring确实有自己的AOP。功能已经基本够用了,除非你的要在接口上动态代理或者方法拦截精确到getter和setter。这些都是写奇葩的需求,一般不使用。
②在使用AOP的时候,你是用xml还是注解的方式(@Aspect)?
1)如果使用xml方式,不需要任何额外的jar包。
2)如果使用@Aspect方式,你就可以在类上直接一个@Aspect就搞定,不用费事在xml里配了。但是这需要额外的jar包( aspectjweaver.jar)。因为spring直接使用AspectJ的注解功能,注意只是使用了它 的注解功能而已。并不是核心功能 !!!
注意到文档上还有一句很有意思的话:文档说到 是选择spring AOP还是使用full aspectJ?
什么是full aspectJ?如果你使用”full aspectJ”。就是说你可以实现基于接口的动态代理,等等强大的功能。而不仅仅是aspectj的 注-解-功-能 !!!
如果用full AspectJ。比如说Load-Time Weaving的方式 还 需要额外的jar包 spring-instrument.jar
当然,无论是使用spring aop还是 aspectj都需要aspectjweaver.jar spring-aop.jar这两个jar包。
参考:https://blog.csdn.net/maerrrr/article/details/78860420
理解2
区别
AspectJ
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
spring aop
Spring提供了四种类型的Aop支持
* 基于经典的SpringAOP
* 纯POJO切面
* @ASpectJ注解驱动的切面
* 注入式AspectJ切面(其实与Spring并无多大的关系,这个就是使用AspectJ这个框架实现Aop编程)
基于经典的SpringAop
其使用ProxyFactoryBean创建:
增强(通知)的类型有:
前置通知:org.springframework.aop.MethodBeforeAdvice
后置通知:org.springframework.aop.AfterReturningAdvice
环绕通知:org.aopalliance.intercept.MethodInterceptor
异常通知:org.springframework.aop.ThrowsAdvice
联系
我们借助于Spring Aop的命名空间可以将纯POJO转换为切面,实际上这些POJO只是提供了满足切点的条件时所需要调用的方法,但是,这种技术需要XML进行配置,不能支持注解。
所以spring借鉴了AspectJ的切面,以提供注解驱动的AOP,本质上它依然是Spring基于代理的AOP,只是编程模型与AspectJ完全一致,这种风格的好处就是不需要使用XML进行配置。
使用@Aspect方式,你就可以在类上直接一个@Aspect就搞定,不用费事在xml里配了。但是这需要额外的jar包( aspectjweaver.jar)。因为spring直接使用AspectJ的注解功能,注意只是使用了它 的注解功能而已,并不是核心功能 。SpringAop的底层技术依然是Jdk动态代理和Cglib。
理解3
AOP(Aspect OrientedProgramming, 面向切面/方面编程) 旨在从业务逻辑中分离出来横切逻辑【eg:性能监控、日志记录、权限控制等】,提高模块化,即通过AOP解决代码耦合问题,让职责更加单一。
运用技术:
SpringAOP使用了两种代理机制,一种是基于JDK的动态代理,另一种是基于CGLib的动态代理,之所以需要两种代理机制,很大程度上是因为JDK本身只提供基于接口的代理,不支持类的代理。
切面植入的方法:
1、编译期织入
2、类装载期织入
3、动态代理织入---->在运行期为目标类添加增强生成子类的方式,Spring AOP采用动态代理织入切面
流行的框架:
AOP现有两个主要的流行框架,即Spring AOP和Spring+AspectJ
二者的区别:
1、织入的时期不同
Spring Aop采用的动态织入,而Aspectj是静态织入。静态织入:指在编译时期就织入,即:编译出来的class文件,字节码就已经被织入了。动态织入又分静动两种,静则指织入过程只在第一次调用时执行;动则指根据代码动态运行的中间状态来决定如何操作,每次调用Target的时候都执行。有不清楚的同学,可以自己补下基础的代理知识
2、从使用对象不同
Spring AOP的通知是基于该对象是SpringBean对象才可以,而AspectJ可以在任何Java对象上应用通知。
Spring AOP:如果你想要在通过this对象调用的方法上应用通知,那么你必须使用currentProxy对象,并调用其上的相应方法;于此相似,如果你想要在某对象的方法上应用通知,那么你必须使用与该对象相应的Spring bean
AspectJ:使用AspectJ的一个间接局限是,因为AspectJ通知可以应用于POJO之上,它有可能将通知应用于一个已配置的通知之上。对于一个你没有注意到这方面问题的大范围应用的通知,这有可能导致一个无限循环。
Spring AOP不同于大多数其他AOP框架。Spring AOP的目的并不是为了提供最完整的AOP实现(虽然Spring AOP具有相当的能力);而是为了要帮助解决企业应用中的常见问题,提供一个AOP实现与Spring IOC之间的紧密集成。由于Spring AOP是容易实现的,如果你计划在Spring Beans之上将横切关注点模块化,Spring的这一目标将是要点之一。但同样的目标也可能成为一个限制,如果你用的是普通的Java对象而不是Spring beans,并基于此将横切关注点模块化的话。另一方面,AspectJ可用于基于普通Java对象的模块化,但在实施之前需要良好的关于这个主题的知识。
参考:https://blog.csdn.net/a128953ad/article/details/50509437
理解4(推荐查看)
Spring AOP and AspectJ
现在,一起来讨论Spring AOP and AspectJ,跨越多指标,如能力和目标、织入方式、内部结构、连接点和简单性。
Capabilities and Goals
简而言之,Spring AOP和AspectJ有不同的目标。
Spring AOP旨在通过Spring IoC提供一个简单的AOP实现,以解决编码人员面临的最常出现的问题。这并不是完整的AOP解决方案,它只能用于Spring容器管理的beans。
另一方面,AspectJ是最原始的AOP实现技术,提供了玩这个的AOP解决方案。AspectJ更为健壮,相对于Spring AOP也显得更为复杂。值得注意的是,AspectJ能够被应用于所有的领域对象。
Weaving
AspectJ and Spring AOP使用了不同的织入方式,这影响了他们在性能和易用性方面的行为。
AspectJ使用了三种不同类型的织入:
- 编译时织入:AspectJ编译器同时加载我们切面的源代码和我们的应用程序,并生成一个织入后的类文件作为输出。
- 编译后织入:这就是所熟悉的二进制织入。它被用来编织现有的类文件和JAR文件与我们的切面。
- 加载时织入:这和之前的二进制编织完全一样,所不同的是织入会被延后,直到类加载器将类加载到JVM。
更多关于AspectJ的信息,请见head on over to this article。
AspectJ使用的是编译期和类加载时进行织入,Spring AOP利用的是运行时织入。
运行时织入,在使用目标对象的代理执行应用程序时,编译这些切面(使用JDK动态代理或者CGLIB代理)。
如果我们分析本节所有的论点,我们就会开始明白,没有绝对的一个框架比另一个框架更好。
简而言之,选择很大程度上取决我们的需求:
- 框架:如果应用程序不使用Spring框架,那么我们别无选择,只能放弃使用Spring AOP的想法,因为它无法管理任何超出spring容器范围的东西。 但是,如果我们的应用程序完全是使用Spring框架创建的,那么我们可以使用Spring AOP,因为它很直接便于学习和应用。
- 灵活性:鉴于有限的连接点支持,Spring AOP并不是一个完整的AOP解决方案,但它解决了程序员面临的最常见的问题。 如果我们想要深入挖掘并利用AOP达到其最大能力,并希望获得来自各种可用连接点的支持,那么AspectJ是最佳选择。
- 性能:如果我们使用有限的切面,那么性能差异很小。 但是,有时候应用程序有数万个切面的情况。 在这种情况下,我们不希望使用运行时织入,所以最好选择AspectJ。 已知AspectJ比Spring AOP快8到35倍。
- 共同优点:这两个框架是完全兼容的。 我们可以随时利用Spring AOP,并且仍然使用AspectJ来获得前者不支持的连接点。
参考:https://www.cnblogs.com/Irving/p/9739889.html
理解5
Spring AOP
在纯 Java 中实现
不需要单独的编译过程
只能使用运行时织入
功能不强-仅支持方法级编织
只能在由 Spring 容器管理的 bean 上实现
仅支持方法执行切入点
代理是由目标对象创建的, 并且切面应用在这些代理上
比 AspectJ 慢多了
AspectJ
使用 Java 编程语言的扩展实现
除非设置 LTW,否则需要 AspectJ 编译器 (ajc)
运行时织入不可用。支持编译时、编译后和加载时织入
更强大 - 可以编织字段、方法、构造函数、静态初始值设定项、最终类/方法等…。
可以在所有域对象上实现
支持所有切入点
在执行应用程序之前 (在运行时) 前, 各方面直接在代码中进行织入
更好的性能
原文链接:https://blog.csdn.net/weixin_42181142/article/details/101212351
问题1
我们知道JDK的动态代理是针对接口的,在运行期生成代理类。
CGLIB动态代理 是可以针对接口与普通类(继承方式),底层使用ASM框架生成字节码完成代理功能
我在网上查资料说AspectJ是静态代理 在编译期间就生成了class文件完成了代理。这点容易理解一种编译的技术。
那么我的问题是
问题一1
2
3Spring的AOP为什么需要引入aspectjrt-1.6.8.jar与aspectjweaver-1.6.8.jar这两个包?如果是接口直接使用JDK的动态代理,如果是普通类则用cglib不就完了吗?这是我的第一个问题
问题二
第二个问题是Spring使用aspectjrt-1.6.8.jar与aspectjweaver-1.6.8.jar包 那么是编译期代理 还是 运行期代理 求详细解释
参考:https://www.iteye.com/problems/98151
问题2
AspectJ是Eclipse基金组织的开源项目,它是Java语言的一个AOP实现,是最早、功能比较强大的AOP实现之一,对整套AOP机制都有较好的实现,很多其他语言的AOP实现也借鉴或者采纳了AspectJ中的很多设计。在Java领域,AspectJ中的很多语法结构基本上已经成为AOP领域的标准。
要知道的是,AspectJ框架和Spring框架实现AOP的方式是不一样的,AspectJ是在编译时进行增强,所以它有一个专门的编译器ajc来生成遵守Java字节码编码规范的Class文件。而Spring采用的是动态代理的方式,它并不需要有一个专门的编译器。故也称AspectJ为静态AOP实现,而Spring AOP为动态AOP实现。
AspectJ主要包含两个部分:第一个部分定义了如何表达、定义AOP编程中的语法规范;第二个部分是工具部分,包括编译器、调试工具等。
虽然 AspectJ 很强大,但是跟 lombok不兼容,在编译期间会打架,谁也不认识谁,所以还是放弃吧,老老实实用 AOP
问题3
基于Aspect Spring AOP 开发
Spring AOP 与ApectJ 的目的一致,都是为了统一处理横切业务,但与AspectJ不同的是,Spring AOP 并不尝试提供完整的AOP功能(即使它完全可以实现),Spring AOP 更注重的是与Spring IOC容器的结合,并结合该优势来解决横切业务的问题,因此在AOP的功能完善方面,相对来说AspectJ具有更大的优势,同时,Spring注意到AspectJ在AOP的实现方式上依赖于特殊编译器(ajc编译器),因此Spring很机智回避了这点,转向采用动态代理技术的实现原理来构建Spring AOP的内部机制(动态织入),这是与AspectJ(静态织入)最根本的区别。在AspectJ 1.5后,引入@Aspect形式的注解风格的开发,Spring也非常快地跟进了这种方式,因此Spring 2.0后便使用了与AspectJ一样的注解。请注意,Spring 只是使用了与 AspectJ 5 一样的注解,但仍然没有使用 AspectJ 的编译器,底层依是动态代理技术的实现,因此并不依赖于 AspectJ 的编译器
原文链接:https://blog.csdn.net/javazejian/article/details/56267036
对于AspectJ的使用
Aspect是实现AOP编程的一种具体实现
- 编译时织入
,利用ajc编译器替代javac编译器,直接将源文件(java或者aspect文件)编译成class文件并将切面织入进代码。 - 编译后织入
,利用ajc编译器向javac编译期编译后的class文件或jar文件织入切面代码。 - 加载时织入(LTW)
,不使用ajc编译器,利用aspectjweaver.jar工具,使用java agent代理在类加载期将切面织入进代码。
使用LTW方式(加载时编织)
前两种织入方法都依赖于ajc的编译工具,LTW却通过java agent机制在内存中操作类文件,可以不需要ajc的支持做到 动态织入
不过,这里有一个挺有意思的问题,我们知道编译期一定会编译AnnoAspect类,那么这时候通过切面语法我们就可以找到他要处理的App类,这大概就是编译阶段织入的大概流程。但是如果在类加载期处理的话,当类加载到App类的时候,我们并不知道这个类需要被AnnoAspect处理。。。因此为了实现LTW,我们肯定要有个配置文件,来告诉类加载器,某某某切面需要优先考虑,他们很可能会影响其他的类。
为了实现LTW,我们需要在资源目录下配置META-INF/aop.xml文件,来告知类加载器我们当前注册的切面。
在上面的项目中,我们其实只需要创建src/main/resources/META-INF/aop.xml:
参考:https://blog.csdn.net/whatigame/article/details/103173308
编织方式
1.1 LTW与不同的切面织入时机
AOP——面向切面编程,通过为目标类织入切面的方式,实现对目标类功能的增强。按切面被织如到目标类中的时间划分,主要有以下几种:
- 1.运行期织入
这是最常见的,比如在运行期通过为目标类生成动态代理的方式实现AOP就属于运行期织入,这也是Spring AOP中的默认实现,并且提供了两种创建动态代理的方式:JDK自带的针对接口的动态代理和使用CGLib动态创建子类的方式创建动态代理。 - 2.编译期织入
使用特殊的编译器在编译期将切面织入目标类,这种比较少见,因为需要特殊的编译器的支持。 - 3.类加载期织入
通过字节码编辑技术在类加载期将切面织入目标类中,这是本篇介绍的重点。它的核心思想是:在目标类的class文件被JVM加载前,通过自定义类加载器或者类文件转换器将横切逻辑织入到目标类的class文件中,然后将修改后class文件交给JVM加载。这种织入方式可以简称为LTW(LoadTimeWeaving)。
1.2 JDK实现LTW的原理
可以使用JKD的代理功能让代理器访问到JVM的底层组件,借此向JVM注册类文件转换器,在类加载时对类文件的字节码进行转换。具体而言,java.lang.instrument包下定义了ClassFileTransformer接口,该接口的作用如下面的注释所描述
1 | * An agent provides an implementation of this interface in order |
可以通过实现该接口,并重写如下抽象方法自定义类文件转换规则
1 | byte[] |
classfileBuffer是原始类文件对应的字节码数组,返回的byte[]为转化后的字节码数组,如果返回null,则表示不进行字节码处理。
而java.lang.instrument包下的Instrumentation接口则可以将我们自定义的ClassTransFormer向JVM内部的组件进行注册
1 | void |
在实际使用中,可以通过JVM的-javaagent代理参数在启动时获取JVM内部组件的引用,将ClassFileTransformer实例注册到JVM中,JVM在加载Class文件时,会先调用这个ClassTransformer的transform()方法对Class文件的字节码进行转换,比如织入切面中定义的横切逻辑,实现AOP功能。
参考:https://www.cnblogs.com/takumicx/p/10150344.html
装载方式
编译期织入、装载期织入、运行时织入
AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类,其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
静态代理分为:编译时织入(特殊编译器实现)、类加载时织入(特殊的类加载器实现)。
动态代理有 : jdk动态代理(基于接口来实现)、CGlib(基于类实现)。
编译期织入:指在Java编译期间,采用特殊的编译器,将切面织入到java类中;
类加载期织入:指通过特殊的类加载器,在类字节码加载到JVM时,织入切面
运行期织入:采用cglib或者jdk动态代理进行切面织入
aspectj的三种织入:
编译期:把aspect类(aop切面)和目标类放在一起用ajc编译器编译
后编译期:目标类可能已经被打成了一个jar包,这个时候也可以用ajc命令将jar再织入一次
类加载期(Load-Time Weaving):
在JVM加载类字节码文件时,做字节码替换
其中前两个时间点,可以理解为静态织入,因为在class文件生成后,就已经织入好了。类加载期织入,可以理解为“动态织入”(注意不同于java动态代理的“动态”),因为这个类替换是在jvm加载类的时候完成的
原文链接:https://blog.csdn.net/wenbingoon/article/details/22888619
Spring到底有没有用到AspectJ的实现(结论)
如何判断是织入还是代理
这个问题很有意思,也是非常容易被搞混的,尤其是在讨论spring aop的时候。我们知道spring里有很多基于动态代理的设计,而我们知道动态代理也可以被用作面向切面的编程,但是spring aop本身却支持aspectj的切面语法,而且spring-aop这个包也引用了aspectj,我们知道aspectj是通过织入的方式来实现aop的。。。那么 spring aop究竟是通过织入还是代理来实现aop的呢?
没错就是动态代理
其实spring aop还是通过 动态代理
来实现aop的,即使不去看他的源码,我们也可以通过简单的实验来得到这个结论。
根据aspectj的使用方式,我们知道,如果要向代码中织入切面,那么我们要么采用ajc编译,要么使用aspectjweaver的agent代理。但是spring既没有依赖任何aspectjtools的相关jar包,虽然依赖了aspectjweaver这个包,但是并没有添加agent代理。当然,也存在一种可能就是spring利用aspectjweaver这个包自己实现了动态织入,但是从可复用的角度讲,spring真的会自己重新造轮子?如果真的重新造了那为啥不脱离aspectj彻底重新造,而是用一半造一半呢?
而且,我们知道用织入和用动态代理有一个很大的区别,如果使用织入的话,那么调业务对象的getClass()方法获得的类名就是这个类本身实现的类名;但是如果使用动态代理的话,调用getClass()方法获得的类名就是动态代理类的类名了。做一个简单的实验我们就可以发现,如果我们使用spring aop来对某一个service进行切面处理,那么调用getClass()方法获得的结果就是:
1 | com.mythsman.test.Myservice$$EnhancerBySpringCGLIB$$3afc9148 |
显然,虽然spring aop采用了aspectj语法来定义切面,但是在实现切面逻辑的时候还是采用CGLIB来进行动态代理的方法。
强行织入?
当然,如果我们想,我们也可以强行采用织入的方式,不过我们就不能将切面类注册为spring的bean,并且采用ajc插件编译或者java agent在类加载时织入。
参考:https://blog.csdn.net/whatigame/article/details/103173308
额外说明
emmm
在使用spring-test包测试时,也就是整合junit进行测试
结果使用xml配置的事务出错,原因是找不到aspectjweaver包下的一些类,添加aspectj包即可
(但是项目正常启动时可以不添加aspectjweaver包的),,,,,不知道这个测试包怎么。。。
1 | ... |