使用 @AspectJ 注解配置 Spring AOP
Spring 实现 AOP 功能的方式有两种:
@AspectJ 注解方式。 常用
XML 配置方式
题外话,AOP 概念并非 Spring 所特有,Spring 也并非支持 AOP 编程的唯一框架。在 Spring 之前提供 AOP 功能的,具有里程碑式的框架叫『AspectJ 框架』。
AspectJ 框架的使用方式比较独特,使用起来不太简便,很麻烦。在 Spring AOP 出现后就慢慢被 Spring AOP 所取代。但是,AspectJ 框架设计了一套注解,非常简便和合理,并且被广大 AspectJ 的使用者所熟知,所以 Spring AOP 直接借用这套注解,也就是我们这里所说的 @AspectJ 注解。
由于 @AspectJ 注解是 Spring『借用』的别人的注解,所以使用时需要引入它。
<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>
在 Spring 的配置文件中,也需要引入/声明 AOP 的 namspace :
<beans
...
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
...
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
并且,由于 @AspectJ 注解并非 Spring 框架的一部分,所以需要在配置文件中声明 “启用 @AspectJ 注解” 功能,否则,Spring 并 “不认识” @AspectJ 的一系列注解。
<aop:aspectj-autoproxy />
<!-- 或者使用『@Component 注解 & 包扫描』创建单例对象 -->
<bean id="dept" class="bean.Dept" />
<bean id="aspect1" class="bean.DeptAspect1" />
再重复一遍,使用 Spring AOP 的核心问题:在『什么类』的『什么方法』的『什么地方』,做出『什么样』的增强。
@Aspect // 注意,不要忘记在切面类的头上加 @Aspect 注解。
public class DeptAspect1 {
@Before("execution(* bean.Dept.sayHi(..))")
public void before() {
System.out.println("before ...");
}
@After("execution(* bean.Dept.sayHi(..))")
public void after() {
System.out.println("after ...");
}
@Around("execution(* bean.Dept.sayHi(..))")
public void around(ProceedingJoinPoint jp) {
System.out.println("hello");
try {
jp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("world");
}
@AfterReturning("execution(* bean.Dept.sayHi(..))")
public void afterReturuing() {
System.out.println("afterReturning ...");
}
@AfterThrowing("execution(* bean.Dept.sayHi(..))")
public void afterThrowing() {
System.out.println("afterThrowing ...");
}
}
可以发现,上述代码中 execution(…) 部分有大量重复现象。为此,可以提供一个 @Pointcut 来进行「缩写」。
@Aspect
public class DeptAspect1 {
@Pointcut("execution(* bean.Dept.sayHi(..))")
public void xxx() { // 这个方法是空的。需要的不是它的内容,需要的是它的名字。
}
@Before("xxx()")
public void before() { ... }
@After("xxx()")
public void after() { ... }
@Around("xxx()")
public void around() { ... }
@AfterReturning("xxx())")
public void afterReturning() { ... }
@AfterThrowing("xxx())")
public void afterThrowing() { ... }
}
另外,有时你要拦截/增强的方法是有参数的,例如:
public void sayHi(String name, int age) { ... }
为此,你也可以在增强方法中获得这些参数,
@Pointcut("execution(* bean.Dept.sayHi(..))")
public void xxx() {}
@Before("execution(* bean.Dept.sayHi(..)) && args(name, age)")
public void before(String name, int age) {
...
}
@After("xxx() && args(name, age)")
public void after(String name, int age) {
...
}