springboot下的spring aop注解实现和execution正则实现

2022-06-26 12:57:59

先上maven依赖

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

一个简单的增删改查service(被切入的类)

packagecom.fchan.layui.service;importcom.fasterxml.jackson.core.JsonProcessingException;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.fchan.layui.aspect.CheckUser;importcom.fchan.layui.entity.AopTestEntity;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Service;@Service@Slf4jpublicclassSpringAopTestService{privatestaticfinalObjectMapper objectMapper=newObjectMapper();publicStringinsert(AopTestEntity aopTestEntity)throwsJsonProcessingException{String result= objectMapper.writeValueAsString(aopTestEntity);
        log.info("插入一条数据:{}",result);return result;}@CheckUserpublicvoiddelete(AopTestEntity aopTestEntity)throwsJsonProcessingException{String result= objectMapper.writeValueAsString(aopTestEntity);
        log.info("删除一条数据:{}",result);}}

AspectJ提供不同的通知类型

  • @Before:前置通知,在执行方法之前进入前置通知
  • @AfterReturning:后置通知,可以获取到方法的返回值,但是修改不了返回值,方法抛异常后不会执行这个切入点的后置通知方法.
  • @Around:环绕通知,可以阻止目标方法执行.同时有AroundBefore的时候优先执行Around,然后再执行Before.同时有AroundAfterReturning的时候先执行AfterReturning再执行Around.Around.要注意Around可以拿到方法的返回值,并且可以修改返回值.
  • @AfterThrowing:异常抛出通知,只能捕获方法的异常,如果是在切面中抛出的异常是捕获不到的.
  • @After:最终final通知,不管被切入方法是否抛异常,这个通知都会执行.和Around一起用的时候After优先级高
  • @DeclareParents:引介通知

aop切面的注解形式和execution正则形式

注解形式的需要先声明一个注解接口

packagecom.fchan.layui.aspect;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public@interfaceCheckUser{}
`切面demo代码`

在切面中使用根据注解切的时候的各种方式,如根据注解切整个controller类的方法。(当前这个注解要声明未可以加在类上)

//@Around("@annotation(自定义注解)")//自定义注解标注在方法上的方法执行aop方法
如:@Around("@annotation(org.springframework.transaction.annotation.Transactional)")//@Around("@within(自定义注解)")//自定义注解标注在的类上;该类的所有方法(不包含子类方法)执行aop方法
如:@Around("@within(org.springframework.transaction.annotation.Transactional)")//@Around("within(包名前缀.*)")//com.aop.within包下所有类的所有的方法都会执行(不包含子包) aop方法
如:@Around("within(com.aop.test.*)")//@Around("within(包名前缀..*)")//com.aop.within包下所有的方法都会执行(包含子包)aop 方法
如:@Around("within(com.aop.test..*)")//@Around("this(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,但不包含父类的方法)都会执行aop方法
如:@Around("this(com.aop.service.TestService)")//@Around("target(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,包含父类的方法)
如:@Around("this(com.aop.service.TestService)")
packagecom.fchan.layui.aspect;importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.JoinPoint;importorg.aspectj.lang.annotation.After;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;importorg.aspectj.lang.annotation.Pointcut;importorg.springframework.stereotype.Component;@Aspect@Component@Slf4jpublicclassTestAspect{@Pointcut(value="execution(* com.fchan.layui.service.SpringAopTestService.insert(..))")publicvoidinsertPointcut(){}@Before(value="insertPointcut()")publicStringbeforeInsert(JoinPoint joinPoint){if(!"hello".equals(CurrentUserHolder.get())){return"not allow";}return"allow";}@Pointcut("@annotation(CheckUser)")publicvoidCheckUser(){}@After(value="CheckUser()")publicvoidafterDelete(JoinPoint joinPoint){
        log.info("删除一条数据之后:{}",joinPoint.getArgs());}/**
     * 要判断传入的参数类型是Map或者List时需要写包名全路径
     */@Pointcut("execution(String com.fchan.layui.service.SpringAopTestService.*(java.util.Map)))")publicvoidtestArgOnlyReturnStringAndMap(){}}

@Pointcut的几种匹配包的方式

within
..代表匹配包以及子包

//匹配 ProductService类里的的所有方法@Pointcut("within(com.imooc.service.ProductService)")publicvoidtestWithin(){}//匹配com.imooc包以及子包下所有类的方法@Pointcut("within(com.imooc..*)")publicvoidtestWithin(){}

匹配对象

/**
 * 匹配AOP对象的目标对象为指定类型的方法,即 SpringAopTestService 的aop代理对象的方法
 */@Pointcut(value="this(com.fchan.layui.service.SpringAopTestService)")publicvoidtestThis(){}/**
 * 匹配实现 SpringAopTestInterface 接口的目标对象(而不是aop代理后的对象)的方法,这里即SpringAopTestService的方法
 */@Pointcut(value="target(com.fchan.layui.service.SpringAopTestInterface)")publicvoidtestTarget(){}/**
 * 匹配所有以service结尾的bean里头的方法
 */@Pointcut(value="bean(*Service)")publicvoidtestBean(){}

匹配参数

/**
  * 匹配任何以find开头而且只有一个Long参数的方法
  */@Pointcut("execution(* *..find*(Long))")publicvoidtestArgs(){}/**
 * 匹配任何只有一个Long参数的方法
 */@Pointcut("args(Long)")publicvoidtestArgOne(){}/**
 * 匹配任何以find开头的而且第一个参数为Long型的方法
 */@Pointcut("execution( * *..find*(Long, ..))")publicvoidtestArgsOne(){}/**
 * 匹配第一个参数为Long型的所有方法
 */@Pointcut("args(Long, ..)")publicvoidtestArgFirstOne(){}
/**
 * 匹配路径在com.fchan.layui.service下的SpringAopTestService中只有一个Long型参数的所有方法
 */@Pointcut("args(Long) && within(com.fchan.layui.service.SpringAopTestService))")publicvoidtestArgAndPackage(){}

execution的格式
在这里插入图片描述
修饰符
返回值类型
包名
方法名和方法形参
匹配方法抛出的异常

图中后面带"?"的可以省略,其他的不能省略

/**
 * 匹配路径在com.fchan.layui.service下的SpringAopTestService中返回值为String的所有方法
 */@Pointcut("execution(String com.fchan.layui.service.SpringAopTestService.*(..)))")publicvoidtestArgAndPackageOnlyReturnString(){}

使用通知的时候获取传入方法的形参,下面以Before通知为例

在这里插入图片描述

  • 作者:好大的月亮
  • 原文链接:https://blog.csdn.net/weixin_43944305/article/details/108187407
    更新时间:2022-06-26 12:57:59