AspectJ

4.6.2 在 Spring 中启用 AspectJ 注解支持

• 要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库.

• 将 Schema 下的 aop 添加到 <beans> 根元素中.

• 要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素 <aop:aspectj-autoproxy>, 称之为AOP的自动代理

• 当 Spring IOC 容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy> 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.

4.6.3 用 AspectJ 注解声明切面

• 要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.

• 在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.

• 通知是标注有某种注解的简单的 Java 方法.

• AspectJ 支持 5 种类型的通知注解:

-- @Before: 用于配置前置通知。指定增强的方法在切入点方法(⽬标⽅法)之前执⾏.

-- @After: 用于配置后置通知。指定增强的方法在切入点方法(⽬标⽅法)之后执⾏, ⽆论是否有异常都会执⾏.

-- @AfterReturning: 用于配置返回后通知。指定增强的方法在切入点方法(⽬标⽅法)之后执⾏, 有异常不会执⾏.

-- @AfterThrowing:用于配置异常抛出通知。指定增强的方法在出现异常时执行.

-- @Around: 用于配置环绕通知。指定增强的方法在切入点方法(⽬标⽅法)之前和之后都执行.

通知的配置语法:@通知注解("切点表达式")

4.6.4 切入点表达式说明

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))

• 访问修饰符可以省略(修饰符加了中括号, 代表可以省略)

• 返回值类型、包名、类名、方法名可以使用星号 * 代表任意

• 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类

• 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表(如果参数有一个就写一个, 有两个就写两个, 不过可以使用 .. 表示任意个数)

例如:

全匹配方式:

execution(public void com.sy.aop.Target.method(参数))

访问修饰符可以省略:

execution( void com.sy.aop.Target.method(com.sy.pojo.User))

返回值可以使用*号,表示任意返回值:

execution( * com.sy.aop.Target.method(com.sy.pojo.User))

包名可以使用*号,表示任意包,但是有几级包,需要写几个*

execution( * *.*.*.Target.method(com.sy.pojo.User))

使用..来表示当前包,及其子包

execution( * com..Target.method(com.sy.pojo.User))

类名可以使用*号,表示任意类

execution( * com..*.method(com.sy.pojo.User))

方法名可以使用*号,表示任意方法

execution( * com..*.*(com.sy.pojo.User))

参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数

execution( * com..*.*(*))

参数列表可以使用..表示有无参数均可,有参数可以是任意类型

execution( * com..*.*(..))

全通配方式:

execution(* *..*.*(..))

注意:

通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。

execution(* com.sy.service.impl.*.*(..))

五、Spring基于注解的AOP配置

基于注解的aop开发步骤:

  1. 创建目标接口和目标类(内部有切点)

  2. 创建切面类(内部有增强方法)

  3. 将目标类和切面类的对象创建权交给 spring

  4. 在切面类中使用注解配置织入关系

  5. 在配置文件中开启组件扫描和 AOP 的自动代理

  6. 测试程序

构建Maven工程并添加依赖

XML 复制代码
<properties>
      <maven.compiler.source>17</maven.compiler.source>
      <maven.compiler.target>17</maven.compiler.target>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

  <spring.version>5.2.5.RELEASE</spring.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>

  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
  </dependency>

</dependencies>

​

创建 Spring 的配置文件并导入约束

XML 复制代码
<?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:context="http://www.springframework.org/schema/context"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启注解扫描器-->
    <context:component-scan base-package="com.study"></context:component-scan>

    <!--配置基于注解的AspactJ(AOP的自动代理)-->
    <aop:aspectj-autoproxy> </aop:aspectj-autoproxy>
</beans>

前置后置 环绕 异常

java 复制代码
// 切面类
@Component //创建当前对象并存入IOC容器
@Aspect //标注当前MyAspect类是一个切面类
@Order(3)
public class MyAspect {

    /**
     * 抽取切点表达式(重用切入点表达式): 根据方法名复用即可
     * 声明切入点
     */
    @Pointcut("execution(* com.sy.service.*.*(..))")
    public void myPointcut(){

    }

    /**
     * 在add方法执行前,执行beforeMethod方法
     * 目标方法前执行的通知,前置通知
     * JoinPoint: 连接的对象,该对象包含了与目标方法相关的一些信息
     * execution: 配置切入点表达式,指定切入点
     */
    // @Before("execution(public int com.sy.service.impl.CalculatorServiceImpl.add(int,int))")
    @Before("myPointcut()")
    public void beforeMethod(JoinPoint joinPoint){
        //获取目标方法名
        String method = joinPoint.getSignature().getName();
        //获取目标方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("MyAspect ==> 这是一个beforeMethod方法, 在 " + method + " 方法前执行了, 参数有:"+ Arrays.asList(args));
    }

