Spring 扩展点 - BeanPostProcessor

本文介绍了如何利用Spring的BeanPostProcessor扩展点,自定义注解@TimeDetect来监控Bean方法的执行时间。通过创建TimeDetectBeanPostProcessor和TimeDetectInterceptor,实现了在Bean初始化时进行动态代理,当方法执行时间超过预设阈值时,触发监听器进行处理。文章还展示了注解使用示例和全局配置方式,并提供了源代码链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring 扩展点 - BeanPostProcessor



前言

前面有分享过 链接: Spring如何实现AOP 的文章,里面有提到过是基于 BeanPostProcessor的前后处理实现,那么在本篇文章中我们自定义扩展 BeanPostProcessor,手动实现对Bean的代理增强。


一、目标

通过自定义的注解完成对Bean的方法进行执行时间监控,实现原理利用BeanPostProcessorBean 初始化是,采用动态代理方式进行方法拦截增强。

注解 @TimeDetect

自定义注解 @TimeDetect ,可以作用于 class 上 和 方法名上;监控方法的执行时间,可配置单位和阈值,超过执行时间,会触发监听器。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TimeDetect {
	/**
	 * 1 毫秒
	 * 2 秒
	 * 3 分
	 * 4 小时
	 */
	int timeUnit() default 1;

	/**
	 * 监控阈值,执行超过这个时间就会触发监听器
	 */
	long threshold() default 1000;
}

TimeDetectBeanPostProcessor

TimeDetectBeanPostProcessor 继承于 AbstractAutoProxyCreator ,通过重写 wrapIfNecessary 方法进行代理。

@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	try {
		//扫描bean 是否带有 TimeDetect 注解,检查是否开启全局配置
		if (!ClassUtils.hasTimeDetectAnnotation(bean) && !enbleGlobalConfig(this.timeDetectPropConfiguration, ClassUtils.findTargetClass(bean))) {
			return bean;
		}
		//如果是非代理对象,则去执行默认的代理方法
		if (!AopUtils.isAopProxy(bean)) {
			return super.wrapIfNecessary(bean, beanName, cacheKey);
		}
		//如果已经是代理对象了,则这里再次进行增强
		AdvisedSupport advisedSupport = ClassUtils.getAdvisedSupport(bean);
		if (null != advisedSupport) {
			Advisor[] advisors = this.buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null));
			for (Advisor advisor : advisors) {
				advisedSupport.addAdvisor(0,advisor);
			}
		}
		return bean;
	} catch (Exception e) {
		throw new RuntimeException(e);
	}
}

重写getAdvicesAndAdvisorsForBean方法,获取自定义通知 TimeDetectInterceper

@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException {
	return new Object[]{new TimeDetectInterceper()};
}

TimeDetectInterceper

方法拦截器,对代理对象方法进行拦截处理。监控方法执行时间,对于执行时间超过阈值的方法,通过TimeDetectEventMulticaster 广播给监听器。

public class TimeDetectInterceper implements MethodInterceptor {


	private final Logger logger = LoggerFactory.getLogger(TimeDetectInterceper.class);

	private Class getInterceperAnnotation() {
		return TimeDetect.class;
	}

	/**
	 * 代理对象进行方法增强
	 *
	 * @param invocation
	 * @return
	 * @throws Throwable
	 */
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		Class<?> target = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
		Method method = ClassUtils.getMostSpecificMethod(invocation.getMethod(), target);
		//如果注解是加载class上,则表示开启了所有方法的拦截
		Annotation annotation = AnnotationUtils.findAnnotation(target, getInterceperAnnotation());
		if (annotation != null || (annotation = method.getAnnotation(getInterceperAnnotation())) != null) {
			int timeUnit = (Integer) AnnotationUtils.getAnnotationAttributes(annotation).get("timeUnit");
			long threshold = (Long) AnnotationUtils.getAnnotationAttributes(annotation).get("threshold");
			return enhance(invocation, timeUnit, threshold);
		} else {
			//如果没有注解信息,就走全局配置
			return enhance(invocation, 1, TimeDetectGlobalConfig.getThreshold());
		}
	}

	private Object enhance(MethodInvocation invocation, int timeUnit, long threshold) throws Throwable {
		long startTime = System.currentTimeMillis();
		try {
			return invocation.proceed();
		} catch (Throwable e) {
			throw e;
		} finally {
			long endTime = System.currentTimeMillis();
			long executeTime = endTime - startTime;
			String timeFormat = TimeUtils.getTimeStr(executeTime);
			logger.debug("execute " + (AopUtils.isAopProxy(invocation.getThis()) ? AopUtils.getTargetClass(invocation.getThis()).getName() : invocation.getThis().getClass().getName()) + "." + invocation.getMethod().getName() + " time : " + timeFormat);
			//执行事件超过设定的阈值,触发事件分发
			if (TimeUtils.compare(executeTime, timeUnit, threshold) > 0) {
				logger.debug("execute method time too long , start multicastEvent");
				TimeDetectEventMulticaster.multicastEvent(new TimeDetectEvent(invocation.getThis().getClass(), invocation.getMethod(), timeFormat));
			}
		}
	}
}

TimeDetectListener

监听器,监听 TimeDetectEvent事件,处理超时方法。

public interface TimeDetectListener {
	void onListen(TimeDetectEvent event);
}

二、如何使用?

1.直接使用注解

对于Spring管理的Bean,可以直接将注解 @TimeDetect 直接加在方法上,表示该方法开启执行时间监控。

@Component
public class RunMethod implements TestMethod{

	@Autowired
	RunMethod2 runMethod2;

	@TimeDetect(threshold = 300)
	public void test1() throws InterruptedException {
		Thread.sleep(500);
	}


	public void test2() throws InterruptedException {
		Thread.sleep(2000);
		this.test1();
	}

}

也可以直接作用在 class上,开启该类的所有方法的监控。

@Component
@TimeDetect(threshold = 100)
public class RunMethod2 implements TestMethod {

	@Autowired
	RunMethod runMethod;

	public void test1() throws InterruptedException {
		Thread.sleep(1000);
	}

	public void test2() throws InterruptedException {
		Thread.sleep(2000);
	}
}

2.使用全局配置

application.yaml 中添加如下配置,无需添加注解即可开启全局监控。

time-detect:
  enable: true #是否开启配置
  basePackages: com.demo # 监控的类包路径
  threshold: 2000 # 监控的时间阈值

总结

1.通过扩展SpringBeanPostProcessor,对Bean进行代理增强。
2. @TimeDetect注解,灵活的监控每个方法执行时间。
3.TimeDetectListener,监听超时方法,自定义监听逻辑。

以上代码都放在个人github上:
项目连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值