SpringFramework学习-(13)SpringAOP与AspectJ

SpringFramework学习-(11)初识AOP介绍了AOP的基本概念和作用,以及SpringAop的一些实现小例子。
SpringFramework学习-(12)AOP与代理模式介绍了AOP的内部实现原理。

现在的疑惑来了
1)SpringAOP和AspectJ是什么关系?
2)SpringAOP、AspectJ和CGLIB是什么关系?
3)SpringAOP、AspectJ和JDK代理是什么关系?
4)什么时候使用SpringAOP、什么时候使用AspectJ?

这些问题一直也困扰着我,所以这次就好好的来看看他们的关系。

1.AspectJ

AspectJ 是一个基于 Java 语言的 AOP 框架,提供了强大的 AOP 功能,其他很多 AOP 框架都借鉴或采纳其中的一些思想。
AspectJ 是 Java 语言的一个 AOP 实现,其主要包括两个部分:第一个部分定义了如何表达、定义 AOP 编程中的语法规范,通过这套语言规范,我们可以方便地用 AOP 来解决 Java 语言中存在的交叉关注点问题;另一个部分是工具部分,包括编译器、调试工具等。
AspectJ 是最早、功能比较强大的 AOP 实现之一,对整套 AOP 机制都有较好的实现,很多其他语言的 AOP 实现,也借鉴或采纳了 AspectJ 中很多设计。在 Java 领域,AspectJ 中的很多语法结构基本上已成为 AOP 领域的标准。

