SpringBoot AOP注解失效问题排查与解决(调用内部方法)

 更新时间:2024年04月02日 10:58:06   作者:园区阿祖  
这篇文章主要介绍了SpringBoot AOP注解失效问题排查与解决(调用内部方法),文中通过代码示例介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下

开发时,遇到这样一个问题。项目使用springboot框架,项目中的task基于quartz实现,其中有个BaseTask代码实现quartz的Job接口,关键代码如下:

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public abstract class BaseTask implements Job {

    @Override
    public void execute(JobExecutionContext context) {
    	// do something before...
        doTask(taskDto);
        // do something after...
    }
    
    public abstract void doTask(TaskDTO taskDto);
}

项目中的所有task会继承这个BaseTask,并重写doTask方法,如下:

public class MyTask extends BaseTask {

    @Override
    public void doTask(TaskDTO taskDto) {
        // 在这里实现你的具体任务逻辑
        System.out.println("执行MyTask的doTask方法");
    }
}

这时候因为做的业务会涉及很多类似的数据同步,有很多task,且都需要做如下操作:

1.实现并发锁控制

2.读取上次同步时间进行增量同步操作

3.插入taskRecord记录等

这些代码会极大的冗余和重复,故此想使用AOP的思想,使用注解的方式,将这些操作抽象整合,于是开干:

1.引入springboot的aop依赖

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

2.写注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TaskRecord {

}

3.使用@Aspect写切面

import org.aspectj.lang.JoinPoint;
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
public class TaskAspect {

    @Pointcut("execution(* com.example.task.*.doTask(..)) && @annotation(TaskRecord)")
    public void pointcut() {}

    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs(); // 获取方法的参数
        System.out.println("方法参数: " + args[0]);
    }
}

4.在MyTask类的方法里加上注解

public class MyTask extends BaseTask {

    @Override
    @TaskRecord
    public void doTask(TaskDTO taskDto) {
        // 在这里实现你的具体任务逻辑
        System.out.println("执行MyTask的doTask方法");
    }
}

一番操作下来行云流水,心里默想:老子cv代码天下第一,肯定没问题。打断点debug调试,发现问题出现,根本进不了@Before方法里啊

于是排查问题,百度chatgpt问了n²+1天,在试过如下方法如:

1.将MyTask,BaseTask加上@Component将bean加载到spring容器内

2.启动类增加@EnableAspectJAutoProxy注解(实际SpringBoot默认自动配置AOP的)

3.联想是否跟CGLIB或JDK动态代理有关

一系列操作,发现屡战屡败,于是搁置了数天,今天又心血来潮试了下,换了个思路去思考,查询AOP失效的几个场景,总结如下:

1.内部方法调用:AOP通常不会拦截同一个类内部的方法调用。如果一个被代理的方法调用了另一个被代理的方法,那么只有外部调用的方法会触发AOP。

2.AOP配置问题:AOP切入点配置不正确,切入点表达式可能没有正确应用到目标bean。

3.直接实例化对象:如直接使用new关键字创建了一个对象实例,AOP将无法拦截此对象的方法调用。需要通过Spring容器获取对象,让Spring负责bean的实例化,才能让AOP生效。

嗯?好像有点思路了,很符合1的情况,于是在某个controller写了个请求并加上注解,发现可以进入,但是如果写一个内部方法,在内部方法上加注解,请求的方法再去调用则会失效,如下:

这样是ok的

 	@RequestMapping("/test")
    @ResponseBody
     @TaskRecord
    public String test(@RequestBody TestRequest request) {
        test1(request);
        return "执行成功!";
    }

这样是not ok的

 	@RequestMapping("/test")
    @ResponseBody
    public String test(@RequestBody TestRequest request) {
        test1(request);
        return "执行成功!";
    }
    
    @TaskRecord
    public void test1(TestRequest request) {
        System.out.println(request.toString());
    }

所以思路就是在执行内部的doTask方法时,不能直接调用,需要通过代理类去调用才能让AOP切面生效,改造如下:

1.新增接口

public interface JobCommonService {

    void doTaskNew(TaskDTO taskDTO) throws Exception;
}

2.BaseTask改造,不直接调用doTask

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public abstract class BaseTask implements Job {

    @Override
    public void execute(JobExecutionContext context) {
    	// do something before...
    	// 这是自建的一个spring容器工具类,可以获取容器里的bean
    	JobCommonService jobService = SpringContextUtil.getBean(JobCommonService.class);
        jobService.doTaskNew(taskDTO);
        // do something after...
    }
    
    public abstract void doTask(TaskDTO taskDto);
}

3.具体task改造,实现JobCommon接口重写doTaskNew方法

@Component
public class MyTask extends BaseTask implements JobCommonService{

    @Override
    public void doTask(TaskDTO taskDto) {
    	return;
    }

	@Override
    @TaskRecord
    public void doTaskNew(TaskDTO taskDTO) {
        // do something...
        return;
    }
}

至此打完收工,成功进入@Before方法,下面关掉所有查询的窗口,可以愉快的进行其他操作啦!

到此这篇关于SpringBoot AOP注解失效问题排查与解决(调用内部方法)的文章就介绍到这了,更多相关SpringBoot AOP注解失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • spring session同域下单点登录实现解析

    spring session同域下单点登录实现解析

    这篇文章主要介绍了spring session同域下单点登录实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • IDEA中osgi的开发应用指南详解

    IDEA中osgi的开发应用指南详解

    这篇文章主要介绍了IDEA中osgi的开发应用指南详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-04-04
  • springboot整合mybatis实现数据库的更新批处理方式

    springboot整合mybatis实现数据库的更新批处理方式

    这篇文章主要介绍了springboot整合mybatis实现数据库的更新批处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Java ArrayList.add 的实现方法

    Java ArrayList.add 的实现方法

    这篇文章主要介绍了Java ArrayList.add 的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • 学Java做项目需要学习的一些技能

    学Java做项目需要学习的一些技能

    这篇文章主要介绍了学Java做项目需要学习的一些技能,例如JavaSE、Servlet、JSP等,总结了他们中需要学习的东西,都是一些经验总结,需要的朋友可以参考下
    2014-07-07
  • 一小时迅速入门Mybatis之实体类别名与多参数 动态SQL

    一小时迅速入门Mybatis之实体类别名与多参数 动态SQL

    这篇文章主要介绍了一小时迅速入门Mybatis之实体类别名与多参数 动态SQL,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • spring导入framework报红问题

    spring导入framework报红问题

    这篇文章主要介绍了spring导入framework报红问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • Springboot中Jackson用法详解

    Springboot中Jackson用法详解

    Springboot自带默认json解析Jackson,可以在不引入其他json解析包情况下,解析json字段,下面我们就来聊聊Springboot中Jackson的用法吧
    2025-01-01
  • Java的内存机制详解

    Java的内存机制详解

    本文主要介绍了Java的内存机制的相关知识,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-03-03
  • 聊聊Spring Cloud Gateway过滤器精确控制异常返回问题

    聊聊Spring Cloud Gateway过滤器精确控制异常返回问题

    这篇文章主要介绍了Spring Cloud Gateway过滤器精确控制异常返回问题,本篇任务就是分析上述现象的原因,通过阅读源码搞清楚返回码和响应body生成的具体逻辑,需要的朋友可以参考下
    2021-11-11

最新评论