SpringBoot中使用AspectJ

本文介绍如何在Spring Boot项目中利用AOP(面向切面编程)进行代码层面的解耦,包括配置依赖、创建切面类及通知方法,并通过具体示例展示了如何配合自定义注解实现更灵活的切入点定义。

pom中添加依赖

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

常规切面使用

切入指定包类目录,如@Pointcut("execution(* sb.simple.aspectj.normal.*.*(..))")

@Pointcut参数正则可以自行了解一下

  • 切面类

    package sb.simple.aspectj.normal;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.Arrays;
    
    /**
     * AspectJ
     * 五种通知注解:@Before/@Around/@After/@AfterReturning/@AfterThrowing
     */
    @Configuration
    @Aspect
    public class AJNormal {
    
        @Pointcut("execution(* sb.simple.aspectj.normal.*.*(..))")
        public void point() { }
    
        /**
         * 前置通知
         * 连接点之前执行
         */
        @Before("point()")
        public void before(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            System.out.println("before() methodName:" + methodName + ", args:" + Arrays.toString(args));
        }
    
        @Around("point()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            System.out.println("around() before proceed");
            Object proceed = joinPoint.proceed();
            System.out.println("around() after proceed methodName:" + methodName + ", args:" + Arrays.toString(args) + ", result:" + proceed);
            return proceed;
        }
    
        /**
         * 后置通知
         * 连接点方法完成后执行
         * 无论连接点执行成功与否该通知都会执行
         */
        @After("point()")
        public void after(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            System.out.println("after() methodName:" + methodName + ", args:" + Arrays.toString(args));
        }
    
        /**
         * 连接点方法执行成功后执行
         * 可以拿到返回结果
         */
        @AfterReturning(value = "point()", returning = "result")
        public void result(JoinPoint joinPoint, Object result) {
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            System.out.println("AfterReturning() methodName:" + methodName + ", args:" + Arrays.toString(args) + ", result:" + result);
        }
    
        /**
         * 连接点方法执行异常后执行
         * 可以拿到异常信息
         */
        @AfterThrowing(value = "point()", throwing = "exception")
        public void afterThrowing(JoinPoint joinPoint, Exception exception) {
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            System.out.println("afterThrowing() methodName:" + methodName + ", args:" + Arrays.toString(args) + ", exception:" + exception);
        }
    }
    
  • 被切类

    package sb.simple.aspectj.normal;
    
    import org.springframework.stereotype.Component;
    import org.springframework.stereotype.Service;
    
    @Service
    @Component
    public class CalcService {
    
        public int add(int a, int b) {
            int result = a + b;
            System.out.println("CalcService->add() result:" + result);
            return a + b;
        }
    
        //测试AfterThrowing通知
        public int divider(int a, int b) {
            int result = a / b;
            System.out.println("CalcService->divider() result:" + result);
            return result;
        }
    }
    
  • 测试类

    package sb.simple.aspectj;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import sb.simple.aspectj.annotation.CalcAnClass;
    import sb.simple.aspectj.annotation.CalcAnMethod;
    import sb.simple.aspectj.normal.CalcService;
    
    @SpringBootTest
    public class AJTest {
    
    
        /**普通切入*/
        @Autowired
        private CalcService calcService;
    
        /*
        around() before proceed
        before() methodName:add, args:[1, 2]
        CalcService->add() result:3
        AfterReturning() methodName:add, args:[1, 2], result:3
        after() methodName:add, args:[1, 2]
        around() after proceed methodName:add, args:[1, 2], result:3
         */
        @Test
        public void testAdd() {
            calcService.add(1, 2);
        }
    
        /*
        around() before proceed
        before() methodName:divider, args:[3, 0]
        afterThrowing() methodName:divider, args:[3, 0], exception:java.lang.ArithmeticException: / by zero
        after() methodName:divider, args:[3, 0]
         */
        @Test
        public void testDivider() {
            calcService.divider(3, 0);
        }
    
    
        /*不用@Service注解实例化aspectj就没用
        CalcService->add() result:8
         */
        @Test
        public void testAddNew() {
            new CalcService().add(3, 5);
        }
    }
    

配合自定义注解使用

  • 自定义注解

    package sb.simple.aspectj.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    //METHOD->注解于方法 | TYPE->注解于类
    @Target({ElementType.METHOD, ElementType.TYPE})
    public @interface Require {
    
        String value();
    }
    
  • 被切类

    package sb.simple.aspectj.annotation;
    
    import org.springframework.stereotype.Service;
    
    //注解于类
    @Service
    @Require("in class")
    public class CalcAnClass {
    
        public void hi(String name) {
            System.out.println(getClass().getSimpleName() + ": hi:" + name);
        }
    }
    

    package sb.simple.aspectj.annotation;
    
    import org.springframework.stereotype.Service;
    
    //注解于方法
    @Service
    public class CalcAnMethod {
    
        @Require("in method")
        public void hi(String name) {
            System.out.println(getClass().getSimpleName() + ": hi:" + name);
        }
    }
    
  • 切面类

    在通知方法@Around中,通过ProceedingJoinPoint实例可以获取被切方法的方法名,方法参数,所在类,注解,注解参数等,可以通过这些数据对方法进行增强

    package sb.simple.aspectj.annotation;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.context.annotation.Configuration;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    @Configuration
    @Aspect
    public class AJAnnotation {
    
        /**注解于类@within*/
        @Pointcut("@within(sb.simple.aspectj.annotation.Require)&&execution(public * *.*(..))")
        public void pointClass() { }
    
        @Around("pointClass()")
        public Object aroundClass(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("aroundClass() in");
    
            //获取在类上的注解中的属性值
            String className = joinPoint.getTarget().getClass().getName();
            Class<?> clz = Class.forName(className);
            Require require = clz.getAnnotation(Require.class);
            System.out.println("annotation(class), clzName:" + clz.getSimpleName());
            String value = require.value();
            System.out.println("annotation(class)->Require->value:" + value);
    
            return joinPoint.proceed();
        }
    
    
        /**注解于方法@annotation*/
        @Pointcut("@annotation(sb.simple.aspectj.annotation.Require)")
        public void point() { }
    
    
        @Around("point()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("aroundMethod() in");
    
            //获取在方法上的注解的属性值
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Class<?> clz = joinPoint.getTarget().getClass();
            String name = signature.getName();
            Class[] parameterTypes = signature.getParameterTypes();
            Method method = clz.getMethod(name, parameterTypes);
            System.out.println("annotation(method), clzName:" + clz.getSimpleName() + ", name:" + name + ", parameterTypes:" + Arrays.toString(parameterTypes));
            Require require = method.getAnnotation(Require.class);
            String value = require.value();
            System.out.println("annotation(method)->Require->value:" + value);
    
    
            return joinPoint.proceed();
        }
    }
    
  • 测试类

    package sb.simple.aspectj;
    
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import sb.simple.aspectj.annotation.CalcAnClass;
    import sb.simple.aspectj.annotation.CalcAnMethod;
    import sb.simple.aspectj.normal.CalcService;
    
    @SpringBootTest
    public class AJTest {
    
        /**配合自定义注解切入*/
        @Autowired
        CalcAnClass calcAnClass;
    
        @Test
        public void anClass() {
            calcAnClass.hi("akali(class)");
        }
    
        @Autowired
        CalcAnMethod calcAnMethod;
    
        @Test
        public void anMethod() {
            calcAnMethod.hi("annie(method)");
        }
    }
    
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值