下载、安装 AspectJ 比较简单,读者登录 AspectJ 官网(http://www.eclipse.org/aspectj),即可下载到一个可执行的 JAR 包,使用 java -jar aspectj-1.x.x.jar 命令、多次单击“Next”按钮即可成功安装 AspectJ。

·bin:存放了 aj、aj5、ajc、ajdoc、ajbrowser 等命令,其中 ajc 命令最常用,它的作用类似于 javac
·doc:存放了 AspectJ 的使用说明、参考手册、API 文档等文档
·lib:该路径下的 4 个 JAR 文件是 AspectJ 的核心类库
·相关授权文件。
public class SayHelloService {
    public void say(){
        System.out.print("Hello  AspectJ");
    }
} 

在需要在调用say()方法之后,需要记录日志。那就是通过AspectJ的后置增强。

public aspect LogAspect{
    pointcut logPointcut():execution(void SayHelloService.say());
    after():logPointcut(){
         System.out.println("记录日志 ..."); 
    }
}

编译

执行命令 ajc -d . SayHelloService.java LogAspect.java
生成 SayHelloService.class
执行命令 java SayHelloService
输出 Hello AspectJ 记录日志

ajc.exe 可以理解为 javac.exe 命令,都用于编译 Java 程序,区别是 ajc.exe 命令可识别 AspectJ 的语法;
我们可以将 ajc.exe 当成一个增强版的 javac.exe 命令.执行ajc命令后的 SayHelloService.class 文件不是由原来的 SayHelloService.java 文件编译得到的,该 SayHelloService.class 里新增了打印日志的内容——这表明 AspectJ 在编译时“自动”编译得到了一个新类,这个新类增强了原有的 SayHelloService.java 类的功能,因此 AspectJ 通常被称为编译时增强的 AOP 框架。

AspectJ 在编译时“自动”编译得到了一个新类,这个新类(代理类)增强了原有的类(被代理类)的功能,因此 AspectJ 通常被称为编译时增强的 AOP 框架(通过对编译后的被代理类反编译就可以看到增强代码,这里不展示了)。

AspectJ织如图(引自http://blog.csdn.net/javazejian/article/details/56267036
这里写图片描述

与 AspectJ 相对的还有另外一种 AOP 框架,它们不需要在编译时对目标类进行增强,而是运行时生成目标类的代理类,该代理类要么与目标类实现相同的接口,要么是目标类的子类——总之,代理类的实例可作为目标类的实例来使用。一般来说,编译时增强的 AOP 框架在性能上更有优势——因为运行时动态增强的 AOP 框架需要每次运行时都进行动态增强。

2.Spring AOP

与 AspectJ 相同的是,Spring AOP 同样需要对目标类进行增强,也就是生成新的 AOP 代理类;与 AspectJ 不同的是,Spring AOP 无需使用任何特殊命令对 Java 源代码进行编译,它采用运行时动态地、在内存中临时生成“代理类”的方式来生成 AOP 代理。

Spring 允许使用 AspectJ Annotation 用于定义方面(Aspect)、切入点(Pointcut)和增强处理(Advice),Spring 框架则可识别并根据这些 Annotation 来生成 AOP 代理。Spring 只是使用了和 AspectJ 5 一样的注解,但并没有使用 AspectJ 的编译器或者织入器(Weaver),底层依然使用的是 Spring AOP,依然是在运行时动态生成 AOP 代理,并不依赖于 AspectJ 的编译器或者织入器。
简单地说,Spring 依然采用运行时生成动态代理的方式来增强目标对象,所以它不需要增加额外的编译,也不需要 AspectJ 的织入器支持;而 AspectJ 在采用编译时增强,所以 AspectJ 需要使用自己的编译器来编译 Java 文件,还需要织入器。

为了启用 Spring 对 @AspectJ 方面配置的支持,并保证 Spring 容器中的目标 Bean 被一个或多个方面自动增强,必须在 Spring 配置文件中添加如下配置。

<aop:aspectj-autoproxy/>

当启动了 @AspectJ 支持后,在 Spring 容器中配置一个带 @Aspect 注释的 Bean,Spring 将会自动识别该 Bean,并将该 Bean 作为方面 Bean 处理。方面Bean与普通 Bean 没有任何区别,一样使用

@Component
public class SayHelloService {
    public void say(){
        System.out.print("Hello  AspectJ");
    }
} 

做后置增强的日志处理。

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
     @After("execution(* com.ywsc.fenfenzhong.aspectj.learn.SayHelloService.*(..))")
     public void log(){
         System.out.println("记录日志 ...");
     }
}

测试

public class TestCase {
    public static void main(String[] args) {
        SayHelloService sayHelloService = ApplicationUtil.getContext().getBean(SayHelloService.class);
        sayHelloService.say();
    }
}

结果

Hello AspectJ
记录日志…

AOP 代理 = 原来的业务类+增强处理。
这个生成AOP 代理由 Spring 的 IoC 容器负责生成。也由 IoC 容器负责管理。因此,AOP 代理可以直接使用容器中的其他 Bean 实例作为目标,这种关系可由 IoC 容器的依赖注入提供。回顾上面的例子,其中程序员参与的只有 3 个部分:

. 定义普通业务组件。
. 定义切入点,一个切入点可能横切多个业务组件。
. 定义增强处理,增强处理就是在 AOP 框架为普通业务组件织入的处理动作。

AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理主要分为静态代理和动态代理两大类,AspectJ 是静态代理的代表;而Spring AOP则是动态代理则的代表。

静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;
动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。

3.CGLIB

这里再次介绍到cglib。

CGLIB(Code Generation Library)它是一个代码生成类库。它可以在运行时候动态是生成某个类的子类。代理模式为要访问的目标对象提供了一种途径,当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,唯一限制便是使用动态代理的对象必须实现一个或多个接口。而CGLIB却不必有此限制。要想Spring AOP 通过CGLIB生成代理,只需要在Spring 的配置文件引入

<aop:aspectj-autoproxy proxy-target-class="true"/>

CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。由于没有了解过class 文件和字节码,因而也就写不下去了。

到这里我们就明白了他们之间的关联了:
1)SpringAOP和AspectJ是独立存在的;
2)SpringAOP使用JDK动态代理和CGLIB两种动态代理,根据是否实现接口切换使用JDK动态代理还是CGLIB动态代理。

