当前位置: 首页 > 工具软件 > Aspect C++ > 使用案例 >

Java - Spring框架:利用 Java 注解配置 Aspect 织入

拓拔嘉运
2023-12-01

1 Java 示例代码

1.1 案例介绍

Spring框架:利用 XML文档配置 Aspect 织入 中的案例一样,本案例将使用 Target 类和 MyAspect 类演示基于 Java 注解的 Aspect 织入方法。其中,Target 类中的方法应当定义核心业务代码;MyAspect 类中定义 AOP 通知方法。本案例期望引导 Spring 框架实现基于 JDK 的代理,因此还要为 Target 类定义一个接口并实现。最后定义一个测试类作为主方法。

1.2 利用 Java 注解配置 IOC 依赖

定义 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...

2 使用 XML 文档辅助配置织入关系

2.1 在 XML 文档中配置 AOP 注解支持

使用 XML 文档辅助注解配置。配置注解支持需要在 XML 文档中作三件事:

  1. 导入 AOP 约束
  2. 配置 AOP 组件扫描
  3. 配置 AOP 自动代理
<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>

2.2 声明通知类:@Aspect

注解配置 Aspect 的通用方式如下:

@通知注解("execution(切点表达式)")
修饰符 返回值类型 方法名(输入参数) { 
	// 方法体
}

在标注通知类型之前,首先要利用 @Aspect 注解声明该类是一个通知类:

@Component("myAspect")
@Aspect     // 告知Spring容器,MyAspect是一个切面类
public class MyAdvice {
	// 成员&方法定义
}

以下演示了五种不同通知类型的注解配置方法。

2.2.1 前置通知:@Before

@Before("execution(* com.abe.aop.annotation.TargetInterface.*(..))")
public void before() {
    System.out.println("MyAspect: 前置增强.....");
}

替换 XML 配置:

<aop:before method="before" pointcut="execution(* TargetInterface.*(..))"/>

2.2.2 后置通知:@AfterRunning

@AfterReturning("execution(* com.abe.aop.annotation.TargetInterface.*(..))")
public void afterReturning() {
    System.out.println("MyAspect: 后置增强...");
}

替换 XML 配置:

<aop:after-returning method="afterReturning" pointcut="execution(* TargetInterface.*(..))"/>

2.2.3 环绕通知:@Around

/**
 * 环绕增强
 * @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.*(..))"/>

2.2.4 异常通知:@AfterThrowing

@AfterThrowing("execution(* TargetInterface.*(..))")
public void afterThrowing() {
    System.out.println("MyAspect: 异常抛出增强...");
}

替换 XML 配置:

<aop:after-throwing method="afterThrowing" pointcut="execution(* TargetInterface.*(..))"/>

2.2.5 最终通知:@After

@After("execution(* TargetInterface.*(..))")
public void after() {
    System.out.println("MyAspect: 最终增强...");
}

替换 XML 配置:

<aop:after method="after" pointcut="execution(* TargetInterface.*(..))"/>

2.3.6 抽取切点表达式:@Pointcut

同样地,使用注解配置时依然支持抽取通用的切点表达式,此时需要定义的空方法,这个方法无需执行任何功能,只是提供一个定义切点表达式的注解的位置。其它方法使用该表达式时,只需将原来定义表达式的位置替换为定义表达式的方法名即可。

// 定义和抽取切点表达式
@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: 最终增强...");
}

3 使用新注解完全替代 XML 文档

3.1 声明自动代理:@EnableAspectJAutoProxy

类似 IOC 配置,配置 Aspect 也可以使用 Spring 新注解完全替换 XML 配置文档。同样地,这需要顶一个专门的配置类,配置类中无需定义任何逻辑,只是提供定义注解的位置。利用 @Configuration 表明该类是一个注解配置类,利用 @ComponentScan 定义 AOP 组件扫描路径,最后,还需要调用 AOP 配置的专门注解 @EnableAspectJAutoProxy,声明允许 AspectJ 执行自动代理。

@Configuration
@ComponentScan("com.abe.aop.annotation")
@EnableAspectJAutoProxy     // 允许 AspectJ 执行自动代理
public class SpringConfiguration {
}

通过注解配置类可以完全替换 XML 配置文档。

4 完整配置示例

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: 后置增强...
 类似资料: