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;
    }

}
相关推荐
YikNjy5 分钟前
break和continue
java·开发语言·算法
SomeOtherTime6 分钟前
Geojson相关(AI回答)
java·前端·python
日月云棠18 分钟前
10 Integer —— 最常用的整数包装类深度解析
java·后端
秋922 分钟前
java项目中cpu飙升排查及解决方法
java·开发语言
Elastic 中国社区官方博客23 分钟前
我们如何在 Elasticsearch Serverless 上将向量搜索吞吐量提升一倍
大数据·数据库·人工智能·elasticsearch·搜索引擎·云原生·serverless
野生技术架构师23 分钟前
牛客网2026最新大厂Java高频面试题精选(附标准答案)
java·开发语言
PH = 727 分钟前
JAVA的SPI机制
java·开发语言
一 乐27 分钟前
高校实习信息发布网站|基于Spring Boot的高校实习信息发布网站的设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·高校实习信息发布网站
weelinking29 分钟前
【产品】11_实现后端接口——数据在背后如何流动
java·人工智能·python·sql·oracle·json·ai编程
摇滚侠37 分钟前
东方通替换tomcat,实战经验
java