SpringBoot AOP注解失效問題排查與解決(調用內部方法)
開發(fā)時,遇到這樣一個問題。項目使用springboot框架,項目中的task基于quartz實現(xiàn),其中有個BaseTask代碼實現(xiàn)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) {
// 在這里實現(xiàn)你的具體任務邏輯
System.out.println("執(zhí)行MyTask的doTask方法");
}
}
這時候因為做的業(yè)務會涉及很多類似的數(shù)據(jù)同步,有很多task,且都需要做如下操作:
1.實現(xiàn)并發(fā)鎖控制
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(); // 獲取方法的參數(shù)
System.out.println("方法參數(shù): " + args[0]);
}
}
4.在MyTask類的方法里加上注解
public class MyTask extends BaseTask {
@Override
@TaskRecord
public void doTask(TaskDTO taskDto) {
// 在這里實現(xiàn)你的具體任務邏輯
System.out.println("執(zhí)行MyTask的doTask方法");
}
}
一番操作下來行云流水,心里默想:老子cv代碼天下第一,肯定沒問題。打斷點debug調試,發(fā)現(xiàn)問題出現(xiàn),根本進不了@Before方法里啊
于是排查問題,百度chatgpt問了n²+1天,在試過如下方法如:
1.將MyTask,BaseTask加上@Component將bean加載到spring容器內
2.啟動類增加@EnableAspectJAutoProxy注解(實際SpringBoot默認自動配置AOP的)
3.聯(lián)想是否跟CGLIB或JDK動態(tài)代理有關
一系列操作,發(fā)現(xiàn)屢戰(zhàn)屢敗,于是擱置了數(shù)天,今天又心血來潮試了下,換了個思路去思考,查詢AOP失效的幾個場景,總結如下:
1.內部方法調用:AOP通常不會攔截同一個類內部的方法調用。如果一個被代理的方法調用了另一個被代理的方法,那么只有外部調用的方法會觸發(fā)AOP。
2.AOP配置問題:AOP切入點配置不正確,切入點表達式可能沒有正確應用到目標bean。
3.直接實例化對象:如直接使用new關鍵字創(chuàng)建了一個對象實例,AOP將無法攔截此對象的方法調用。需要通過Spring容器獲取對象,讓Spring負責bean的實例化,才能讓AOP生效。
嗯?好像有點思路了,很符合1的情況,于是在某個controller寫了個請求并加上注解,發(fā)現(xiàn)可以進入,但是如果寫一個內部方法,在內部方法上加注解,請求的方法再去調用則會失效,如下:
這樣是ok的
@RequestMapping("/test")
@ResponseBody
@TaskRecord
public String test(@RequestBody TestRequest request) {
test1(request);
return "執(zhí)行成功!";
}
這樣是not ok的
@RequestMapping("/test")
@ResponseBody
public String test(@RequestBody TestRequest request) {
test1(request);
return "執(zhí)行成功!";
}
@TaskRecord
public void test1(TestRequest request) {
System.out.println(request.toString());
}
所以思路就是在執(zhí)行內部的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改造,實現(xiàn)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注解失效內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
spring session同域下單點登錄實現(xiàn)解析
這篇文章主要介紹了spring session同域下單點登錄實現(xiàn)解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-10-10
springboot整合mybatis實現(xiàn)數(shù)據(jù)庫的更新批處理方式
這篇文章主要介紹了springboot整合mybatis實現(xiàn)數(shù)據(jù)庫的更新批處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
Java ArrayList.add 的實現(xiàn)方法
這篇文章主要介紹了Java ArrayList.add 的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11
一小時迅速入門Mybatis之實體類別名與多參數(shù) 動態(tài)SQL
這篇文章主要介紹了一小時迅速入門Mybatis之實體類別名與多參數(shù) 動態(tài)SQL,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
聊聊Spring?Cloud?Gateway過濾器精確控制異常返回問題
這篇文章主要介紹了Spring?Cloud?Gateway過濾器精確控制異常返回問題,本篇任務就是分析上述現(xiàn)象的原因,通過閱讀源碼搞清楚返回碼和響應body生成的具體邏輯,需要的朋友可以參考下2021-11-11

