先上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
:环绕通知,可以阻止目标方法执行.同时有Around
和Before
的时候优先执行Around
,然后再执行Before
.同时有Around
和AfterReturning
的时候先执行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(){}