Spring AOP 的简单应用
因为工作需求,自己去了解一下aop并做下的记录,当然大部分都是参考他人博客以及官方文档。
目录
<a name="关于 AOP"></a>
关于 AOP
大家都知道Spring框架有两大重要特性,IOC 控制反转 (Inversion of Control,IoC) 以及AOP 面向切面编程(Aspect Oriented Program, AOP)。今天主要是来一起了解一下AOP。
其主要作用是,在不修改源代码的情况下给某个或者一组操作添加额外的功能。像日志记录,事务处理,权限控制等功能,都可以用AOP来“优雅”地实现,使这些额外功能和真正的业务逻辑分离开来,软件的结构将更加清晰。
简单来说在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
<a name = "相关术语"></a>
相关术语
Aspect(切面)
aspect
由pointcut
和advice
组成,它即包含了横切的定义,也包含了链接点的定义。由Spring AOP负责实施切面,它将切面所定义的横切逻辑织入到切面所指定的链接点中。
简单来说,只要在类上有 @Aspect 注解的类就是切面。
Join point(链接点/记录点)
程序运行中的一个点,例如一个运行方法或者异常处理。
在Spring AOP中,一个join point总是一个运行方法,即只有方法才是连接点。
advice (增强/通知)
在join point(即满足 point cut 规则的join point)上特定的时刻执行的操作,Advice有几种不同类型,下文将会讨论(通俗地来讲就是起作用的内容和时间点)。
Pointcut(切点)
匹配 join point 的谓词(a predicate that matches join points).
advice 与 pointcut 表达式相关联,并在与 pointcut 匹配的任意 joinpoint 运行(例如,执行具有特定名称的方法)。
简单来说 pointcut 是一个joinpoint 的过滤器,只有满足 pointcut 的规则的 joinpoint 才会执行 advice。
Introduction
为一个类型添加额外的方法或字段. Spring AOP 允许我们为 目标对象 引入新的接口(和对应的实现). 例如我们可以使用 introduction 来为一个 bean 实现 IsModified 接口, 并以此来简化 caching 的实现.
Target object
织入一个或多个 advice 的目标对象. 目标对象也被称为 advised object.
因为 Spring AOP 使用运行时代理的方式来实现 aspect, 因此 adviced object 总是一个代理对象(proxied object)
注意, adviced object 指的不是原来的类, 而是织入 advice 后所产生的代理类.
AOP proxy
一个类被 AOP 织入 advice, 就会产生一个结果类, 它是融合了原类和增强逻辑的代理类.在 Spring AOP 中, AOP 代理将是一个 JDK 动态代理对象或 CGLIB 代理对象.
Weaving (织入)
将 aspect 和其他对象连接起来, 并创建 adviced object 的过程.根据不同的实现技术, AOP织入有三种方式:
- 编译器织入, 这要求有特殊的Java编译器.
- 类装载期织入, 这需要有特殊的类装载器.
- 动态代理织入, 在运行期为目标类添加增强(Advice)生成子类的方式.
与其他纯Java AOP框架一样,Spring AOP在运行时执行编织。
advice 的类型
- before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
- after return advice, 在一个 join point 正常返回后执行的 advice
- after throwing advice, 当一个 join point 抛出异常后执行的 advice
- after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
- around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
Pointcut expression
Pointcut通过pointcut expression来描述,有若干种限定词。具体可以参考Spring文档7.2.3 Declaring a pointcut 或 aspectj-cheat-sheet.
<a name="Spring AOP的使用"></a>
Spring AOP的使用
我们可以通过三种方式来使用Spring AOP,他们分别是:@Aspect-based(Annotation),Schema-based(XML),以及底层的Spring AOP API。我们后续讲解的主要是基于注解的实现。
使用xml的实现原理与使用注解基本一致,基本上只是把注解包含的配置信息都转移到了xml配置文件中。
至于AOP API则是Spring1.2提供的历史用法,现在的Sping4也仍然支持,注解与xml也是基于其上的使用。它是SpringAOP的基础,有兴趣的可以去参考链接4查看。
<a name="基于注解的使用"></a>
基于注解的使用
添加 @AspectJ 支持
@AspectJ 是一种使用 Java 注解来实现 AOP 的编码风格.
@AspectJ 风格的 AOP 是 AspectJ Project 在 AspectJ 5 中引入的, 并且 Spring 也支持@AspectJ 的 AOP 风格.
<a name="添加依赖"></a>
添加依赖
<!-- 5)Spring AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- aspectj依赖开始 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- aspectj依赖结束 -->
<a name="开启 @Aspect 注解"></a>
开启 @Aspect 注解
在 spring-mvc.xml 中添加一下语句 用于启用@Aspect注解
<aop:aspectj-autoproxy/> //jdk 代理
或
<aop:aspectj-autoproxy proxy-target-class="true" /> //cglib 代理
并且需要在xml中加上 aop 的 namespace
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- bean definitions here -->
</beans>
jdk 代理 与 CGlib 代理
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的 方法 。
因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。
<a name="定义 aspect(切面)"></a>
定义 aspect(切面)
当使用注解 @Aspect 标注一个 Bean 后, 那么 Spring 框架会自动收集这些 Bean, 并添加到 Spring AOP 中, 例如:
@Aspect
@Component
public class TestAspect {
}
请注意,@Aspect 不能被 Spring 自动识别并注册为 Bean;为此,您需要添加一个单独的 @Component 注释
<a name="声明 pointcut"></a>
声明 pointcut
pointcut 声明由两部分组成:
Pointcut签名(signature) 包括方法名和相关参数
Pointcut表示式(expression) 用来指定哪些方法执行是我们感兴趣的(即因此可以织入 advice).
pointcut expression
这个方法必须无返回值.
这个方法本身就是 pointcut signature, pointcut 表达式使用@Pointcut 注解指定.
上图定义了一个 pointcut,它所描述的是:匹配在项目路径 aspects.trace.demo 下的所有方法的执行
<a name="切点标志符"></a>
切点标志符(designator)
具体使用请参考案例代码
- execution - for matching method execution join points, this is the primary pointcut designator you will use when working with Spring AOP
- within - limits matching to join points within certain types (simply the execution of a method declared within a matching type when using Spring AOP)
- this - limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type
- target - limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type
- args - limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types
- bean - limit the matching of join points to a particular named Spring bean, or to a set of named Spring beans (when using wildcards).
- @target - limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type
- @args - limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given type(s)
- @within - limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)
- @annotation - limits matching to join points where the subject of the join point (method being executed in Spring AOP) has the given annotation
<a name="声明 advice"></a>
声明 advice
advice 是和一个 pointcut 表达式关联在一起的, 并且会在匹配的 join point 的方法执行的前/后/周围 运行. pointcut 表达式可以是简单的一个 pointcut 名字的引用, 或者是完整的 pointcut 表达式.
* Before
Before Advice 由切面中的 @Before 注解声明
代表 advice 在 joinpoint 之前执行
* After returning
当匹配的方法正常执行并返回时运行 After returning Advice。
由 @AfterReturning 注解声明
* After throwing
当匹配的方法执行并抛出异常退出时,运行。
由 @AfterThorwing 注解声明。
* After (finally)
当匹配的方法执行完成并退出后执行。
通过 @After 声明
try{
try{
//@Before
method.invoke(..);
}finally{
//@After
}
//@AfterReturning
}catch(){
//@AfterThrowing
}
* Around
它可以在一个方法的之前之前和之后添加不同的操作, 并且甚至可以决定何时, 如何, 是否调用匹配到的方法.
通过 @Around 声明
<a name = "代码地址">
代码地址
https://github.com/Kcyfrank/SpringMVC-example
<a name = "参考链接">