一、性能阐明
SpringBoot的定时工作的增强工具,实现对SpringBoot原生的定时工作进行动静治理,齐全兼容原生@Scheduled注解,无需对本来的定时工作进行批改
二、疾速应用
具体的性能曾经封装成SpringBoot-starter即插即用:
<dependency> <groupId>com.github.guoyixing</groupId> <artifactId>spring-boot-starter-super-scheduled</artifactId> <version>0.3.1</version></dependency>
应用办法和源码:
- 码云:https://gitee.com/qiaodaimadewangcai/super-scheduled
- github:https://github.com/guoyixing/super-scheduled
举荐一个开源收费的 Spring Boot 实战我的项目:
https://github.com/javastacks/spring-boot-best-practice
三、实现原理
1、动静治理实现
(1) 配置管理介绍
@Component("superScheduledConfig")public class SuperScheduledConfig { /** * 执行定时工作的线程池 */ private ThreadPoolTaskScheduler taskScheduler; /** * 定时工作名称与定时工作回调钩子 的关联关系容器 */ private Map<String, ScheduledFuture> nameToScheduledFuture = new ConcurrentHashMap<>(); /** * 定时工作名称与定时工作须要执行的逻辑 的关联关系容器 */ private Map<String, Runnable> nameToRunnable = new ConcurrentHashMap<>(); /** * 定时工作名称与定时工作的源信息 的关联关系容器 */ private Map<String, ScheduledSource> nameToScheduledSource = new ConcurrentHashMap<>(); /* 一般的get/sets省略 */}
(2) 应用后处理器拦挡SpringBoot本来的定时工作
- 实现
ApplicationContextAware
接口拿到SpringBoot的上下文 - 实现
BeanPostProcessor
接口,将这个类标记为后处理器,后处理器会在每个bean实例化之后执行 - 应用
@DependsOn
注解强制依赖SuperScheduledConfig
类,让SpringBoot实例化SuperScheduledPostProcessor
类之前先实例化SuperScheduledConfig
类 - 次要实现逻辑在
postProcessAfterInitialization()
办法中
@DependsOn({"superScheduledConfig"})@Component@Orderpublic class SuperScheduledPostProcessor implements BeanPostProcessor, ApplicationContextAware { protected final Log logger = LogFactory.getLog(getClass()); private ApplicationContext applicationContext; /** * 实例化bean之前的操作 * @param bean bean实例 * @param beanName bean的Name */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } /** * 实例化bean之后的操作 * @param bean bean实例 * @param beanName bean的Name */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //1.获取配置管理器 SuperScheduledConfig superScheduledConfig = applicationContext.getBean(SuperScheduledConfig.class); //2.获取以后实例化实现的bean的所有办法 Method[] methods = bean.getClass().getDeclaredMethods(); //循环解决对每个办法逐个解决 if (methods.length > 0) { for (Method method : methods) { //3.尝试在该办法上获取@Scheduled注解(SpringBoot的定时工作注解) Scheduled annotation = method.getAnnotation(Scheduled.class); //如果无奈获取到@Scheduled注解,就跳过这个办法 if (annotation == null) { continue; } //4.创立定时工作的源属性 //创立定时工作的源属性(用来记录定时工作的配置,初始化的时候记录的是注解上本来的属性) ScheduledSource scheduledSource = new ScheduledSource(annotation, method, bean); //对注解上获取到源属性中的属性进行检测 if (!scheduledSource.check()) { throw new SuperScheduledException("在" + beanName + "Bean中" + method.getName() + "办法的注解参数谬误"); } //生成定时工作的名称(id),应用beanName+“.”+办法名 String name = beanName + "." + method.getName(); //将以key-value的模式,将源数据存入配置管理器中,key:定时工作的名称 value:源数据 superScheduledConfig.addScheduledSource(name, scheduledSource); try { //5.将本来SpringBoot的定时工作勾销掉 clearOriginalScheduled(annotation); } catch (Exception e) { throw new SuperScheduledException("在敞开原始办法" + beanName + method.getName() + "时呈现谬误"); } } } //最初bean放弃原有返回 return bean; } /** * 批改注解原先的属性 * @param annotation 注解实例对象 * @throws Exception */ private void clearOriginalScheduled(Scheduled annotation) throws Exception { changeAnnotationValue(annotation, "cron", Scheduled.CRON_DISABLED); changeAnnotationValue(annotation, "fixedDelay", -1L); changeAnnotationValue(annotation, "fixedDelayString", ""); changeAnnotationValue(annotation, "fixedRate", -1L); changeAnnotationValue(annotation, "fixedRateString", ""); changeAnnotationValue(annotation, "initialDelay", -1L); changeAnnotationValue(annotation, "initialDelayString", ""); } /** * 获取SpringBoot的上下文 * @param applicationContext SpringBoot的上下文 */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}
(3) 应用ApplicationRunner
初始化自定义的定时工作运行器
- 实现
ApplicationContextAware
接口拿到SpringBoot的上下文 - 应用
@DependsOn
注解强制依赖threadPoolTaskScheduler
类 - 实现
ApplicationRunner
接口,在所有bean初始化完结之后,运行自定义逻辑 - 次要实现逻辑在
run()
办法中
@DependsOn("threadPoolTaskScheduler")@Componentpublic class SuperScheduledApplicationRunner implements ApplicationRunner, ApplicationContextAware { protected final Log logger = LogFactory.getLog(getClass()); private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private ApplicationContext applicationContext; /** * 定时工作配置管理器 */ @Autowired private SuperScheduledConfig superScheduledConfig; /** * 定时工作执行线程 */ @Autowired private ThreadPoolTaskScheduler threadPoolTaskScheduler; @Override public void run(ApplicationArguments args) { //1.定时工作配置管理器中缓存 定时工作执行线程 superScheduledConfig.setTaskScheduler(threadPoolTaskScheduler); //2.获取所有定时工作源数据 Map<String, ScheduledSource> nameToScheduledSource = superScheduledConfig.getNameToScheduledSource(); //逐个解决定时工作 for (String name : nameToScheduledSource.keySet()) { //3.获取定时工作源数据 ScheduledSource scheduledSource = nameToScheduledSource.get(name); //4.获取所有加强类 String[] baseStrengthenBeanNames = applicationContext.getBeanNamesForType(BaseStrengthen.class); //5.创立执行控制器 SuperScheduledRunnable runnable = new SuperScheduledRunnable(); //配置执行控制器 runnable.setMethod(scheduledSource.getMethod()); runnable.setBean(scheduledSource.getBean()); //6.逐个解决加强类(增强器实现原理前面具体分析) List<Point> points = new ArrayList<>(baseStrengthenBeanNames.length); for (String baseStrengthenBeanName : baseStrengthenBeanNames) { //7.将增强器代理成point Object baseStrengthenBean = applicationContext.getBean(baseStrengthenBeanName); //创立代理 Point proxy = ProxyUtils.getInstance(Point.class, new RunnableBaseInterceptor(baseStrengthenBean, runnable)); proxy.setSuperScheduledName(name); //8.所有的points连成起来 points.add(proxy); } //将point造成调用链 runnable.setChain(new Chain(points)); //将执行逻辑封装并缓存到定时工作配置管理器中 superScheduledConfig.addRunnable(name, runnable::invoke); try { //8.启动定时工作 ScheduledFuture<?> schedule = ScheduledFutureFactory.create(threadPoolTaskScheduler , scheduledSource, runnable::invoke); //将线程回调钩子存到工作配置管理器中 superScheduledConfig.addScheduledFuture(name, schedule); logger.info(df.format(LocalDateTime.now()) + "工作" + name + "曾经启动..."); } catch (Exception e) { throw new SuperScheduledException("工作" + name + "启动失败,错误信息:" + e.getLocalizedMessage()); } } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}
(4) 进行动静治理
@Componentpublic class SuperScheduledManager { protected final Log logger = LogFactory.getLog(getClass()); private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Autowired private SuperScheduledConfig superScheduledConfig; /** * 批改Scheduled的执行周期 * * @param name scheduled的名称 * @param cron cron表达式 */ public void setScheduledCron(String name, String cron) { //终止原先的工作 cancelScheduled(name); //创立新的工作 ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name); scheduledSource.clear(); scheduledSource.setCron(cron); addScheduled(name, scheduledSource); } /** * 批改Scheduled的fixedDelay * * @param name scheduled的名称 * @param fixedDelay 上一次执行结束工夫点之后多长时间再执行 */ public void setScheduledFixedDelay(String name, Long fixedDelay) { //终止原先的工作 cancelScheduled(name); //创立新的工作 ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name); scheduledSource.clear(); scheduledSource.setFixedDelay(fixedDelay); addScheduled(name, scheduledSource); } /** * 批改Scheduled的fixedRate * * @param name scheduled的名称 * @param fixedRate 上一次开始执行之后多长时间再执行 */ public void setScheduledFixedRate(String name, Long fixedRate) { //终止原先的工作 cancelScheduled(name); //创立新的工作 ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name); scheduledSource.clear(); scheduledSource.setFixedRate(fixedRate); addScheduled(name, scheduledSource); } /** * 查问所有启动的Scheduled */ public List<String> getRunScheduledName() { Set<String> names = superScheduledConfig.getNameToScheduledFuture().keySet(); return new ArrayList<>(names); } /** * 查问所有的Scheduled */ public List<String> getAllSuperScheduledName() { Set<String> names = superScheduledConfig.getNameToRunnable().keySet(); return new ArrayList<>(names); } /** * 终止Scheduled * * @param name scheduled的名称 */ public void cancelScheduled(String name) { ScheduledFuture scheduledFuture = superScheduledConfig.getScheduledFuture(name); scheduledFuture.cancel(true); superScheduledConfig.removeScheduledFuture(name); logger.info(df.format(LocalDateTime.now()) + "工作" + name + "曾经终止..."); } /** * 启动Scheduled * * @param name scheduled的名称 * @param scheduledSource 定时工作的源信息 */ public void addScheduled(String name, ScheduledSource scheduledSource) { if (getRunScheduledName().contains(name)) { throw new SuperScheduledException("定时工作" + name + "曾经被启动过了"); } if (!scheduledSource.check()) { throw new SuperScheduledException("定时工作" + name + "源数据内容谬误"); } scheduledSource.refreshType(); Runnable runnable = superScheduledConfig.getRunnable(name); ThreadPoolTaskScheduler taskScheduler = superScheduledConfig.getTaskScheduler(); ScheduledFuture<?> schedule = ScheduledFutureFactory.create(taskScheduler, scheduledSource, runnable); logger.info(df.format(LocalDateTime.now()) + "工作" + name + "曾经启动..."); superScheduledConfig.addScheduledSource(name, scheduledSource); superScheduledConfig.addScheduledFuture(name, schedule); } /** * 以cron类型启动Scheduled * * @param name scheduled的名称 * @param cron cron表达式 */ public void addCronScheduled(String name, String cron) { ScheduledSource scheduledSource = new ScheduledSource(); scheduledSource.setCron(cron); addScheduled(name, scheduledSource); } /** * 以fixedDelay类型启动Scheduled * * @param name scheduled的名称 * @param fixedDelay 上一次执行结束工夫点之后多长时间再执行 * @param initialDelay 第一次执行的延迟时间 */ public void addFixedDelayScheduled(String name, Long fixedDelay, Long... initialDelay) { ScheduledSource scheduledSource = new ScheduledSource(); scheduledSource.setFixedDelay(fixedDelay); if (initialDelay != null && initialDelay.length == 1) { scheduledSource.setInitialDelay(initialDelay[0]); } else if (initialDelay != null && initialDelay.length > 1) { throw new SuperScheduledException("第一次执行的延迟时间只能传入一个参数"); } addScheduled(name, scheduledSource); } /** * 以fixedRate类型启动Scheduled * * @param name scheduled的名称 * @param fixedRate 上一次开始执行之后多长时间再执行 * @param initialDelay 第一次执行的延迟时间 */ public void addFixedRateScheduled(String name, Long fixedRate, Long... initialDelay) { ScheduledSource scheduledSource = new ScheduledSource(); scheduledSource.setFixedRate(fixedRate); if (initialDelay != null && initialDelay.length == 1) { scheduledSource.setInitialDelay(initialDelay[0]); } else if (initialDelay != null && initialDelay.length > 1) { throw new SuperScheduledException("第一次执行的延迟时间只能传入一个参数"); } addScheduled(name, scheduledSource); } /** * 手动执行一次工作 * * @param name scheduled的名称 */ public void runScheduled(String name) { Runnable runnable = superScheduledConfig.getRunnable(name); runnable.run(); }}
2、加强接口实现
增强器实现的整体思路与SpringAop的思路统一,实现没有Aop简单
(1) 加强接口
@Order(Ordered.HIGHEST_PRECEDENCE)public interface BaseStrengthen { /** * 前置强化办法 * * @param bean bean实例(或者是被代理的bean) * @param method 执行的办法对象 * @param args 办法参数 */ void before(Object bean, Method method, Object[] args); /** * 后置强化办法 * 出现异常不会执行 * 如果未出现异常,在afterFinally办法之后执行 * * @param bean bean实例(或者是被代理的bean) * @param method 执行的办法对象 * @param args 办法参数 */ void after(Object bean, Method method, Object[] args); /** * 异样强化办法 * * @param bean bean实例(或者是被代理的bean) * @param method 执行的办法对象 * @param args 办法参数 */ void exception(Object bean, Method method, Object[] args); /** * Finally强化办法,出现异常也会执行 * * @param bean bean实例(或者是被代理的bean) * @param method 执行的办法对象 * @param args 办法参数 */ void afterFinally(Object bean, Method method, Object[] args);}
(2) 代理抽象类
public abstract class Point { /** * 定时工作名 */ private String superScheduledName; /** * 形象的执行办法,应用代理实现 * @param runnable 定时工作执行器 */ public abstract Object invoke(SuperScheduledRunnable runnable); /* 一般的get/sets省略 */}
(3) 调用链类
public class Chain { private List<Point> list; private int index = -1; /** * 索引自增1 */ public int incIndex() { return ++index; } /** * 索引还原 */ public void resetIndex() { this.index = -1; }}
(4) cglib动静代理实现
应用cglib代理增强器,将增强器全副代理成调用链节点Point
public class RunnableBaseInterceptor implements MethodInterceptor { /** * 定时工作执行器 */ private SuperScheduledRunnable runnable; /** * 定时工作加强类 */ private BaseStrengthen strengthen; @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object result; //如果执行的是invoke()办法 if ("invoke".equals(method.getName())) { //前置强化办法 strengthen.before(obj, method, args); try { //调用执行器中的invoke()办法 result = runnable.invoke(); } catch (Exception e) { //异样强化办法 strengthen.exception(obj, method, args); throw new SuperScheduledException(strengthen.getClass() + "中强化执行时产生谬误", e); } finally { //Finally强化办法,出现异常也会执行 strengthen.afterFinally(obj, method, args); } //后置强化办法 strengthen.after(obj, method, args); } else { //间接执行办法 result = methodProxy.invokeSuper(obj, args); } return result; } public RunnableBaseInterceptor(Object object, SuperScheduledRunnable runnable) { this.runnable = runnable; if (BaseStrengthen.class.isAssignableFrom(object.getClass())) { this.strengthen = (BaseStrengthen) object; } else { throw new SuperScheduledException(object.getClass() + "对象不是BaseStrengthen类型"); } } public RunnableBaseInterceptor() { }}
(5) 定时工作执行器实现
public class SuperScheduledRunnable { /** * 原始的办法 */ private Method method; /** * 办法所在的bean */ private Object bean; /** * 增强器的调用链 */ private Chain chain; public Object invoke() { Object result; //索引自增1 if (chain.incIndex() == chain.getList().size()) { //调用链中的加强办法曾经全副执行完结 try { //调用链索引初始化 chain.resetIndex(); //增强器全副执行结束,执行本来的办法 result = method.invoke(bean); } catch (IllegalAccessException | InvocationTargetException e) { throw new SuperScheduledException(e.getLocalizedMessage()); } } else { //获取被代理后的办法增强器 Point point = chain.getList().get(chain.getIndex()); //执行增强器代理 //增强器代理中,会回调办法执行器,造成调用链,逐个运行调用链中的增强器 result = point.invoke(this); } return result; } /* 一般的get/sets省略 */}
(6) 增强器代理逻辑
com.gyx.superscheduled.core.SuperScheduledApplicationRunner
类中的代码片段
//创立执行控制器SuperScheduledRunnable runnable = new SuperScheduledRunnable();runnable.setMethod(scheduledSource.getMethod());runnable.setBean(scheduledSource.getBean());//用来寄存 增强器的代理对象List<Point> points = new ArrayList<>(baseStrengthenBeanNames.length);//循环所有的增强器的beanNamefor (String baseStrengthenBeanName : baseStrengthenBeanNames) { //获取增强器的bean对象 Object baseStrengthenBean = applicationContext.getBean(baseStrengthenBeanName); //将增强器代理成Point节点 Point proxy = ProxyUtils.getInstance(Point.class, new RunnableBaseInterceptor(baseStrengthenBean, runnable)); proxy.setSuperScheduledName(name); //增强器的代理对象缓存到list中 points.add(proxy);}//将增强器代理实例的汇合生成调用链//执行控制器中设置调用链runnable.setChain(new Chain(points));
版权申明:本文为CSDN博主「敲代码的旺财」的原创文章,遵循CC 4.0 BY-SA版权协定,转载请附上原文出处链接及本申明。
原文链接:https://blog.csdn.net/qq_34886352/article/details/106494637
近期热文举荐:
1.1,000+ 道 Java面试题及答案整顿(2022最新版)
2.劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4.别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!
5.《Java开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞+转发哦!