实现 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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP可以使用AspectJ注解来实现接口重复调用拦截。假设我们有一个接口`UserService`,其有一个`saveUser`方法,我们希望在该方法调用时进行拦截,防止重复调用。我们可以定义一个切面类`DuplicateInvokeAspect`,并为其添加一个`@Around`注解的方法,该方法拦截`UserService`的`saveUser`方法,并在该方法添加重复调用判断逻辑。 ``` @Aspect public class DuplicateInvokeAspect { private Map<String, Long> lastInvokeMap = new ConcurrentHashMap<>(); @Around("execution(* com.example.UserService.saveUser(..))") public Object checkDuplicateInvoke(ProceedingJoinPoint joinPoint) throws Throwable { String methodName = joinPoint.getSignature().getName(); if (lastInvokeMap.containsKey(methodName)) { long lastInvokeTime = lastInvokeMap.get(methodName); long currentTime = System.currentTimeMillis(); if (currentTime - lastInvokeTime < 5000) { // 限制5秒内不能重复调用 throw new RuntimeException("不能重复调用" + methodName); } } lastInvokeMap.put(methodName, System.currentTimeMillis()); return joinPoint.proceed(); } } ``` 在上述代码,我们使用`ConcurrentHashMap`来存储方法名和上一次调用时间的映射关系。在`checkDuplicateInvoke`方法,我们首先判断该方法是否已经在`lastInvokeMap`存在,如果存在则判断距离上一次调用的时间是否已经超过了5秒,如果没有超过则抛出异常,否则将当前时间更新为上一次调用时间,并继续执行原有的方法逻辑。 最后,我们需要在Spring配置文件启用该切面类: ``` <aop:aspectj-autoproxy/> <bean id="duplicateInvokeAspect" class="com.example.DuplicateInvokeAspect"/> ``` 这样就完成了接口重复调用拦截功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值