和 Spring框架:利用 XML文档配置 Aspect 织入 中的案例一样,本案例将使用 Target 类和 MyAspect 类演示基于 Java 注解的 Aspect 织入方法。其中,Target 类中的方法应当定义核心业务代码;MyAspect 类中定义 AOP 通知方法。本案例期望引导 Spring 框架实现基于 JDK 的代理,因此还要为 Target 类定义一个接口并实现。最后定义一个测试类作为主方法。
定义 TargetInterface.java 接口:
public interface TargetInterface {
void save();
}
定义 Target.java 实现类
@Component("target") // Component注解替代bean标签
public class Target implements TargetInterface {
public void save() {
System.out.println("Target: save running...");
}
}
定义包含各种通知类型方法的 MyAdvice 类,配置 IOC 注解
@Component("myAspect")
public class MyAdvice {
public void before() {
System.out.println("MyAspect: 前置增强.....");
}
public void afterReturning() {
System.out.println("MyAspect: 后置增强...");
}
/**
* 环绕增强
* @param pjp 正在执行的连接点,也就是切点
*/
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("MyAspect: 环绕前增强...");
// 切点方法
Object proceed = pjp.proceed();
System.out.println("MyAspect: 环绕后增强...");
return proceed;
}
public void afterThrowing() {
System.out.println("MyAspect: 异常抛出增强...");
}
public void after() {
System.out.println("MyAspect: 最终增强...");
}
}
定义测试类 AnnoAOP:
public class AnnoAOP {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
TargetInterface target = app.getBean(TargetInterface.class);
target.save();
}
}
执行结果:
Target: save running...
使用 XML 文档辅助注解配置。配置注解支持需要在 XML 文档中作三件事:
<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"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!--配置AOP组件扫描-->
<context:component-scan base-package="com.abe.aop.annotation"/>
<!--配置AOP自动代理-->
<aop:aspectj-autoproxy/>
</beans>
注解配置 Aspect 的通用方式如下:
@通知注解("execution(切点表达式)")
修饰符 返回值类型 方法名(输入参数) {
// 方法体
}
在标注通知类型之前,首先要利用 @Aspect
注解声明该类是一个通知类:
@Component("myAspect")
@Aspect // 告知Spring容器,MyAspect是一个切面类
public class MyAdvice {
// 成员&方法定义
}
以下演示了五种不同通知类型的注解配置方法。
@Before("execution(* com.abe.aop.annotation.TargetInterface.*(..))")
public void before() {
System.out.println("MyAspect: 前置增强.....");
}
替换 XML 配置:
<aop:before method="before" pointcut="execution(* TargetInterface.*(..))"/>
@AfterReturning("execution(* com.abe.aop.annotation.TargetInterface.*(..))")
public void afterReturning() {
System.out.println("MyAspect: 后置增强...");
}
替换 XML 配置:
<aop:after-returning method="afterReturning" pointcut="execution(* TargetInterface.*(..))"/>
/**
* 环绕增强
* @param pjp 正在执行的连接点,也就是切点
*/
@Around("execution(* TargetInterface.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("MyAspect: 环绕前增强...");
// 切点方法
Object proceed = pjp.proceed();
System.out.println("MyAspect: 环绕后增强...");
return proceed;
}
替换 XML 配置:
<aop:around method="around" pointcut="execution(* TargetInterface.*(..))"/>
@AfterThrowing("execution(* TargetInterface.*(..))")
public void afterThrowing() {
System.out.println("MyAspect: 异常抛出增强...");
}
替换 XML 配置:
<aop:after-throwing method="afterThrowing" pointcut="execution(* TargetInterface.*(..))"/>
@After("execution(* TargetInterface.*(..))")
public void after() {
System.out.println("MyAspect: 最终增强...");
}
替换 XML 配置:
<aop:after method="after" pointcut="execution(* TargetInterface.*(..))"/>
同样地,使用注解配置时依然支持抽取通用的切点表达式,此时需要定义的空方法,这个方法无需执行任何功能,只是提供一个定义切点表达式的注解的位置。其它方法使用该表达式时,只需将原来定义表达式的位置替换为定义表达式的方法名即可。
// 定义和抽取切点表达式
@Pointcut("execution(* com.abe.aop.annotation.TargetInterface.*(..))")
public void pointcut() {} // 空方法,协助注解配置
@Before("pointcut()")
publicvoid before() {
System.out.println("MyAspect: 前置增强.....");
}
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("MyAspect: 后置增强...");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 方法体
}
@AfterThrowing("pointcut()")
public void afterThrowing() {
System.out.println("MyAspect: 异常抛出增强...");
}
@After("pointcut()")
public void after() {
System.out.println("MyAspect: 最终增强...");
}
类似 IOC 配置,配置 Aspect 也可以使用 Spring 新注解完全替换 XML 配置文档。同样地,这需要顶一个专门的配置类,配置类中无需定义任何逻辑,只是提供定义注解的位置。利用 @Configuration
表明该类是一个注解配置类,利用 @ComponentScan
定义 AOP 组件扫描路径,最后,还需要调用 AOP 配置的专门注解 @EnableAspectJAutoProxy
,声明允许 AspectJ 执行自动代理。
@Configuration
@ComponentScan("com.abe.aop.annotation")
@EnableAspectJAutoProxy // 允许 AspectJ 执行自动代理
public class SpringConfiguration {
}
通过注解配置类可以完全替换 XML 配置文档。
MyAdvice.java
@Component("myAspect")
@Aspect // 告知Spring容器,MyAspect是一个切面类
public class MyAdvice {
// 定义和抽取切点表达式
@Pointcut("execution(* com.abe.aop.annotation.TargetInterface.*(..))")
public void pointcut() {} // 空方法,协助注解配置
@Before("pointcut()")
public void before() {
System.out.println("MyAspect: 前置增强.....");
}
@Before("MyAdvice.pointcut()") // 等价抽取写法,方便其它类引用
public void before2() {
System.out.println("MyAspect: 前置增强2.....");
}
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("MyAspect: 后置增强...");
}
/**
* 环绕增强
* @param pjp 正在执行的连接点,也就是切点
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("MyAspect: 环绕前增强...");
// 切点方法
Object proceed = pjp.proceed();
System.out.println("MyAspect: 环绕后增强...");
return proceed;
}
@AfterThrowing("pointcut()")
public void afterThrowing() {
System.out.println("MyAspect: 异常抛出增强...");
}
@After("pointcut()")
public void after() {
System.out.println("MyAspect: 最终增强...");
}
}
AOP 配置类 SpringConfiguration.java
@Configuration
@ComponentScan("com.abe.aop.annotation")
@EnableAspectJAutoProxy // 允许 AspectJ 执行自动代理
public class SpringConfiguration {
}
执行结果:
MyAspect: 前置增强.....
MyAspect: 环绕前增强...
Target: save running...
MyAspect: 最终增强...
MyAspect: 环绕后增强...
MyAspect: 后置增强...