那么还有一个问题SpringAOP已经实现了AOP,那么为什么还要使用AspectJ集成呢?
看看官方文档怎么说
|————————————————————————————————————————————-
Use the simplest thing that can work. Spring AOP is simpler than using full AspectJ as there is no requirement to introduce the AspectJ compiler / weaver into your development and build processes. If you only need to advise the execution of operations on Spring beans, then Spring AOP is the right choice. If you need to advise objects not managed by the Spring container (such as domain objects typically), then you will need to use AspectJ. You will also need to use AspectJ if you wish to advise join points other than simple method executions (for example, field get or set join points, and so on).

When using AspectJ, you have the choice of the AspectJ language syntax (also known as the “code style”) or the @AspectJ annotation style. Clearly, if you are not using Java 5+ then the choice has been made for you…​ use the code style. If aspects play a large role in your design, and you are able to use the AspectJ Development Tools (AJDT) plugin for Eclipse, then the AspectJ language syntax is the preferred option: it is cleaner and simpler because the language was purposefully designed for writing aspects. If you are not using Eclipse, or have only a few aspects that do not play a major role in your application, then you may want to consider using the @AspectJ style and sticking with a regular Java compilation in your IDE, and adding an aspect weaving phase to your build script.

使用最简单的方法。Spring AOP比使用full AspectJ更简单,因为没有必要将AspectJ编译器/ weaver引入到开发和构建过程中。如果您只需要通知Spring bean的操作的执行,那么Spring AOP是正确的选择。如果需要通知由非Spring容器(例如通常的域对象)管理的对象,则需要使用AspectJ。您还需要使用AspectJ,如果您希望通知连接点,而不是简单的方法执行(例如,字段获取或设置连接点,等等)。
使用AspectJ时,您可以选择AspectJ语言语法(也称为“代码风格”)或@ AspectJ注释样式。很明显,如果你不使用Java 5 +然后为你已经作出了选择…使用的代码风格。如果方面在您的设计中发挥了很大的作用,并且您能够使用AspectJ开发工具(AJDT)Eclipse插件,那么AspectJ语言语法是首选的选项:它更简洁、更简单,因为语言是专门为编写方面而设计的。如果你不使用Eclipse,或只有几个方面不扮演重要角色在你的应用程序中,那么您可能需要考虑使用@ aspectj风格和坚持常规Java IDE编译,并添加一个方面编织阶段构建脚本。
—————————————————————————————————————————————–|
1)通过官方文档的翻译我们看到了,虽然SpringAOP已经足够强大了,但是有些处理还是不够完美,所以集成AspectJ可以起到一定的补充作用。
但是
|——————————————————————————————————————————
@AspectJ refers to a style of declaring aspects as regular Java classes annotated with annotations. The @AspectJ style was introduced by the AspectJ project as part of the AspectJ 5 release. Spring interprets the same annotations as AspectJ 5, using a library supplied by AspectJ for pointcut parsing and matching. The AOP runtime is still pure Spring AOP though, and there is no dependency on the AspectJ compiler or weaver.

@Aspectj引用了一种声明方面的样式,它是用注释注释的常规Java类。AspectJ项目引入了@AspectJ样式作为AspectJ 5发布的一部分。Spring解释了与AspectJ 5相同的注解,使用AspectJ提供的库来进行切入点分析和匹配。但是AOP运行时仍然是纯粹的Spring AOP,并且不依赖于AspectJ编译器或weaver。
——————————————————————————————————————————————|
2)@Aspect注解是由AspectJ最先提供,Spring直接使用AspectJ的注解功能,但只是使用了它 的注解功能而已。并不是核心功能 !!!

到此,SpringAOP和AspectJ的关系应该足够清晰了。至于SpringAOP为什么是这个样子,为什么不自己去做一套完整的AOP,这个问题就好比spring-core为什么要依赖于commons-logging一样。没有意义!

做自己最重要的是,专业的人做专业的事。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值