實現 Spring AOP 攔截對象內部調用的方法

轉:實現 Spring AOP 攔截對象內部調用的方法

一個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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章