先上maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
一个简单的增删改查service(被切入的类)
package com.fchan.layui.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fchan.layui.aspect.CheckUser;
import com.fchan.layui.entity.AopTestEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class SpringAopTestService {
private static final ObjectMapper objectMapper = new ObjectMapper();
public String insert(AopTestEntity aopTestEntity) throws JsonProcessingException {
String result = objectMapper.writeValueAsString(aopTestEntity);
log.info("插入一条数据:{}",result);
return result;
}
@CheckUser
public void delete(AopTestEntity aopTestEntity) throws JsonProcessingException {
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正则形式
注解形式的需要先声明一个注解接口
package com.fchan.layui.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CheckUser {
}
`切面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("target(com.aop.service.TestService)")
package com.fchan.layui.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class TestAspect {
@Pointcut(value = "execution(* com.fchan.layui.service.SpringAopTestService.insert(..))")
public void insertPointcut(){}
@Before(value = "insertPointcut()")
public String beforeInsert(JoinPoint joinPoint){
if(!"hello".equals(CurrentUserHolder.get())){
return "not allow";
}
return "allow";
}
@Pointcut("@annotation(CheckUser)")
public void CheckUser(){
}
@After(value = "CheckUser()")
public void afterDelete(JoinPoint joinPoint){
log.info("删除一条数据之后:{}",joinPoint.getArgs());
}
/**
* 要判断传入的参数类型是Map或者List时需要写包名全路径
*/
@Pointcut("execution(String com.fchan.layui.service.SpringAopTestService.*(java.util.Map)))")
public void testArgOnlyReturnStringAndMap(){}
}
@Pointcut的几种匹配包的方式
within
..
代表匹配包以及子包
//匹配 ProductService类里的的所有方法
@Pointcut("within(com.imooc.service.ProductService)")
public void testWithin(){}
//匹配com.imooc包以及子包下所有类的方法
@Pointcut("within(com.imooc..*)")
public void testWithin(){}
匹配对象
/**
* 匹配AOP对象的目标对象为指定类型的方法,即 SpringAopTestService 的aop代理对象的方法
*/
@Pointcut(value = "this(com.fchan.layui.service.SpringAopTestService)")
public void testThis(){}
/**
* 匹配实现 SpringAopTestInterface 接口的目标对象(而不是aop代理后的对象)的方法,这里即SpringAopTestService的方法
*/
@Pointcut(value = "target(com.fchan.layui.service.SpringAopTestInterface)")
public void testTarget(){}
/**
* 匹配所有以service结尾的bean里头的方法
*/
@Pointcut(value = "bean(*Service)")
public void testBean(){}
匹配参数
/**
* 匹配任何以find开头而且只有一个Long参数的方法
*/
@Pointcut("execution(* *..find*(Long))")
public void testArgs(){}
/**
* 匹配任何只有一个Long参数的方法
*/
@Pointcut("args(Long)")
public void testArgOne(){}
/**
* 匹配任何以find开头的而且第一个参数为Long型的方法
*/
@Pointcut("execution( * *..find*(Long, ..))")
public void testArgsOne(){}
/**
* 匹配第一个参数为Long型的所有方法
*/
@Pointcut("args(Long, ..)")
public void testArgFirstOne(){}
/**
* 匹配路径在com.fchan.layui.service下的SpringAopTestService中只有一个Long型参数的所有方法
*/
@Pointcut("args(Long) && within(com.fchan.layui.service.SpringAopTestService))")
public void testArgAndPackage(){}
execution的格式
修饰符
返回值类型
包名
方法名和方法形参
匹配方法抛出的异常
图中后面带"?"的可以省略,其他的不能省略
/**
* 匹配路径在com.fchan.layui.service下的SpringAopTestService中返回值为String的所有方法
*/
@Pointcut("execution(String com.fchan.layui.service.SpringAopTestService.*(..)))")
public void testArgAndPackageOnlyReturnString(){}