Spring注解驱动开发——AOP常用注解

一、用于开启注解AOP支持的

@EnableAspectJAutoProxy

(一) 作用

表示开启spring对注解aop的支持。它有两个属性,分别是指定采用的代理方式和 是否暴露代理对象,通过AopContext可以进行访问。
在这里插入图片描述

(三) 属性

proxyTargetClass: 指定是否采用cglib进行代理。默认值是false,表示使用jdk的代理。
exposeProxy: 指定是否暴露代理对象,通过AopContext可以进行访问。

(四) 细节

(1) proxyTargetClass
当使用JDK的代理时,被代理类至少实现一个接口。
当使用cglib接口时,被代理类不不能是最终类,即不能用final修饰
(2)exposeProxy
在这里插入图片描述
在这里插入图片描述

二、用于配置切面

@Aspect

(一) 作用

声明当前类是一个切面类。

(二) 属性

value: 默认我们的切面类应该为单例的。但是当切面类为一个多例类时,指定预 处理的切入点表达式。用法是perthis(切入点表达式)。 它支持指定切入点表达式,或者是用@Pointcut修饰的方法名称(要求全 限定方法名)
在这里插入图片描述

(三) 使用场景

此注解也是一个注解驱动开发aop的必备注解。

(四) 多个切面的执行顺序

默认情况下,多个切面相同类型通知的执行顺序与类名首字母有关,首字母顺序靠前执行顺序靠前
在这里插入图片描述
自定义执行顺序@Order
在这里插入图片描述

三、用于配置切入点表达式

@Pointcut

(一) 作用

此注解是用于指定切入点表达式的。

(二) 属性

value: 用于指定切入点表达式。
argNames:用于指定切入点表达式的参数。参数可以是execution中的,也可以是 args中的。通常情况下不使用此属性也可以获得切入点方法参数。

配置通知时,需要获取切入点参数,与args有关
在这里插入图片描述

(三) 使用场景

在实际开发中,当我们的多个通知需要执行,同时增强的规则确定的情况下,就可 以把切入点表达式通用化。此注解就是代替xml中的< aop:pointcut >标签,实现切入点表达 式的通用化。

(四) 将切入点表达式抽取到通知类中

在这里插入图片描述
在这里插入图片描述
如果切面类是多例的
在这里插入图片描述

四、用于配置通知

@Before

(1) 作用

被此注解修饰的方法为前置通知。前置通知的执行时间点是在切入点方法执行之 前。

(2) 属性

value: 用于指定切入点表达式。可以是表达式,也可以是表达式的引用。
argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称 一致。通常不指定也可以获取切入点方法的参数内容。

(3) 使用场景

在实际开发中,我们需要对切入点方法执行之前进行增强,例如对请求的数据的编码进行处理, 此时就用到了前置通 知。在通知(增强的方法)中需要获取切入点方法中的参数进行处理时,就要配合切入点表达 式参数来使用。以下面的案例为例,案例中我们希望获得记录日志的id,注意此案例的参数个数及写法
在这里插入图片描述

@After

(一) 作用

用于指定最终通知。

(二) 属性

value: 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称 一致。通常不指定也可以获取切入点方法的参数内容。

(三) 使用场景

最终通知的执行时机,是在切入点方法执行完成之后执行,无论切入点方法执行是 否产生异常最终通知都会执行。所以被此注解修饰的方法,通常都是做一些清理操作。

@AfterReturning

(一) 作用

用于配置后置通知。后置通知的执行是在切入点方法正常执行之后、方法返回之前执行。
注意
小编使用的是Spring5.3的版本,不知道是不是版本的缘故,小编写的通知的执行顺序和许多资料上不太相同,这个问题还有待研究,在这里记录一下结果
在这里插入图片描述
在这里插入图片描述

(二) 属性

value: 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
pointcut:它的作用和value是一样的。
returning:指定切入点方法返回值的变量名称。它必须和切入点方法返回值名称一 致。
argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称 一致。通常不指定也可以获取切入点方法的参数内容。

(三) 使用场景

此注解是用于配置后置增强切入点方法的。被此注解修饰方法会在切入点方法正常 执行之后执行。在我们实际开发中,像提交事务,记录访问日志,统计方法执行效率等等都可 以利用后置通知实现。
在这里插入图片描述

@AfterThrowing

作用: 用于配置异常通知。
属性:
value: 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
pointcut:它的作用和value是一样的。
throwing:指定切入点方法执行产生异常时的异常对象变量名称。它必须和异常变量 名称一致。
argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称 一致。通常不指定也可以获取切入点方法的参数内容。
使用场景:用此注解修饰的方法执行时机是在切入点方法执行产生异常之后执行。
在这里插入图片描述

一个切面内相同类型通知的执行顺序

在这里插入图片描述
结论:
(1) 一个切面内相同类型通知,按照通知的方法名字母数字ASCII码值小的优先执行,与@Order无关。如果遇到方法的重载,则继续向后比较参数列表的ASCII值的大小,规则与之前相同
(2) 不同切面之间的相同类型通知可以通过@Order调整的

@Around

(一) 作用

用于指定环绕通知。环绕通知有别于前面介绍的四种通知类型。它不是指定增强方法执行时机的,而是 spring为我们提供的一种可以通过编码的方式手动控制增强方法何时执行的机制。

(二) 属性

value: 用于指定切入点表达式,可以是表达式,也可以是表达式的引用。
argNames:用于指定切入点表达式参数的名称。它要求和切入点表达式中的参数名称 一致。通常不指定也可以获取切入点方法的参数内容。

(三) 案例

日志功能

@Component
@Aspect//表面当前类是一个切面类
public class LogUtil {

