SpringBoot的AOP实现各类通知,以及利用AOP实现自定义注解的范例

第一部分:SpringBoot实现AOP

1、准备工作:

在springboot项目的基础上,额外引入pom包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、编写通知类

前置通知、返回通知、异常通知、后置通知、环绕通知如下所示:

package com.fh.service.impl.aop;

import com.fh.controller.requestParam.PlayerParam;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @BelongsProject: SpringBootRedisDemo1
 * @Description: TODO
 * @Version: 1.0
 */
@Component//把当前类注入spring容器中
@Aspect//指出当前类为切面类
public class PlayerServiceAop {

    @Pointcut("execution(* com.fh.service.impl.PlayerServiceImpl.insertPlayer(..))")//把下面方法testMethod与切入点"com.fh.service.impl.PlayerServiceImpl.insertPlayer(..)"这个方法关联
    public void testMethod(){}

    @Before(value = "testMethod()")//前置通知 关联的切入点方法为上面的testMethod方法
    public void beforeMethod(JoinPoint jp){//前置通知获取切入点方法的入参参数的类:JoinPoint。该类对于环绕通知以外的通知均适用。
        Object[] os = jp.getArgs();//获取所有入参参数,对应"insertPlayer(..)"括号内参数列表
        for (Object o : os) {//遍历所有入参,对入参转换为原本切入点方法的入参"insertPlayer(..)",修改入参信息
            PlayerParam pp = (PlayerParam) o;
            pp.setName("川普");
        }
        System.out.println("before");
    }
    
    @AfterReturning("testMethod()")//返回通知
    public void afterReturnMethod(){
        System.out.println("after return");
    }

    @AfterThrowing("testMethod()")//异常通知
    public void afterThrowMethod(){
        System.out.println("after throw");
    }

    @After("testMethod()")//后置通知
    public void afterMethod(){
        System.out.println("after");
    }

    @Around("testMethod()")//环绕通知
    public Integer aroundMethod(ProceedingJoinPoint pjp) {//环绕通知获取切入点方法的入参的特有类:ProceedingJoinPoint。返回参数Integer,对应着"insertPlayer(..)"的返回参数的类型。
        try {
            for (Object o : pjp.getArgs()) {
                PlayerParam pp = (PlayerParam) o;
                pp.setName("拜登");
            }
            System.out.println("around before");//环绕通知的before早于前置通知
            Integer result = (Integer) pjp.proceed(pjp.getArgs());
            System.out.println("around after");//环绕通知的after早于前置通知
            return  result + 100;//这里可以直接对返回结果进行修改,额外+100。
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return -100;
    }
}

3、通知的执行顺序

通知的执行顺序是固定不变的,如下:

try {
    try {
        // @Before
        mothod.invoke();
     } finally {
        // @After
     }
     // @AfterReturning
catch() {
    // @AfterThrowing
}

所以,注解执行事务时,@Before刚刚开启事务,接着 @After就关闭了事务,直接导致@AfterReturning和@AfterThrowing的代码无法运行,因此注解方法无法执行事务,需引入环绕通知来解决事务。

第二部分:AOP实现自定义注解的范例

1、编写自定义注解

//以下四个为自定义注解使用的元注解
@Target({ElementType.FIELD,ElementType.METHOD})//描述注解的使用范围:字段、方法
@Retention(RetentionPolicy.RUNTIME)//描述注解保留的时间范围:运行时
@Documented//描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息
@Inherited//使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)
public @interface UserInfo {//"@interface"为自定义注解的标志
    String name() default "default";
    String age() default "0";
}

2、利用AOP找到被相应注解修饰的方法,对该方法上的注解参数进行解析,并对其中的注解参数进行使用

/**
 * @BelongsProject: SpringBootRedisDemo1
 * @Description: TODO
 * @Version: 1.0
 */
@Component//把当前类注入spring容器中
@Aspect//指出当前类为切面类
public class PlayerServiceAop {

    @Around("execution(* com.fh.service.impl.PlayerServiceImpl.insertPlayer(..)) && @annotation(com.fh.annotation.UserInfo)")//本环绕通知指向的方法位置,以及该方法被何种类型注解修饰
    public Object aroundSuperMethod(ProceedingJoinPoint pjp) {
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();//获取方法签名
        Method method = methodSignature.getMethod();//获取方法
        UserInfo userInfo = method.getAnnotation(UserInfo.class);//获取自定义的Limit注解信息
        //如果该注解为空,也就是方法上没有UserInfo注解,则直接返回执行结果
        if (Objects.isNull(userInfo)) {
            try {
                return pjp.proceed(pjp.getArgs());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            return null;
        } else {
            for (Object o : pjp.getArgs()) {
                PlayerParam pp = (PlayerParam) o;
                pp.setName(userInfo.name());
                pp.setAge(Integer.parseInt(userInfo.age()));
            }
            try {
                return pjp.proceed(pjp.getArgs());
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            return null;
        }
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值