Spring 扩展点 - BeanPostProcessor
文章目录
前言
前面有分享过 链接: Spring如何实现AOP 的文章,里面有提到过是基于 BeanPostProcessor的前后处理实现,那么在本篇文章中我们自定义扩展 BeanPostProcessor,手动实现对Bean的代理增强。
一、目标
通过自定义的注解完成对Bean的方法进行执行时间监控,实现原理利用BeanPostProcessor在Bean 初始化是,采用动态代理方式进行方法拦截增强。
注解 @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.通过扩展Spring的BeanPostProcessor,对Bean进行代理增强。
2. @TimeDetect注解,灵活的监控每个方法执行时间。
3.TimeDetectListener,监听超时方法,自定义监听逻辑。
以上代码都放在个人github上:
项目连接