    /**
     * 需求:记录当前执行的是什么方法以及执行此方法时的相关信息
     * @return
     */
    @Around("execution(* cn.spring.service.impl.*.*(..))")
    public Object aroundPrintLog(ProceedingJoinPoint pjp) {
        Object rtValue = null;
        try {
            //创建系统日志对象
            SystemLog log = new SystemLog();
            //设置主键
            String id = UUID.randomUUID().toString().replace("_", "").toUpperCase();
            log.setId(id);
            //设置来访者IP
            log.setRemoteIP("127.0.0.1");
            //设置执行时间
            log.setTime(new Date());
            //设置当前执行的方法名称
            //1.使用ProceedingJoinPoint接口中的获取签名方法
            Signature signature = pjp.getSignature();
            //2.判断当前签名是否是方法签名
            if(signature instanceof MethodSignature) {
                //3.把签名转成方法签名
                MethodSignature methodSignature = (MethodSignature) signature;
                //4.获取当前执行的方法
                Method method = methodSignature.getMethod();
                String methodName = method.getName();
                log.setMethod(methodName);

                //设置当前方法的说明
                //7.判断当前方法上是否有@Description注解
                boolean isAnnotated = method.isAnnotationPresent(Deprecated.class);
                if (isAnnotated) {
                    //8.得到当前方法上的Deprecated注解
                    Description description = method.getAnnotation(Description.class);
                    //9.得到注解的value属性
                    String value = description.value();
                    //10.给系统日志方法说明属性赋值
                    log.setAction(value);
                }
            }

            System.out.println("环绕通知执行了记录日志" + log);

            //获取切入点表达式的参数
            Object[] args = pjp.getArgs();
            //切入点方法执行
            rtValue = pjp.proceed(args);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return rtValue;
    }

}

五、用于扩展目标类

@DeclareParents

(一) 使用场景

当我们已经完成了一个项目的某个阶段开发,此时需要对已完成的某个类加入一些 新的方法,我们首先想到的是写一个接口,然后让这些需要方法的类实现此接口,但是如果目 标类非常复杂,牵一发而动全身,改动的话可能非常麻烦。此时就可以使用此注解,然后建一 个代理类,同时代理该类和目标类。

(二) 作用

用于给被增强的类提供新的方法。(实现新的接口),换句话说,之前的案例中实现代理对象只代理了一个接口,现在可以代理多个接口。

(三) 属性

value: 用于指定目标类型的表达式。当在全限定类名后面跟上+时,表示当前类 及其子类
defaultImpl: 指定提供方法或者字段的默认实现类。
在这里插入图片描述
在这里插入图片描述

(四) 两种触发方式

第一种触发方式:在使用时自行强转新引入接口类型,然后调用方法。
在这里插入图片描述
第二种触发方式:在通知类中,使用this关键字,引入新目标类对象,调用方法 触发。
在这里插入图片描述

@EnableLoadTimeWeaving

(一) Weaving(织入)

指把增强应用到目标对象来创建新的代理对象的过程。 spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

(二) 作用

用于切换不同场景下实现增强。

(三) 使用场景

在Java 语言中,从织入切面的方式上来看,存在三种织入方式:编译期织入、类 加载期织入和运行期织入。编译期织入是指在Java编译期,采用特殊的编译器,将切面织入 到Java类中;而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切 面;运行期织入则是采用CGLib工具或JDK动态代理进行切面的织入。 AspectJ提供了两种切面织入方式,第一种通过特殊编译器,在编译期,将AspectJ 语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;第二种 方式是类加载期织入,也简称为LTW(Load Time Weaving)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它提供了面向切面编程(AOP)的支持。AOP是一种编程思想,它可以在不改变原有代码的情况下,通过在程序运行时动态地将代码“织入”到现有代码中,从而实现对原有代码的增强。 Spring AOP提供了基于注解AOP实现,使得开发者可以通过注解的方式来定义切面、切点和通知等相关内容,从而简化了AOP的使用。 下面是一个基于注解AOP实现的例子: 1. 定义切面类 ```java @Aspect @Component public class LogAspect { @Pointcut("@annotation(Log)") public void logPointcut() {} @Before("logPointcut()") public void beforeLog(JoinPoint joinPoint) { // 前置通知 System.out.println("执行方法:" + joinPoint.getSignature().getName()); } @AfterReturning("logPointcut()") public void afterLog(JoinPoint joinPoint) { // 后置通知 System.out.println("方法执行完成:" + joinPoint.getSignature().getName()); } @AfterThrowing(pointcut = "logPointcut()", throwing = "ex") public void afterThrowingLog(JoinPoint joinPoint, Exception ex) { // 异常通知 System.out.println("方法执行异常:" + joinPoint.getSignature().getName() + ",异常信息:" + ex.getMessage()); } } ``` 2. 定义业务逻辑类 ```java @Service public class UserService { @Log public void addUser(User user) { // 添加用户 System.out.println("添加用户:" + user.getName()); } @Log public void deleteUser(String userId) { // 删除用户 System.out.println("删除用户:" + userId); throw new RuntimeException("删除用户异常"); } } ``` 3. 在配置文件中开启AOP ```xml <aop:aspectj-autoproxy/> <context:component-scan base-package="com.example"/> ``` 在这个例子中,我们定义了一个切面类LogAspect,其中通过@Aspect注解定义了一个切面,通过@Pointcut注解定义了一个切点,通过@Before、@AfterReturning和@AfterThrowing注解分别定义了前置通知、后置通知和异常通知。 在业务逻辑类中,我们通过@Log注解标注了需要增强的方法。 最后,在配置文件中,我们通过<aop:aspectj-autoproxy/>开启了AOP功能,并通过<context:component-scan>扫描了指定包下的所有组件。 这样,当我们调用UserService中的方法时,就会触发LogAspect中定义的通知,从而实现对原有代码的增强。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值