文章目录
  1. 1. SpringAop与AspectJ是什么
    1. 1.1. 理解1
    2. 1.2. 理解2
    3. 1.3. 理解3
    4. 1.4. 理解4(推荐查看)
    5. 1.5. 理解5
  2. 2. 问题1
  3. 3. 问题2
  4. 4. 问题3
  5. 5. 对于AspectJ的使用
    1. 5.1. 使用LTW方式(加载时编织)
  6. 6. 编织方式
    1. 6.0.1. 1.1 LTW与不同的切面织入时机
    2. 6.0.2. 1.2 JDK实现LTW的原理
  • 7. 装载方式
  • 8. Spring到底有没有用到AspectJ的实现(结论)
    1. 8.1. 如何判断是织入还是代理
    2. 8.2. 没错就是动态代理
    3. 8.3. 强行织入?
    4. 8.4. 额外说明
  • 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使用了三种不同类型的织入:

    1. 编译时织入:AspectJ编译器同时加载我们切面的源代码和我们的应用程序,并生成一个织入后的类文件作为输出。
    2. 编译后织入:这就是所熟悉的二进制织入。它被用来编织现有的类文件和JAR文件与我们的切面。
    3. 加载时织入:这和之前的二进制编织完全一样,所不同的是织入会被延后,直到类加载器将类加载到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
    3
    Spring的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编程的一种具体实现

    1. 编译时织入
      ,利用ajc编译器替代javac编译器,直接将源文件(java或者aspect文件)编译成class文件并将切面织入进代码。
    2. 编译后织入
      ,利用ajc编译器向javac编译期编译后的class文件或jar文件织入切面代码。
    3. 加载时织入(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
    2
    3
    * An agent provides an implementation of this interface in order
    * to transform class files.
    * The transformation occurs before the class is defined by the JVM.

    可以通过实现该接口,并重写如下抽象方法自定义类文件转换规则

    1
    2
    3
    4
    5
    6
    7
    byte[]
    transform( ClassLoader loader,
    String className,
    Class<?> classBeingRedefined,
    ProtectionDomain protectionDomain,
    byte[] classfileBuffer)
    throws IllegalClassFormatException;

    classfileBuffer是原始类文件对应的字节码数组,返回的byte[]为转化后的字节码数组,如果返回null,则表示不进行字节码处理。

    而java.lang.instrument包下的Instrumentation接口则可以将我们自定义的ClassTransFormer向JVM内部的组件进行注册

    1
    2
    void
    addTransformer(ClassFileTransformer transformer);

    在实际使用中,可以通过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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    ...
    Caused by: java.lang.IllegalStateException: Failed to introspect Class [org.springframework.aop.aspectj.AspectJExpressionPointcut] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:659)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:556)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:541)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:245)
    ... 60 more
    Caused by: java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.getDeclaredMethods(Class.java:1975)
    at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:641)
    ... 63 more
    Caused by: java.lang.ClassNotFoundException: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 67 more
    文章目录
    1. 1. SpringAop与AspectJ是什么
      1. 1.1. 理解1
      2. 1.2. 理解2
      3. 1.3. 理解3
      4. 1.4. 理解4(推荐查看)
      5. 1.5. 理解5
    2. 2. 问题1
    3. 3. 问题2
    4. 4. 问题3
    5. 5. 对于AspectJ的使用
      1. 5.1. 使用LTW方式(加载时编织)
    6. 6. 编织方式
      1. 6.0.1. 1.1 LTW与不同的切面织入时机
      2. 6.0.2. 1.2 JDK实现LTW的原理
  • 7. 装载方式
  • 8. Spring到底有没有用到AspectJ的实现(结论)
    1. 8.1. 如何判断是织入还是代理
    2. 8.2. 没错就是动态代理
    3. 8.3. 强行织入?
    4. 8.4. 额外说明