Spring @Aspect注解

28 篇文章 0 订阅
10 篇文章 0 订阅

使用场景

  • 常见用于记录日志, 异常集中处理, 权限验证以及 Web参数有效验证等等

列子1 (演示基本过程


@Aspect
@Component
public class TestAspect {
    /**
     * 声明一个切入点, 命名 pointcut1
     * */
    @Pointcut("execution(public java.util.Map com.example.demo.controller.TestService.getMap(String, Integer))")
    private void pointcut1() {}

    /**
     * 锁定的切点方法之前执行
     * JoinPoint 获取连接点信息 
     *	-	Object[] getArgs() 			获取连接点方法运行时的入参列表
     *	-	Signature getSignature() 	获取连接点的方法签名对象
     *	-	Object getTarget() 			获取连接点所在的目标对象
     *	-	Object getThis() 			获取代理对象
     * */
    @Before("pointcut1()")
    public void aspect1(JoinPoint joinPoint) {
        System.out.println("TestAspect -> @Before 方法名称:" + joinPoint.getSignature().getName() + ", 参数:" + Arrays.asList(joinPoint.getArgs()));
    }

    /**
     * 锁定的切点方法之后执行
     * */
    @After("pointcut1()")
    public void aspect2() {
        System.out.println("TestAspect -> @After");
    }

    /**
     * 锁定的切点方法返回后执行
     * */
    @AfterReturning(pointcut = "pointcut1()", returning="result")
    public void aspect3(Object result){
        System.out.println("TestAspect -> @AfterReturning 返回值:" + result);
    }

}

@Service
public class TestService {
    public Map<String, Object> getMap(String name, Integer age) {
        final Map<String, Object> result = new HashMap<>(2);
        result.put("name", name);
        result.put("age", age);
        System.out.println("TestService -> getMap 函数体内输出:" + result);
        return result;
    }

}

# 测试地址 http://127.0.0.1:8080/aspectTest
@RestController
public class AspectController {
    @Autowired
    private TestService testService;

    @GetMapping(value = "/aspectTest")
    public Map<String, Object> aspectTest(@RequestParam(value="name", required=false, defaultValue="大爷") String name,
                                          @RequestParam(value="age", required=false, defaultValue="35") Integer age) {
        System.out.println("AspectController -> aspectTest");
        return testService.getMap(name, age);
    }

}

# 输出
AspectController -> aspectTest
TestAspect -> @Before 方法名称:getMap, 参数:[大爷, 35]
TestService -> getMap 函数体内输出:{name=大爷, age=35}
TestAspect -> @After
TestAspect -> @AfterReturning 返回值:{name=大爷, age=35}

切点表达式

  1. ..两个点表明多个, *代表一个
  2. 其中权限修饰符是可选, 当不写时不能用*代替, 因此第一个*代表返回类型不限
  3. 第二个*表示指定包下所有类
  4. 第三个*表示指定类下所有方法
  5. (..)两个点表示指定方法的参数不限

@Pointcut(execution(* com..demo.controller.*.*(..)))

切点复合运算

  • 切点表达式可以加运算符 与&&, 或||, 非! 做复合运算

@Before(value="execution(* com.example.demo.controller.TestService.getMap(..)) && args(name, age, ..)")

切点匹配方法

  • execution: 用于匹配方法的执行
  • within: 用于匹配指定类内的方法的执行
  • args: 用于指定匹配方法的参数类型
  • target: 用于匹配容器内的类的对象执行方法, 不包括引入接口
  • this: 用于匹配容器内的类的对象执行方法, 包括引入接口

注: target和 this两种方法表达式必须全限定名到类名, 不支持*通配符

通知注解

  • @Before: 前置通知, 在方法执行之前执行
  • @After: 后置通知, 在方法执行之后执行
  • @AfterRunning: 返回通知, 在方法返回结果之后执行

@AfterReturning(pointcut = "pointcut1()", returning="result")
public void aspect3(JoinPoint joinPoint, Object result){
	System.out.println("TestAspect1 -> @AfterReturning 返回值:" + result);
}

# 执行过程
Request -> @Before -> Method -> @After -> @AfterReturning	

  • @AfterThrowing: 异常通知, 在方法抛出异常之后执行, 意味着跳过返回通知

/**
* 指定方法内抛出异常时才会被通知
* 属性 throwing上设置异常类 Throwable将会捕获任何错误和异常, 或按需设置指定异常类
*/
@AfterThrowing(pointcut = "pointcut1()", throwing="ex")
public void aspect4(JoinPoint joinPoint, TestException ex) {
	System.out.println("TestAspect1 -> @AfterThrowing 异常:" + ex.getMessage());
}

# 执行过程
Request -> @Before -> Method -> @After -> @AfterThrowing

  • @Around: 环绕通知, 围绕着方法执行

/**
* ProceedingJoinPoint继承于 JoinPoint接口, 并多了两个方法
*	-	Object proceed() throws Throwable 		通过反射执行目标对象连接点处的方法
*	-	Object proceed(Object[] var1) throws Throwable 	通过入参替换原参, 通过反射执行目标对象连接点处的方法
*/
@Around("pointcut1()")
public Object aspect5(ProceedingJoinPoint pjp) throws Throwable {
	System.out.println("TestAspect1 -> @Around start");
	Object[] args = pjp.getArgs();
	for (Object arg : args) {
		System.out.println("TestAspect1 -> @Around 参数:" + arg); // 输出指定方法参数
	}
	long startTime = System.currentTimeMillis();
	Object object = pjp.proceed(); // 调用 proceed后指定方法会被执行
	long endTime = System.currentTimeMillis();
	System.out.println("TestAspect1 -> @Around " + (endTime - startTime) + " ms");
	return object;
}

# 输出
AspectController -> aspectTest
TestAspect1 -> @Around start
TestAspect1 -> @Around 参数:大爷
TestAspect1 -> @Around 参数:35
TestAspect1 -> @Before
TestService -> getMap 函数体内输出:{name=大爷, age=35}
TestAspect1 -> @Around 0 ms
TestAspect1 -> @After
TestAspect1 -> @AfterReturning 返回值:{name=大爷, age=35}

# 正常执行过程
Request -> @Around -> @Before -> Method -> @Around -> @After -> @AfterReturning
# 异常执行过程
Request -> @Around -> @Before -> Method -> @Around -> @After -> @AfterThrowing

切面类执行顺序

  • 通过 @Order注解设置, 数越小越靠前

@Aspect
@Component
@Order(3)
public class TestAspect1 {}

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
@Aspect注解Spring框架中用于定义切面的注解。通过在一个类上添加@Aspect注解,该类就成为了一个切面。在该类中,我们可以定义各种各样的通知(Advice),如@Before、@After、@Around等等,来拦截、增强目标方法的执行。 @Aspect注解需要和其他注解配合使用,其中最常用的注解是@Pointcut和@Before/@After/@Around等通知注解。@Pointcut注解用于定义切点,即需要被拦截的目标方法,而@Before/@After/@Around等通知注解则用于定义具体的拦截逻辑。 例如,我们可以在一个类中定义如下的@Aspect切面: ```java @Aspect @Component public class LogAspect { @Pointcut("execution(* com.example.demo.service..*.*(..))") public void serviceMethod() {} @Before("serviceMethod()") public void before(JoinPoint joinPoint) { // 在目标方法执行之前执行的逻辑 ... } @AfterReturning("serviceMethod()") public void afterReturning(JoinPoint joinPoint) { // 在目标方法执行之后执行的逻辑 ... } @AfterThrowing("serviceMethod()") public void afterThrowing(JoinPoint joinPoint) { // 在目标方法抛出异常时执行的逻辑 ... } @Around("serviceMethod()") public Object around(ProceedingJoinPoint pjp) throws Throwable { // 在目标方法执行前后执行的逻辑 ... Object result = pjp.proceed(); ... return result; } } ``` 在上述代码中,我们使用@Pointcut注解定义了一个切点serviceMethod(),该切点匹配所有com.example.demo.service包及其子包中的所有方法。然后我们使用@Before、@AfterReturning、@AfterThrowing和@Around注解定义了各种通知,来实现在目标方法执行前后、抛出异常时执行相关逻辑的功能。最后,我们将该类标注为@Component,使得Spring容器可以自动扫描并加载该切面。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值