    /**
     * 目标方法后执行的通知,后置通知
     *  1.目标方法不管有没有执行异常,后置通知都会执行
     *  2.后置通知获取不到目标方法的返回值
     */
    // @After("execution(* com.sy.service.impl.CalculatorServiceImpl.*(..))")
    @After("myPointcut()")
    public void afterMethod(JoinPoint joinPoint){
        //获取目标方法名
        String method = joinPoint.getSignature().getName();
        //获取目标方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("MyAspect ==> 这是一个afterMethod方法, 在 " + method + " 方法后执行了, 参数有:"+ Arrays.asList(args));
    }

    /**
     * 目标方法正常执行结束后;返回通知
     * returning:用于指定接收目标方法的返回值,必须与通知方法的形参名一致
     */
    // @AfterReturning(value = "execution(* com.sy.service.*.*(..))",returning = "result")
    @AfterReturning(value = "myPointcut()", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        //获取方法名
        String method = joinPoint.getSignature().getName();
        //获取方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("MyAspect ==> 这是一个afterReturningMethod方法, 在 " + method + " 方法返回结果后执行了, 参数有:"+ Arrays.asList(args)+ ", 结果是:" + result);
    }

    /**
     * 目标方法抛出异常后执行:异常通知
     * throwing:用于指定接收目标方法的异常信息,必须与通知方法的形参名一致
     */
    // @AfterThrowing(value = "execution(* com.sy.service.*.*(..))", throwing = "e")
    @AfterThrowing(value = "myPointcut()", throwing = "e")
    public void afterThrowingMethod(JoinPoint joinPoint, Exception e){
        //获取方法名
        String method=joinPoint.getSignature().getName();
        //获取方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("MyAspect ==> 这是一个afterThrowingMethod方法, 在" + method + "方法执行异常后执行了, 参数有:" + Arrays.asList(args) + ", 异常是:" + e);
    }

    /**
     * 环绕着整个目标方法执行:环绕通知
     * 环绕通知综合了前置 后置 返回 异常 四个通知的功能
     */
    // @Around("execution(* com.sy.service.impl.CalculatorServiceImpl.*(..))")
    @Around("myPointcut()")
    public Object aroundMethod(ProceedingJoinPoint point){
        try {
            // 1.目标方法前执行的通知:前置通知
            String method = point.getSignature().getName();
            Object[] args = point.getArgs();
            System.out.println("前置通知 ==> 目标方法为:" + method + " -- 参数有: " + Arrays.asList(args));
            //point.proceed():执行目标方法
            Object result = point.proceed();
            // 3.返回通知
            System.out.println("返回通知 ==> 目标方法为:" + method + " -- 参数有: " + Arrays.asList(args) + " -- 结果为: " + result);
            return result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            // 4.异常通知
            System.out.println("异常通知 ==> 目标方法为:" + point.getSignature().getName() + ", 异常是: " + throwable);
        }finally {
            // 2.目标方法后执行的通知:后置通知
            String method = point.getSignature().getName();
            Object[] args = point.getArgs();
            System.out.println("后置通知 ==> 目标方法为: " + method + " -- 参数有: " + Arrays.asList(args));
        }
        return null;
    }

}
相关推荐
杨凯凡2 小时前
【014】基本类型与包装类:缓存、相等性、NPE
java·数据结构·缓存
亚空间仓鼠2 小时前
NoSQL数据库Redis(一):数据库基础
数据库·redis·nosql
网域小星球2 小时前
C++ 从 0 入门(四)|继承、多态、this 指针、深浅拷贝(C++ 面试终极收官)
开发语言·c++·面试·多态·继承·this指针·深浅拷贝
weixin_580614002 小时前
如何防止SQL注入利用存储过程_确保存储过程不拼字符串.txt
jvm·数据库·python
james的分享2 小时前
向量数据库之Milvus
数据库·milvus
emmjng3692 小时前
使用飞算JavaAI实现在线图书借阅平台
java
CoderYanger2 小时前
14届蓝桥杯省赛Java A 组Q1~Q3
java·开发语言·线性代数·算法·职场和发展·蓝桥杯
钮钴禄·爱因斯晨2 小时前
他到底喜欢我吗?赛博塔罗Java+前端实现,一键解答!
java·开发语言·前端·javascript·css·html
词元Max2 小时前
Java 转 AI Agent 开发学习路线(2026年3月最新版)
java·人工智能·学习