springboot集成aop

文章目录

      • aop的作用
    • 集成过程
      • pom.xml引入依赖
      • Application加上 @EnableAspectJAutoProxy 注解
      • 配置文件中设置aop
      • aop配置类
      • 例子
      • 获取入参
      • 获取出参
      • payAop例子
      • Around的作用
      • 如果方法中断,如何记录日志
      • aop和拦截器的区别
      • aop是异步的么
      • 切入点用service还是serviceImpl
      • cglib动态代理 和 jdk动态代理 的区别
      • aop异常处理

aop的作用

面向切换编程。
比如记录controller日志等,都很有用。

集成过程

pom.xml引入依赖

spring-boot-starter-parent一般会包含它,所以不用引入:

<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-aopartifactId>
dependency>

Application加上 @EnableAspectJAutoProxy 注解

springboot是不用加这个注解的。

配置文件中设置aop

aop的配置项少的可怜,已知的就2个,一般采用默认就行,也不用配置:

# 是否使用aop自动代理,默认值为true。 设置为false表示关闭自动代理。
# 在boot中不用加@EnableAspectJAutoProxy 因为只要引入aop依赖,并且spring.aop.auto=true 也是默认值,就会自动使用代理
spring.aop.auto=true 
# 是否使用cglib动态代理,默认为false 
spring.aop.proxy-target-class=false

如果不是用的boot,那么需要添加 @EnableAspectJAutoProxy 注解,启用动态代理。

aop配置类

@Pointcut 定义切入点,以便后续调用。
@After(“captureUserFindAll()”) ,调用切入点,并增加逻辑。
代码:

@Aspect
@Component
public class UserAop {
    // 定义切入点,这个方法名captureUserFindAll() 后续要用到
    @Pointcut("execution (* com.example.accessingdatamysql.controller.UserService.findAll(..))")
    public void captureUserFindAll(){}

    // 在 captureUserFindAll() 这个切入点之后执行操作
    @After("captureUserFindAll()")
    public void doAfter() {
        System.out.println("after UserService.findAll 成功");
    }
}

然后用controller调用下 UserService.findAll() 方法,发现切入成功。

例子

推荐设置切入点,这样逻辑清楚,而且可以复用,当然,也可以每个注解直接写表达式,不引入切入点。

@Aspect
@Configuration
public class TestAop{
    @Before("execution(* com.example.accessingdatamysql.controller..*.*(..))")
    public void before(JoinPoint joinPoint) throws Throwable {
        System.out.println("------- this is before --------");
    }
    @After("execution(* com.example.accessingdatamysql.controller..*.*(..))")
    public void after(JoinPoint joinPoint){
        System.out.println("------- this is after --------");
    }
    @Around("execution(* com.example.accessingdatamysql.controller..*.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("------- this is around before --------");
            Object proceed = proceedingJoinPoint.proceed(); //  这个方法表示继续执行
//            System.out.println(JSON.toJSON(proceedingJoinPoint));
            System.out.println("------- this is around after --------");
            return null;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            return null;
        }
    }
}

获取入参

获取入参比较简单,Object[] args = joinPoint.getArgs(); 获取参数列表,然后强制转换即可。

获取出参

需要用 @AfterReturning 注解,returning指定返回对象的名称,代码:

@AfterReturning(value="execution (* com.ssss.web.ServiceImpl.findAll(..))",
       returning="user")
public void afterCollect(JoinPoint joinPoint,User user){

}

payAop例子

代码:

@Aspect
@Component
public class PayAop {

    private static final Logger logger = LoggerFactory.getLogger(PayAop.class);

    @Pointcut("execution (* com.example.PayServiceImpl.pay(..))")
    public void proxy() {

    }

    @Before("proxy()")
    public void before(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        Double amount=(Double)args[0];
        logger.info("开始支付,支付金额"+amount);

    }

    @After("proxy()")
    public void after(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        Double amount=(Double)args[0];
        logger.info("支付完成,支付金额"+amount);


    }

    @AfterReturning(value="proxy()",returning="string")
    public void afterRerurning(JoinPoint joinPoint,String string){
        logger.info("支付结果:"+string);
    }

    // Around 相当于Before、AfterReturning、After之和
    @Around("proxy()")
    public void around(ProceedingJoinPoint proceedingJoinPoint){
        Object[] args = proceedingJoinPoint.getArgs();
        Double amount=(Double)args[0];
        logger.info("开始支付,支付金额"+amount);
        try {
            String string = (String)proceedingJoinPoint.proceed();
            logger.info("支付结果:"+string);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        logger.info("支付完成,支付金额"+amount);
    }

    // 异常处理
    @AfterThrowing(value="proxy()",throwing="e")
    public void afterThrowing(Exception e){
        logger.info("出现异常了");
    }
}

Around的作用

Around 相当于Before、AfterReturning、After之和

如果方法中断,如何记录日志

aop和拦截器的区别

拦截器算是aop的一种实现方式。
拦截器基于url,aop更灵活,面向的范围更广,例如 类,方法等。
拦截器可以用来阻断后续的执行,但是aop不能,aop并不影响原来的逻辑。

aop是异步的么

实测不是,例如用controller调用,需要等aop方法执行完毕之后才会返回。
为此找bug还费了很多时间。
如果要异步,可以在aop新增的逻辑里面启动个线程,这样就不会阻塞了。

还有一种方式是调用的service方法上加 @Async 注解,表示异步。
这种其实有限制,因为要切入的service已经写好,aop没法改他。

切入点用service还是serviceImpl

实测2个都可以。
我这里用的是springboot,并且用的默认配置(没做任何设置)。

cglib动态代理 和 jdk动态代理 的区别

spring.aop.proxy-target-class=false 这个配置项就是设置选用哪种代理的。
中间的知识点很多,不详述。

aop异常处理

你可能感兴趣的:(spring)