一個SpringBoot項目中使用了 Scheduled Task ,如果任務出現異常,需要發送郵件給管理員,讓管理員進行處理。而使用 AOP 對所有任務裏的一些方法進行攔截處理時,由於方法經過封裝(需要被攔截的),在 task 內部調用該方法時 AOP 不能直接攔截,所以我們需要進行一些特殊處理。
在 task 中定義了兩個定時 job 方法,一個是導入 yri
的文件,一個是導入 usk
的。在導入(從ftp下載文件,複製文件到數據庫服務器,導入)的過程中如果有任何的異常,都需要在 AOP 中進行處理,發送郵件。因此,我們的 AOP 的切面方法需要直接定位到 importFeedFiles
,而根據實際情況,我們是在內部進行調用的 importFeedFiles
。所以我們需要在 AOP 中添加 @EnableAspectJAutoProxy(exposeProxy = true)
實現,然後在 FeedFileScheduleTasks
中通過 AopContext.currentProxy()
獲取 FeedFileScheduleTasks
對象,然後使用該方法返回的對象調用 importFeedFiles
時,AOP 就可以攔截到。
@Component
@EnableScheduling
@Data
@Slf4j
public class FeedFileScheduleTasks {
@Scheduled(cron = "${feedfile.schedule.yri.download.cron}")
public CommonResponse importYriFeedFiles() {
CommonResponse response = this.currentScheduledTask().importFeedFiles(FEED_FILE_TYPE_YRI);
this.yriScheduledResult = response.isSuccess();
return response;
}
@Scheduled(cron = "${feedfile.schedule.usk.download.cron}")
public CommonResponse importUskFeedFiles() {
CommonResponse response = this.currentScheduledTask().importFeedFiles(FEED_FILE_TYPE_USK);
// uskScheduledResult 類型爲 boolean
this.uskScheduledResult = response.isSuccess();
return response;
}
@ScheduledMethodLog
public CommonResponse importFeedFiles(String feedFileType) {
return this.importFeedFiles(feedFileType, DateUtils.getToday(), true);
}
/**
* 具體的業務方法.
*/
public CommonResponse importFeedFiles(String feedFileType, String dateStr, boolean feedFileDownloaded) {
// 1. download: 下載 feed file
// 2. copy: 到數據庫服務器
// 3. import: 到數據庫
return response;
}
private FeedFileScheduleTasks currentScheduledTask() {
if(isExposeProxy()){
return (FeedFileScheduleTasks) AopContext.currentProxy();
} else {
return this;
}
}
/**
* AOP切面類的 {@linkplain EnableAspectJAutoProxy} 註釋的 exposeProxy 設置。
*
* @return 返回 true 如果 exposeProxy 設置爲 true
*/
private boolean isExposeProxy() {
try {
AopContext.currentProxy();
} catch (IllegalStateException e) {
return false;
}
return true;
}
}
AOP 類
@Aspect
@Component
@Slf4j
@EnableAspectJAutoProxy(exposeProxy = true)
public class YumCommonAop {
/**
* 攔截處理 task 的方法。
**/
@Around("@annotation(com.tts.commons.annotation.ScheduledMethodLog)")
public Object handlerTaskMethod(ProceedingJoinPoint pjp) throws Throwable {
String method = pjp.getSignature().getName();
StringBuilder target = new StringBuilder();
if (!StringUtils.isEmpty(pjp.getArgs()) && pjp.getArgs().length > 0) {
target.append(pjp.getArgs()[0]);
}
target.append(" ").append(method);
log.info("{} task start...", target.toString());
Object returning = null;
try {
returning = pjp.proceed();
} catch (Throwable ex) {
returning = handlerException(target.toString(), ex);
}
log.info("{} task end...", target.toString());
return returning;
}
/**
* 處理異常,發送郵件.
* @param target feedFileType + 目標方法
* @param ex 異常
* @return {@linkplain CommonResponse#SIMPLE_FAILURE}
*/
private CommonResponse handlerException(String target, Throwable ex) {
log.error("The {} execute error!", target, ex);
// 發送郵件
return CommonResponse.SIMPLE_FAILURE;
}
}
自定義切面註釋類:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ScheduledMethodLog {
String description() default "";
}
參考:
http://blog.csdn.net/nieyanshun_me/article/details/74898401
https://www.cnblogs.com/intsmaze/p/5206584.html