范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Spring定时任务玩出花

  1. 项目概览
  我们首先来大概看下这个项目:
  这里和定时任务相关的配置主要在 config 包里边,其他的都是业务类代码,换句话说其他的都是常规的 CURD,所以我这里主要和小伙伴们介绍 config 中的代码。  2. 整体思路
  我先来说说这个项目的整体思路,这样方便大家理解下面的内容。
  在这个项目中,每一个定时任务都由一个线程去处理,负责处理每一个定时任务的线程类是 SchedulingRunnable,所有的线程都跑在一个线程池中,这个线程池是 ThreadPoolTaskScheduler,这是一个专为定时任务设计的线程池(支持 Cron 表达式),它的底层其实就是大家所熟知的 ScheduledThreadPoolExecutor。当有一个新的定时任务需要执行时,创建一个 SchedulingRunnable 线程,然后连同 Cron 表达式一起扔到 ThreadPoolTaskScheduler 池子里去执行就行了。  3. 配置分析
  几个配置类我们逐一来分析。  3.1 SpringContextUtils
  首先我们提供了一个 SpringContextUtils 工具类,这个工具类实现了 ApplicationContextAware 接口,通过这个工具类,我们可以从 Spring 容器中查询一个 Bean 或者判断 Spring 容器中是否存在某一个 Bean,工具类的代码如下(我主要列出来了有哪些方法,具体实现大家可以参考:https://github.com/lenve/scheduling):  @Component public class SpringContextUtils implements ApplicationContextAware {      private static ApplicationContext applicationContext;      @Override     public void setApplicationContext(ApplicationContext applicationContext)             throws BeansException {         SpringContextUtils.applicationContext = applicationContext;     }      public static Object getBean(String name) {     }      public static  T getBean(Class requiredType) {     }      public static  T getBean(String name, Class requiredType) {     }      public static boolean containsBean(String name) {     }      public static boolean isSingleton(String name) {     }      public static Class<? extends Object> getType(String name) {     } } 3.2 SchedulingRunnable
  将来每一个定时任务执行的时候,我们都开启一个新的线程去执行这个定时任务,SchedulingRunnable 就是关于这个线程的配置,我们来看下:  public class SchedulingRunnable implements Runnable {      private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);      private String beanName;      private String methodName;      private String params;      private Object targetBean;      private Method method;      public SchedulingRunnable(String beanName, String methodName) {         this(beanName, methodName, null);     }      public SchedulingRunnable(String beanName, String methodName, String params) {         this.beanName = beanName;         this.methodName = methodName;         this.params = params;         init();     }      private void init() {         try {             targetBean = SpringContextUtils.getBean(beanName);              if (StringUtils.hasText(params)) {                 method = targetBean.getClass().getDeclaredMethod(methodName, String.class);             } else {                 method = targetBean.getClass().getDeclaredMethod(methodName);             }              ReflectionUtils.makeAccessible(method);         } catch (NoSuchMethodException e) {             e.printStackTrace();         }     }      @Override     public void run() {         logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);         long startTime = System.currentTimeMillis();          try {             if (StringUtils.hasText(params)) {                 method.invoke(targetBean, params);             } else {                 method.invoke(targetBean);             }         } catch (Exception ex) {             logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);         }          long times = System.currentTimeMillis() - startTime;         logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times);     }      @Override     public boolean equals(Object o) {         if (this == o) return true;         if (o == null || getClass() != o.getClass()) return false;         SchedulingRunnable that = (SchedulingRunnable) o;         if (params == null) {             return beanName.equals(that.beanName) &&                     methodName.equals(that.methodName) &&                     that.params == null;         }          return beanName.equals(that.beanName) &&                 methodName.equals(that.methodName) &&                 params.equals(that.params);     }      @Override     public int hashCode() {         if (params == null) {             return Objects.hash(beanName, methodName);         }          return Objects.hash(beanName, methodName, params);     } }
  SchedulingRunnable 实现了 Runnable 接口,这里的实现逻辑也比较简单,我们一起来看下:  首先声明了 beanName、methodName 以及 params 分别作为定时任务执行的 Bean 的 bean 名称、方法名称以及方法参数。不知道小伙伴们是否记得我们上篇文章中介绍的该系统的用法,在添加一个定时任务时,我们需要传入相应的 beanName、methodName 以及 params 参数,传入后就来到这里了。另外还有 targetBean 和 method 分别表示 beanName 对应的对象以及 methodName 对应的对象,其中 targetBean 通过 beanName 从 Spring 容器中查找,method 则通过 methodName 从 targetBean 中查找。  在 run 方法中,通过反射去调用 method 方法,这也是定时任务执行时候的具体逻辑。  另外,这里重写了 equals 和 hashCode 方法,这两个方法主要是比较了 beanName、methodName 以及 params 三个属性,换言之,如果这三个属性相同,则认为这是同一个对象(这三个属性相同表示这是同一个定时任务)。  3.3 SchedulingConfig@Configuration public class SchedulingConfig {     @Bean     public TaskScheduler taskScheduler() {         ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();         taskScheduler.setPoolSize(4);         taskScheduler.setRemoveOnCancelPolicy(true);         taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");         return taskScheduler;     } }
  这里主要是配置一下 ThreadPoolTaskScheduler,这个可以很方便的对重复执行的任务进行调度管理,相比于通过 Java 自带的周期性任务线程池ScheduleThreadPoolExecutor,ThreadPoolTaskScheduler 对象支持根据 Cron 表达式创建周期性任务。
  既然是线程池,必然就有线程数量等问题,它的核心线程池大小就是我们配置的 poolSize 属性,最大线程池大小是  Integer.MAX_VALUE ,keepAliveTime  为 0 ,这里用到的队列是 DelayedWorkQueue  ,这个队列有一个属性 private final DelayQueue dq = new DelayQueue();  对这个队列的操作实际是是对这个 DelayQueue 的操作,这个队列大小是 Integer.MAX_VALUE,所以线程数量肯定是够用了。
  其他配置就没啥好说的。  3.4 ScheduledTask
  ScheduledTask 是 ScheduledFuture 的包装类,这个包装类中主要多了一个 future 属性,这个 future 属性表示 TaskScheduler 定时任务线程池的执行结果:  public final class ScheduledTask {     volatile ScheduledFuture<?> future;     public void cancel() {         ScheduledFuture<?> future = this.future;         if (future != null) {             future.cancel(true);         }     } } 3.5 CronTaskRegistrar
  核心的方法都在这个里边。  @Component public class CronTaskRegistrar implements DisposableBean {      private final Map scheduledTasks = new ConcurrentHashMap<>(16);      @Autowired     private TaskScheduler taskScheduler;      public TaskScheduler getScheduler() {         return this.taskScheduler;     }      public void addCronTask(Runnable task, String cronExpression) {         addCronTask(new CronTask(task, cronExpression));     }      public void addCronTask(CronTask cronTask) {         if (cronTask != null) {             Runnable task = cronTask.getRunnable();             if (this.scheduledTasks.containsKey(task)) {                 removeCronTask(task);             }              this.scheduledTasks.put(task, scheduleCronTask(cronTask));         }     }      public void removeCronTask(Runnable task) {         ScheduledTask scheduledTask = this.scheduledTasks.remove(task);         if (scheduledTask != null)             scheduledTask.cancel();     }      public ScheduledTask scheduleCronTask(CronTask cronTask) {         ScheduledTask scheduledTask = new ScheduledTask();         scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());          return scheduledTask;     }       @Override     public void destroy() {         for (ScheduledTask task : this.scheduledTasks.values()) {             task.cancel();         }          this.scheduledTasks.clear();     } }
  稍微说下这个类:  首先这个类实现了 DisposableBean 接口,实现这个接口就重写了 destroy 方法,以便在 Bean 销毁的时候,清除所有的定时任务。  addCronTask(Runnable, String) 方法用来添加一个定时任务,传两个参数,第一个是 Runnable,也就是我们前面所说的定时任务,第二个则是一个 Cron 表达式。  addCronTask(CronTask) 方法也用来添加定时任务,添加之前先判断这个定时任务是否已经存在,如果已经存在,就先移除。然后将定时任务存入 scheduledTasks 中,存储的时候,key 就是那个 Runnable 对象,value 则是一个 ScheduledTask 对象。  ScheduledTask 对象从 scheduleCronTask 方法中获取, 这也是整个系统最最核心的一段代码 ,调用 taskScheduler 对象把定时任务添加进去。  removeCronTask 方法用来移除一个定时任务,移除分为两部分:1. 从 scheduledTasks 集合中找到定时任务并移除;2. 取消定时任务的执行。  最后的 destroy 方法就是一个常规方法,该移除移除,该清空清空。  3.6 InitTask
  这是一个处理数据库中已有定时任务的类。当系统启动时,首先从数据库中读取需要定时执行的任务,然后挨个加入定时任务执行器中:  @Component public class InitTask implements CommandLineRunner {     @Autowired     CronTaskRegistrar cronTaskRegistrar;     @Autowired     SysJobService sysJobService;      @Override     public void run(String... args) throws Exception {         List list = sysJobService.getJobsByStatus(1);         for (SysJob sysJob : list) {             cronTaskRegistrar.addCronTask(new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(), sysJob.getMethodParams()), sysJob.getCronExpression());         }     } } 查询所有状态为 1 的定时任务。  遍历第一步查询出来的集合,添加定时任务。
  好啦,这就是整个项目最最核心的配置了,其他的代码都是一些业务层面的代码,乏善可陈,我就不啰嗦啦。  4. 定时任务怎么配
  有的小伙伴可能还不知道定时任务怎么配置,我这里稍微说两句。
  项目中提供了如下一个测试类:  @Component("schedulingTaskDemo") public class SchedulingTaskDemo {     public void taskWithParams(String params) {         System.out.println("执行有参示例任务:" + params);     }      public void taskNoParams() {         System.out.println("执行无参示例任务");     } }
  这是提前写好的,需要的时候我们配置的定时任务就是这里相关的参数,如下图:
  Bean 名称、方法名称都和测试案例中的 Bean 一一对应。  5. 小结
  好啦,是不是很 Easy?小伙伴们赶紧去尝试下吧!
  项目地址:  GitHub:https://github.com/lenve/scheduling  Gitee:https://gitee.com/lenve/scheduling  原文链接:https://mp.weixin.qq.com/s/skZ7uU7q1iH9QrV2EKiGOg
  原作者:江南一点雨

