免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
SpringBoot 定時任務(wù)動態(tài)管理通用解決方案

來源:blog.csdn.net/qq_34886352/article/details/106494637


    一、功能說明

    SpringBoot的定時任務(wù)的加強(qiáng)工具,實現(xiàn)對SpringBoot原生的定時任務(wù)進(jìn)行動態(tài)管理,完全兼容原生@Scheduled注解,無需對原本的定時任務(wù)進(jìn)行修改

    推薦下自己做的 Spring Boot 的實戰(zhàn)項目:

    https://github.com/YunaiV/ruoyi-vue-pro

    二、快速使用

    具體的功能已經(jīng)封裝成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 Cloud 的實戰(zhàn)項目:

    https://github.com/YunaiV/onemall

    三、實現(xiàn)原理

    1、動態(tài)管理實現(xiàn)

    (1) 配置管理介紹

    @Component("superScheduledConfig")
    public class SuperScheduledConfig {
        /**
         * 執(zhí)行定時任務(wù)的線程池
         */

        private ThreadPoolTaskScheduler taskScheduler;

        /**
         * 定時任務(wù)名稱與定時任務(wù)回調(diào)鉤子  的關(guān)聯(lián)關(guān)系容器
         */

        private Map<String, ScheduledFuture> nameToScheduledFuture = new ConcurrentHashMap<>();

        /**
         * 定時任務(wù)名稱與定時任務(wù)需要執(zhí)行的邏輯  的關(guān)聯(lián)關(guān)系容器
         */

        private Map<String, Runnable> nameToRunnable = new ConcurrentHashMap<>();

        /**
         * 定時任務(wù)名稱與定時任務(wù)的源信息  的關(guān)聯(lián)關(guān)系容器
         */

        private Map<String, ScheduledSource> nameToScheduledSource = new ConcurrentHashMap<>();
     /* 普通的get/sets省略 */
    }

    (2) 使用后處理器攔截SpringBoot原本的定時任務(wù)

    • 實現(xiàn)ApplicationContextAware接口拿到SpringBoot的上下文
    • 實現(xiàn)BeanPostProcessor接口,將這個類標(biāo)記為后處理器,后處理器會在每個bean實例化之后執(zhí)行
    • 使用@DependsOn注解強(qiáng)制依賴SuperScheduledConfig類,讓SpringBoot實例化SuperScheduledPostProcessor類之前先實例化SuperScheduledConfig類
    • 主要實現(xiàn)邏輯在postProcessAfterInitialization()方法中
    @DependsOn({"superScheduledConfig"})
    @Component
    @Order
    public class SuperScheduledPostProcessor implements BeanPostProcessorApplicationContextAware {
        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.獲取當(dāng)前實例化完成的bean的所有方法
            Method[] methods = bean.getClass().getDeclaredMethods();
            //循環(huán)處理對每個方法逐一處理
            if (methods.length > 0) {
                for (Method method : methods) {
                 //3.嘗試在該方法上獲取@Scheduled注解(SpringBoot的定時任務(wù)注解)
                    Scheduled annotation = method.getAnnotation(Scheduled.class);
                    //如果無法獲取到@Scheduled注解,就跳過這個方法
                    if (annotation == null) {
                        continue;
                    }
                    //4.創(chuàng)建定時任務(wù)的源屬性
                    //創(chuàng)建定時任務(wù)的源屬性(用來記錄定時任務(wù)的配置,初始化的時候記錄的是注解上原本的屬性)
                    ScheduledSource scheduledSource = new ScheduledSource(annotation, method, bean);
                    //對注解上獲取到源屬性中的屬性進(jìn)行檢測
                    if (!scheduledSource.check()) {
                        throw new SuperScheduledException("在" + beanName + "Bean中" + method.getName() + "方法的注解參數(shù)錯誤");
                    }
                    //生成定時任務(wù)的名稱(id),使用beanName+“.”+方法名
                    String name = beanName + "." + method.getName();
                    //將以key-value的形式,將源數(shù)據(jù)存入配置管理器中,key:定時任務(wù)的名稱 value:源數(shù)據(jù)
                    superScheduledConfig.addScheduledSource(name, scheduledSource);
                    try {
                     //5.將原本SpringBoot的定時任務(wù)取消掉
                        clearOriginalScheduled(annotation);
                    } catch (Exception e) {
                        throw new SuperScheduledException("在關(guān)閉原始方法" + beanName + method.getName() + "時出現(xiàn)錯誤");
                    }
                }
            }
            //最后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初始化自定義的定時任務(wù)運行器

    • 實現(xiàn)ApplicationContextAware接口拿到SpringBoot的上下文
    • 使用@DependsOn注解強(qiáng)制依賴threadPoolTaskScheduler類
    • 實現(xiàn)ApplicationRunner接口,在所有bean初始化結(jié)束之后,運行自定義邏輯
    • 主要實現(xiàn)邏輯在run()方法中
    @DependsOn("threadPoolTaskScheduler")
    @Component
    public class SuperScheduledApplicationRunner implements ApplicationRunnerApplicationContextAware {
        protected final Log logger = LogFactory.getLog(getClass());
        private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        private ApplicationContext applicationContext;
     
     /**
         * 定時任務(wù)配置管理器
         */

        @Autowired
        private SuperScheduledConfig superScheduledConfig;
        /**
         * 定時任務(wù)執(zhí)行線程
         */

        @Autowired
        private ThreadPoolTaskScheduler threadPoolTaskScheduler;

        @Override
        public void run(ApplicationArguments args) {
         //1.定時任務(wù)配置管理器中緩存  定時任務(wù)執(zhí)行線程
            superScheduledConfig.setTaskScheduler(threadPoolTaskScheduler);
            //2.獲取所有定時任務(wù)源數(shù)據(jù)
            Map<String, ScheduledSource> nameToScheduledSource = superScheduledConfig.getNameToScheduledSource();
            //逐一處理定時任務(wù)
            for (String name : nameToScheduledSource.keySet()) {
                //3.獲取定時任務(wù)源數(shù)據(jù)
                ScheduledSource scheduledSource = nameToScheduledSource.get(name);
                //4.獲取所有增強(qiáng)類
                String[] baseStrengthenBeanNames = applicationContext.getBeanNamesForType(BaseStrengthen.class);
                //5.創(chuàng)建執(zhí)行控制器
                SuperScheduledRunnable runnable = new SuperScheduledRunnable();
                //配置執(zhí)行控制器
                runnable.setMethod(scheduledSource.getMethod());
                runnable.setBean(scheduledSource.getBean());
                //6.逐一處理增強(qiáng)類(增強(qiáng)器實現(xiàn)原理后面具體分析)
                List<Point> points = new ArrayList<>(baseStrengthenBeanNames.length);
                for (String baseStrengthenBeanName : baseStrengthenBeanNames) {
                 //7.將增強(qiáng)器代理成point
                    Object baseStrengthenBean = applicationContext.getBean(baseStrengthenBeanName);
                    //創(chuàng)建代理
                    Point proxy = ProxyUtils.getInstance(Point.classnew RunnableBaseInterceptor(baseStrengthenBeanrunnable));
                    proxy.setSuperScheduledName(name);
                    //8.所有的points連成起來
                    points.add(proxy);
                }
       //將point形成調(diào)用鏈
                runnable.setChain(new Chain(points));
                //將執(zhí)行邏輯封裝并緩存到定時任務(wù)配置管理器中
                superScheduledConfig.addRunnable(name, runnable::invoke);
                try {
                 //8.啟動定時任務(wù)
                    ScheduledFuture<?> schedule = ScheduledFutureFactory.create(threadPoolTaskScheduler
                            , scheduledSource, runnable::invoke);
                    //將線程回調(diào)鉤子存到任務(wù)配置管理器中
                    superScheduledConfig.addScheduledFuture(name, schedule);
                    logger.info(df.format(LocalDateTime.now()) + "任務(wù)" + name + "已經(jīng)啟動...");

                } catch (Exception e) {
                    throw new SuperScheduledException("任務(wù)" + name + "啟動失敗,錯誤信息:" + e.getLocalizedMessage());
                }
            }
        }

        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }

    (4) 進(jìn)行動態(tài)管理

    @Component
    public 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的執(zhí)行周期
         *
         * @param name scheduled的名稱
         * @param cron cron表達(dá)式
         */

        public void setScheduledCron(String name, String cron) {
            //終止原先的任務(wù)
            cancelScheduled(name);
            //創(chuàng)建新的任務(wù)
            ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name);
            scheduledSource.clear();
            scheduledSource.setCron(cron);
            addScheduled(name, scheduledSource);
        }

        /**
         * 修改Scheduled的fixedDelay
         *
         * @param name       scheduled的名稱
         * @param fixedDelay 上一次執(zhí)行完畢時間點之后多長時間再執(zhí)行
         */

        public void setScheduledFixedDelay(String name, Long fixedDelay) {
            //終止原先的任務(wù)
            cancelScheduled(name);
            //創(chuàng)建新的任務(wù)
            ScheduledSource scheduledSource = superScheduledConfig.getScheduledSource(name);
            scheduledSource.clear();
            scheduledSource.setFixedDelay(fixedDelay);
            addScheduled(name, scheduledSource);
        }

        /**
         * 修改Scheduled的fixedRate
         *
         * @param name      scheduled的名稱
         * @param fixedRate 上一次開始執(zhí)行之后多長時間再執(zhí)行
         */

        public void setScheduledFixedRate(String name, Long fixedRate) {
            //終止原先的任務(wù)
            cancelScheduled(name);
            //創(chuàng)建新的任務(wù)
            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()) + "任務(wù)" + name + "已經(jīng)終止...");
        }

        /**
         * 啟動Scheduled
         *
         * @param name            scheduled的名稱
         * @param scheduledSource 定時任務(wù)的源信息
         */

        public void addScheduled(String name, ScheduledSource scheduledSource) {
            if (getRunScheduledName().contains(name)) {
                throw new SuperScheduledException("定時任務(wù)" + name + "已經(jīng)被啟動過了");
            }
            if (!scheduledSource.check()) {
                throw new SuperScheduledException("定時任務(wù)" + name + "源數(shù)據(jù)內(nèi)容錯誤");
            }

            scheduledSource.refreshType();

            Runnable runnable = superScheduledConfig.getRunnable(name);
            ThreadPoolTaskScheduler taskScheduler = superScheduledConfig.getTaskScheduler();


            ScheduledFuture<?> schedule = ScheduledFutureFactory.create(taskScheduler, scheduledSource, runnable);
            logger.info(df.format(LocalDateTime.now()) + "任務(wù)" + name + "已經(jīng)啟動...");

            superScheduledConfig.addScheduledSource(name, scheduledSource);
            superScheduledConfig.addScheduledFuture(name, schedule);
        }

        /**
         * 以cron類型啟動Scheduled
         *
         * @param name scheduled的名稱
         * @param cron cron表達(dá)式
         */

        public void addCronScheduled(String name, String cron) {
            ScheduledSource scheduledSource = new ScheduledSource();
            scheduledSource.setCron(cron);

            addScheduled(name, scheduledSource);
        }

        /**
         * 以fixedDelay類型啟動Scheduled
         *
         * @param name         scheduled的名稱
         * @param fixedDelay   上一次執(zhí)行完畢時間點之后多長時間再執(zhí)行
         * @param initialDelay 第一次執(zhí)行的延遲時間
         */

        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("第一次執(zhí)行的延遲時間只能傳入一個參數(shù)");
            }

            addScheduled(name, scheduledSource);
        }

        /**
         * 以fixedRate類型啟動Scheduled
         *
         * @param name         scheduled的名稱
         * @param fixedRate    上一次開始執(zhí)行之后多長時間再執(zhí)行
         * @param initialDelay 第一次執(zhí)行的延遲時間
         */

        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("第一次執(zhí)行的延遲時間只能傳入一個參數(shù)");
            }

            addScheduled(name, scheduledSource);
        }

        /**
         * 手動執(zhí)行一次任務(wù)
         *
         * @param name scheduled的名稱
         */

        public void runScheduled(String name) {
            Runnable runnable = superScheduledConfig.getRunnable(name);
            runnable.run();
        }
    }

    2、增強(qiáng)接口實現(xiàn)

    增強(qiáng)器實現(xiàn)的整體思路與SpringAop的思路一致,實現(xiàn)沒有Aop復(fù)雜

    (1) 增強(qiáng)接口

    @Order(Ordered.HIGHEST_PRECEDENCE)
    public interface BaseStrengthen {
        /**
         * 前置強(qiáng)化方法
         *
         * @param bean   bean實例(或者是被代理的bean)
         * @param method 執(zhí)行的方法對象
         * @param args   方法參數(shù)
         */

        void before(Object bean, Method method, Object[] args);

        /**
         * 后置強(qiáng)化方法
         * 出現(xiàn)異常不會執(zhí)行
         * 如果未出現(xiàn)異常,在afterFinally方法之后執(zhí)行
         *
         * @param bean   bean實例(或者是被代理的bean)
         * @param method 執(zhí)行的方法對象
         * @param args   方法參數(shù)
         */

        void after(Object bean, Method method, Object[] args);

        /**
         * 異常強(qiáng)化方法
         *
         * @param bean   bean實例(或者是被代理的bean)
         * @param method 執(zhí)行的方法對象
         * @param args   方法參數(shù)
         */

        void exception(Object bean, Method method, Object[] args);

        /**
         * Finally強(qiáng)化方法,出現(xiàn)異常也會執(zhí)行
         *
         * @param bean   bean實例(或者是被代理的bean)
         * @param method 執(zhí)行的方法對象
         * @param args   方法參數(shù)
         */

        void afterFinally(Object bean, Method method, Object[] args);
    }

    (2) 代理抽象類

    public abstract class Point {
        /**
         * 定時任務(wù)名
         */

        private String superScheduledName;

        /**
         * 抽象的執(zhí)行方法,使用代理實現(xiàn)
         * @param runnable 定時任務(wù)執(zhí)行器
         */

        public abstract Object invoke(SuperScheduledRunnable runnable);
        
        /* 普通的get/sets省略 */
    }

    (3) 調(diào)用鏈類

    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動態(tài)代理實現(xiàn)

    使用cglib代理增強(qiáng)器,將增強(qiáng)器全部代理成調(diào)用鏈節(jié)點Point

    public class RunnableBaseInterceptor implements MethodInterceptor {
        /**
         * 定時任務(wù)執(zhí)行器
         */

        private SuperScheduledRunnable runnable;
        /**
         * 定時任務(wù)增強(qiáng)類
         */

        private BaseStrengthen strengthen;

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object result;
            //如果執(zhí)行的是invoke()方法
            if ("invoke".equals(method.getName())) {
             //前置強(qiáng)化方法
                strengthen.before(obj, method, args);
                try {
                 //調(diào)用執(zhí)行器中的invoke()方法
                    result = runnable.invoke();
                } catch (Exception e) {
                 //異常強(qiáng)化方法
                    strengthen.exception(obj, method, args);
                    throw new SuperScheduledException(strengthen.getClass() + "中強(qiáng)化執(zhí)行時發(fā)生錯誤", e);
                } finally {
                 //Finally強(qiáng)化方法,出現(xiàn)異常也會執(zhí)行
                    strengthen.afterFinally(obj, method, args);
                }
                //后置強(qiáng)化方法
                strengthen.after(obj, method, args);

            } else {
             //直接執(zhí)行方法
                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) 定時任務(wù)執(zhí)行器實現(xiàn)

    public class SuperScheduledRunnable {
        /**
         * 原始的方法
         */

        private Method method;
        /**
         * 方法所在的bean
         */

        private Object bean;
        /**
         * 增強(qiáng)器的調(diào)用鏈
         */

        private Chain chain;


        public Object invoke() {
            Object result;
            //索引自增1
            if (chain.incIndex() == chain.getList().size()) {
                //調(diào)用鏈中的增強(qiáng)方法已經(jīng)全部執(zhí)行結(jié)束
                try {
                    //調(diào)用鏈索引初始化
                    chain.resetIndex();
                    //增強(qiáng)器全部執(zhí)行完畢,執(zhí)行原本的方法
                    result = method.invoke(bean);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new SuperScheduledException(e.getLocalizedMessage());
                }
            } else {
                //獲取被代理后的方法增強(qiáng)器
                Point point = chain.getList().get(chain.getIndex());
                //執(zhí)行增強(qiáng)器代理
                //增強(qiáng)器代理中,會回調(diào)方法執(zhí)行器,形成調(diào)用鏈,逐一運行調(diào)用鏈中的增強(qiáng)器
                result = point.invoke(this);
            }
            return result;
        }
        
        /* 普通的get/sets省略 */
    }

    (6) 增強(qiáng)器代理邏輯

    com.gyx.superscheduled.core.SuperScheduledApplicationRunner類中的代碼片段

    //創(chuàng)建執(zhí)行控制器
    SuperScheduledRunnable runnable = new SuperScheduledRunnable();
    runnable.setMethod(scheduledSource.getMethod());
    runnable.setBean(scheduledSource.getBean());
    //用來存放 增強(qiáng)器的代理對象
    List<Point> points = new ArrayList<>(baseStrengthenBeanNames.length);
    //循環(huán)所有的增強(qiáng)器的beanName
    for (String baseStrengthenBeanName : baseStrengthenBeanNames) {
     //獲取增強(qiáng)器的bean對象
        Object baseStrengthenBean = applicationContext.getBean(baseStrengthenBeanName);
        //將增強(qiáng)器代理成Point節(jié)點
        Point proxy = ProxyUtils.getInstance(Point.classnew RunnableBaseInterceptor(baseStrengthenBeanrunnable));
        proxy.setSuperScheduledName(name);
        //增強(qiáng)器的代理對象緩存到list中
        points.add(proxy);
    }
    //將增強(qiáng)器代理實例的集合生成調(diào)用鏈
    //執(zhí)行控制器中設(shè)置調(diào)用鏈
    runnable.setChain(new Chain(points));

    本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
    打開APP,閱讀全文并永久保存 查看更多類似文章
    猜你喜歡
    類似文章
    Java構(gòu)造和解析Json數(shù)據(jù)的兩種方法詳解二
    SSH實現(xiàn)的增刪改查實例
    Spring 定時任務(wù)
    徹底理解spring的定制任務(wù)(scheduling)
    線程池之 ScheduledThreadPoolExecutor中scheduleAtFixedRate執(zhí)行定時任務(wù)
    Quartz實現(xiàn)動態(tài)定時任務(wù)
    更多類似文章 >>
    生活服務(wù)
    分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
    綁定賬號成功
    后續(xù)可登錄賬號暢享VIP特權(quán)!
    如果VIP功能使用有故障,
    可點擊這里聯(lián)系客服!

    聯(lián)系客服