频现拼写翻译等内容错误莫让词典类APP误人子弟在手机APP上查单词背单词,已成为当下大学生中学生及英语学习爱好者的习惯,APP上的电子词典几乎取代了大部头纸质词典。但新华视点记者调查发现,不少动辄用户使用量过亿的英语学习类AP我今天也学会用支付宝了在当下这个信息飞速发展的时代,信息化以更快更便捷的方式,传递着人类创造文明的发展。同时,也给人类提供了快捷的交往手段,推动了人类生活的共同富裕和繁荣。自从有了手机后,它不仅仅是通话海雀虽小,五脏俱全华为智选HQ5S海雀AI全景摄像头体验在智能家居高速发展的今天,人们除了享受AI智能带来的方便的同时,更多人开始注重起了家庭隐私及安全问题,从而也诞生了很多保护家庭安全的科技数码产品,在没有宝宝之前,我对智能摄像头从来腾讯司庆发放NFT藏品,它到底有什么作用?记者司林威11月11日,腾讯公司为其员工发放了23周年纪念版NFT。根据界面新闻了解,腾讯官方将其称为数字藏品,由腾讯旗下NFT交易平台幻核团队设计发行,腾讯旗下联盟链至信链提供链华为手机怎么设置视频铃声?华为EMUI9。1就增加了视频铃声的功能,接下来我们来看看怎么设置视频铃声吧。1打开华为手机的设置声音,选择卡1或卡2的来电铃声进行设置。如下图所示2我们进入选择铃声的页面中,点击安防监控摄像机云储存如何开通,可以保存多少视频云储存是网络WiFi摄像机独有的功能,它是利用互联网视频数据传输到网盘,从而达到大容量存储的目的。也就是说没有WiFi的环境是使用不了云盘功能的,只能使用本地存储。安防监控摄像机的iPhone13pro和华为P50pro应该买哪个?iPhone13pro,是今年整个苹果旗舰系列比较超值的一款手机,整体配置相比于苹果13标准版升级幅度巨大,采用的是a15芯片,GPU性能提升20。但iPhone13pro的价格比需要打击电商吗?首先要搞清楚电商和实体店的区别在那些方面。我觉得最大的区别是实体店让富裕了房东,而电商是富裕了平台。电商主要是因为互联网的普及,应对市场需求顾客群体量大辐射广,利用价格战营销手段多为什么华为手机能站稳高端,别的国产手机就不能?谢邀。原因其实很简单,持续增长的科技投入和不断巩固的品牌建设。不仅手机,华为进入哪个行业都要成为第一的这股蛮劲啊,你想让它不成为高端都难。其他的为什么不能就不说了,我都没用过它们的10月新发布手机性能排行榜vivoT1上榜近日,根据多家科技媒体的消息,鲁大师数据中心公布了10月安卓新发布手机性能排行榜,数据来自鲁大师APP10。01日10。31日的数据,榜单只筛选出在这期间新发布的机型。部分新机测试iPhone13Pro最新售价确认,256GB版跌至新低价,iPhone13不香了如果购买的是普通安卓手机,使用个两年可能就会考虑更换新机了,但如果使用的是苹果手机,可能会持续使用四五年,因为长期使用的情况下,苹果手机确实要比安卓手机更为流畅,这也是不少消费者们
戴助听器可以缓解耳鸣吗?这么说吧,有部分耳鸣是听力下降引起,可以这样理解,原来大脑可以接触到耳朵传过来所有频率的声音,现在耳朵某个频段坏了,在这个频段上脑子接收不到原来应有的信号了,脑子会努力放大这一段的有什么东西是盗版的,却比正版的火的一塌糊涂?系统啊,我们的电脑系统大部分都是盗版的,包括office软件有,必须有中国刀片之王,不仅靠做山寨货打败美国企业,竟还将正品公司收购!在我们生活中,会见到形形色色的山寨货。小到食品饮微信云每年收费130元,你会使用吗?垄断后就开始收费割韭菜,和阿里一样的套路!到时候我不用了。肯定不用啊不用如果不用了,那真好,现在被微信捆的难受!随时随地的工作,各种表格满天飞,各种截屏每天发,各种任务要完成,周末安卓机必备的神级app有哪些?安卓系统真的是优于苹果的系统存在,由于其独特的代码开源性,可以运行大多数不需要审核就可以使用的软件,而苹果的软件必须要通过苹果商店的审核才可以上架。小伙伴们如果有安卓手机,如果是品小米中秋节活动915开跑,RedmiBuds3Pro降噪蓝牙耳机同步开卖小米台湾今(13)日宣布将在2021年9月15日至9月21日止,举办小米中秋节活动,并推出RedmiBuds3Pro降噪蓝牙耳机精选热门品项推出优惠活动。小米中秋节活动915开跑,永辉超市上半年亏损10。83亿,京东腾讯的投资要打水漂了吗?明着算是亏了,但背地里赚大发了。厂家的货在网上平台销售比实体店销售便宜些,毕竟省中间环节。打个比方,A货在线下店卖10元,在网上卖9块钱,你不急的情况下肯定买平台的,虽然线下大的连从碳达峰碳中和看新能源电动车一什么是碳达峰和碳中和?气候变化是人类面临的全球性问题,随着各国二氧化碳排放,温室气体猛增,对生命系统形成威胁。在这一背景下,世界各国以全球协约的方式减排温室气体,中国承诺在203全球服务器排名浪潮第3,联想第4,华为大跌46,中兴大涨52近日全球知名市场调研机构发布了2021年2季度全球服务器市场报告。按照IDC的数据,2季度全球的服务器市场下滑了2。5,市场规模约为236亿美元。而出货量约为322万台,同比增长了未来你身边的机器人朋友9月10日至13日,以共享新成果共注新动能为主题的2021世界机器人大会在北京举行。110余家企业和科研机构携500多款产品参展,集中展示在服务工业特种机器人等不同应用领域的最新产华为P50系列大揭秘这个涟漪云波,它藏了哪些奥秘?华为P系列始终在外观上引领市场潮流。这一次,华为设计师团队再次领先一步,在华为P50Pro上打造涟漪云波动态色彩。今天小侦探就带领大家,一起掀开HUAWEIP50系列涟漪云波动态色国内首次达到3200ANSI流明亮度!当贝投影X3入选2021年度产品近日,由中国视像协会5G云游戏产业联盟支持电科技网主办的大屏视界云游未来2021中国大屏云游戏生态创新峰会在北京正式举行。当贝公司旗下智能投影产品成为此次备受关注的亮点,其中当贝投