欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot動(dòng)態(tài)定時(shí)任務(wù)、動(dòng)態(tài)Bean、動(dòng)態(tài)路由詳解

 更新時(shí)間:2023年10月19日 09:15:02   作者:shirukai  
這篇文章主要介紹了SpringBoot動(dòng)態(tài)定時(shí)任務(wù)、動(dòng)態(tài)Bean、動(dòng)態(tài)路由詳解,之前用過Spring中的定時(shí)任務(wù),通過@Scheduled注解就能快速的注冊(cè)一個(gè)定時(shí)任務(wù),但有的時(shí)候,我們業(yè)務(wù)上需要?jiǎng)討B(tài)創(chuàng)建,或者根據(jù)配置文件、數(shù)據(jù)庫(kù)里的配置去創(chuàng)建定時(shí)任務(wù),需要的朋友可以參考下

1 動(dòng)態(tài)定時(shí)任務(wù)

之前用過Spring中的定時(shí)任務(wù),通過@Scheduled注解就能快速的注冊(cè)一個(gè)定時(shí)任務(wù),但有的時(shí)候,我們業(yè)務(wù)上需要?jiǎng)討B(tài)創(chuàng)建,或者根據(jù)配置文件、數(shù)據(jù)庫(kù)里的配置去創(chuàng)建定時(shí)任務(wù)。這里有兩種思路,一種是自己實(shí)現(xiàn)定時(shí)任務(wù)調(diào)度器或者第三方任務(wù)調(diào)度器如Quartz,另一種是使用Spring內(nèi)置的定時(shí)任務(wù)調(diào)度器ThreadPoolTaskScheduler,其實(shí)很簡(jiǎn)單,從IOC容器中拿到對(duì)應(yīng)的Bean,然后去注冊(cè)定時(shí)任務(wù)即可。下面以動(dòng)態(tài)管理cron任務(wù)為例介紹具體的實(shí)現(xiàn)方案。

1.1 定義CronTask實(shí)體

package org.example.dynamic.timed;

import java.util.concurrent.ScheduledFuture;

/**
 * 定時(shí)任務(wù)
 *
 * @author shirukai
 */
public class CronTask {
    private String id;
    private String cronExpression;
    private ScheduledFuture<?> future;

    private Runnable runnable;

    public String getId() {
        return id;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public ScheduledFuture<?> getFuture() {
        return future;
    }

    public Runnable getRunnable() {
        return runnable;
    }

    public void setFuture(ScheduledFuture<?> future) {
        this.future = future;
    }

    public static final class Builder {
        private String id;
        private String cronExpression;
        private ScheduledFuture<?> future;

        private Runnable runnable;

        private Builder() {
        }

        public static Builder aCronTask() {
            return new Builder();
        }

        public Builder setId(String id) {
            this.id = id;
            return this;
        }

        public Builder setCronExpression(String cronExpression) {
            this.cronExpression = cronExpression;
            return this;
        }

        public Builder setFuture(ScheduledFuture<?> future) {
            this.future = future;
            return this;
        }

        public Builder setRunnable(Runnable runnable) {
            this.runnable = runnable;
            return this;
        }

        public CronTask build() {
            CronTask cronTask = new CronTask();
            cronTask.id = this.id;
            cronTask.cronExpression = this.cronExpression;
            cronTask.future = this.future;
            cronTask.runnable = this.runnable;
            return cronTask;
        }
    }
}

1.2 實(shí)現(xiàn)動(dòng)態(tài)任務(wù)調(diào)度器

該部分主要是獲取調(diào)度器實(shí)例,然后實(shí)現(xiàn)注冊(cè)、取消、獲取列表的方法。

package org.example.dynamic.timed;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;


/**
 * 動(dòng)態(tài)定時(shí)任務(wù)調(diào)度器
 *
 * @author shirukai
 */
@Component
@EnableScheduling
public class CronTaskScheduler {
    @Autowired
    private ThreadPoolTaskScheduler scheduler;

    private final Map<String, CronTask> tasks = new ConcurrentHashMap<>(16);

    /**
     * 注冊(cè)定時(shí)任務(wù)
     *
     * @param task       任務(wù)的具體實(shí)現(xiàn)
     * @param expression cron表達(dá)式
     * @return cronTask
     */
    public CronTask register(Runnable task, String expression) {
        final CronTrigger trigger = new CronTrigger(expression);
        ScheduledFuture<?> future = scheduler.schedule(task, trigger);
        final String taskId = UUID.randomUUID().toString();
        CronTask cronTask = CronTask.Builder
                .aCronTask()
                .setId(taskId)
                .setCronExpression(expression)
                .setFuture(future)
                .setRunnable(task)
                .build();
        tasks.put(taskId, cronTask);
        return cronTask;
    }

    /**
     * 取消定時(shí)任務(wù)
     *
     * @param taskId 任務(wù)ID
     */
    public void cancel(String taskId) {
        if (tasks.containsKey(taskId)) {
            CronTask task = tasks.get(taskId);
            task.getFuture().cancel(true);
            tasks.remove(taskId);
        }
    }

    /**
     * 更新定時(shí)任務(wù)
     *
     * @param taskId     任務(wù)ID
     * @param expression cron表達(dá)式
     * @return cronTask
     */
    public CronTask update(String taskId, String expression) {
        if (tasks.containsKey(taskId)) {
            CronTask task = tasks.get(taskId);
            task.getFuture().cancel(true);
            final CronTrigger trigger = new CronTrigger(expression);
            ScheduledFuture<?> future = scheduler.schedule(task.getRunnable(), trigger);
            task.setFuture(future);
            tasks.put(taskId, task);
            return task;
        } else {
            return null;
        }
    }

    /**
     * 獲取任務(wù)列表
     *
     * @return List<CronTrigger>
     */
    public List<CronTask> getAllTasks() {
        return new ArrayList<>(tasks.values());
    }


}

1.3 單元測(cè)試

定時(shí)任務(wù)的單元測(cè)試不好測(cè)試,這里首先實(shí)現(xiàn)一個(gè)需要被執(zhí)行的任務(wù),任務(wù)中會(huì)有一個(gè)CountDownLatch實(shí)例,主線程會(huì)等待countDown()方法執(zhí)行,說明定時(shí)任務(wù)被調(diào)度了,如果超時(shí)未執(zhí)行,說明定時(shí)任務(wù)未生效,此外還會(huì)定義一個(gè)AtomicInteger的計(jì)數(shù)器用來統(tǒng)計(jì)調(diào)用次數(shù)。具體的單元測(cè)試代碼如下:

package org.example.dynamic.timed;

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.Assert;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author shirukai
 */
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class CronTaskSchedulerTest {
    @Autowired
    private CronTaskScheduler scheduler;
    final private static AtomicInteger counter = new AtomicInteger();
    final private static CountDownLatch latch = new CountDownLatch(1);

    private static CronTask task;

    public static class CronTaskRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("The scheduled task is executed.");
            final int count = counter.incrementAndGet();
            if (count <= 1) {
                latch.countDown();
            }
        }
    }

    @Test
    @Order(1)
    void register() throws InterruptedException {
        CronTaskSchedulerTest.task = scheduler.register(new CronTaskRunnable(), "* * * * * ?");
        boolean down = latch.await(2, TimeUnit.SECONDS);
        Assert.isTrue(down, "The scheduled task is not executed within 2 seconds.");

    }

    @Test
    @Order(4)
    void cancel() throws InterruptedException {
        if(CronTaskSchedulerTest.task!=null){
            int minCount = counter.get();
            scheduler.cancel(CronTaskSchedulerTest.task.getId());
            TimeUnit.SECONDS.sleep(5);
            int maxCount = counter.get();
            int deltaCount = maxCount - minCount;
            Assert.isTrue(deltaCount <= 1, "The scheduled task has not been cancelled.");
        }
    }

    @Test
    @Order(2)
    void update() throws InterruptedException {
        if (CronTaskSchedulerTest.task != null) {
            int minCount = counter.get();
            CronTaskSchedulerTest.task = scheduler.update(CronTaskSchedulerTest.task.getId(), "*/2 * * * * ?");
            TimeUnit.SECONDS.sleep(2);
            int maxCount = counter.get();
            int deltaCount = maxCount - minCount;
            Assert.isTrue(deltaCount <= 1, "The scheduled task has not been update.");
        }
    }

    @Test
    @Order(3)
    void getAllTasks() {
        int count = scheduler.getAllTasks().size();
        Assert.isTrue(count==1,"Failed to get all tasks.");
    }
}

2 動(dòng)態(tài)Bean

動(dòng)態(tài)Bean的場(chǎng)景一開始是為了動(dòng)態(tài)注冊(cè)路由(Controller),后來發(fā)現(xiàn)直接創(chuàng)建實(shí)例也可以注冊(cè)路由,不過這里也還要記錄一下,后面很多場(chǎng)景可能會(huì)用到。

2.1 SpringBeanUtils

這里封裝了一個(gè)utils用來獲取IOC容器中的Bean或者動(dòng)態(tài)注冊(cè)Bean到IOC中,實(shí)現(xiàn)很簡(jiǎn)單從ApplicationContext中獲取BeanFactory,就可以注冊(cè)Bean了,ApplicationContext通過getBean就可以獲取Bean

package org.example.dynamic.bean;

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

/**
 * Spirng Bean動(dòng)態(tài)注入
 *
 * @author shirukai
 */
@Component
public class SpringBeanUtils implements ApplicationContextAware {
    private static ConfigurableApplicationContext context;

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

    public static void register(String name, Object bean) {
        context.getBeanFactory().registerSingleton(name, bean);
    }

    public static <T> T getBean(Class<T> clazz) {
        return context.getBean(clazz);
    }

}

2.2 單元測(cè)試

創(chuàng)建一個(gè)靜態(tài)內(nèi)部類,用來注冊(cè)Bean,然后通過工具類中的register和getBean方法來驗(yàn)證。

package org.example.dynamic.bean;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.Assert;

import java.util.Objects;

/**
 * @author shirukai
 */
@SpringBootTest
class SpringBeanUtilsTest {
    public static class BeanTest {
        public String hello() {
            return "hello";
        }
    }

    @Test
    void register() {
        SpringBeanUtils.register("beanTest",new BeanTest());
        BeanTest beanTest = SpringBeanUtils.getBean(BeanTest.class);
        Assert.isTrue(Objects.equals(beanTest.hello(),"hello"),"");
    }

}

3 動(dòng)態(tài)路由Controller

動(dòng)態(tài)路由這個(gè)場(chǎng)景是因?yàn)轫?xiàng)目中有個(gè)調(diào)用外部接口的單元測(cè)試,我又不想用mock方法,就想真實(shí)的測(cè)試一下HTTP請(qǐng)求的過程。一種是通過@RestController暴露一個(gè)接口,另一種就是動(dòng)態(tài)注冊(cè)路由。

3.1 SpringRouterUtils

動(dòng)態(tài)注冊(cè)controller實(shí)現(xiàn)很假單,通過RequestMappingHandlerMapping實(shí)例的registerMapping方法注冊(cè)即可。

package org.example.dynamic.router;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;

/**
 * 路由注冊(cè)
 * @author shirukai
 */
@Component
public class SpringRouterUtils implements ApplicationContextAware {
    private static RequestMappingHandlerMapping mapping;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringRouterUtils.mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
    }

    public static void register(RequestMappingInfo mapping, Object handler, Method method){
        SpringRouterUtils.mapping.registerMapping(mapping,handler,method);
    }


}

3.2 單元測(cè)試

創(chuàng)建一個(gè)內(nèi)部類用來定義Controller層,然你后通過構(gòu)造RequestMappingInfo來定義請(qǐng)求路徑及方法。

package org.example.dynamic.router;

import org.apache.http.client.fluent.Form;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.util.pattern.PathPatternParser;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author shirukai
 */
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource(properties = {"server.port=21199"})
class SpringRouterUtilsTest {
    public static class ExampleController {
        @ResponseBody
        public String hello(String name) {
            return "hi," + name;
        }
    }

    @Test
    void register() throws Exception {
        RequestMappingInfo.BuilderConfiguration options = new RequestMappingInfo.BuilderConfiguration();
        options.setPatternParser(new PathPatternParser());
        RequestMappingInfo mappingInfo = RequestMappingInfo
                .paths("/api/v1/hi")
                .methods(RequestMethod.POST)
                .options(options)
                .build();

        Method method = ExampleController.class.getDeclaredMethod("hello", String.class);

        SpringRouterUtils.register(mappingInfo, new ExampleController(), method);
        Response response = Request.Post("http://127.0.0.1:21199/api/v1/hi")
                .bodyForm(Form.form().add("name", "xiaoming").build())
                .execute();

        Assert.isTrue(Objects.equals(response.returnContent().asString(), "hi,xiaoming"),"");
    }
}

到此這篇關(guān)于SpringBoot動(dòng)態(tài)定時(shí)任務(wù)、動(dòng)態(tài)Bean、動(dòng)態(tài)路由詳解的文章就介紹到這了,更多相關(guān)SpringBoot動(dòng)態(tài)定時(shí)任務(wù)和路由內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Socket聊天室編程(一)之利用socket實(shí)現(xiàn)聊天之消息推送

    Java Socket聊天室編程(一)之利用socket實(shí)現(xiàn)聊天之消息推送

    這篇文章主要介紹了Java Socket聊天室編程(一)之利用socket實(shí)現(xiàn)聊天之消息推送的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-09-09
  • SpringBoot使用JTA實(shí)現(xiàn)對(duì)多數(shù)據(jù)源的事務(wù)管理

    SpringBoot使用JTA實(shí)現(xiàn)對(duì)多數(shù)據(jù)源的事務(wù)管理

    了解事務(wù)的都知道,在我們?nèi)粘i_發(fā)中單單靠事務(wù)管理就可以解決絕大多數(shù)問題了,但是為啥還要提出JTA這個(gè)玩意呢,到底JTA是什么呢?他又是具體來解決啥問題的呢?本文小編就給大家介紹一下如何在Spring Boot中使用JTA實(shí)現(xiàn)對(duì)多數(shù)據(jù)源的事務(wù)管理
    2023-11-11
  • Spring Security自定義異常 AccessDeniedHandler不生效解決方法

    Spring Security自定義異常 AccessDeniedHandler不生效解決方法

    本文主要介紹了Spring Security自定義異常 AccessDeniedHandler不生效解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Spring Data JPA踩坑記錄(@id @GeneratedValue)

    Spring Data JPA踩坑記錄(@id @GeneratedValue)

    這篇文章主要介紹了Spring Data JPA踩坑記錄(@id @GeneratedValue),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • Java sort集合排序的兩種方式解析

    Java sort集合排序的兩種方式解析

    這篇文章主要介紹了Java sort集合排序的兩種方式解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • 淺析Java的Spring框架中IOC容器容器的應(yīng)用

    淺析Java的Spring框架中IOC容器容器的應(yīng)用

    這篇文章主要介紹了Java的Spring框架中IOC容器容器的應(yīng)用,包括BeanFactory容器和ApplicationContext容器的介紹,需要的朋友可以參考下
    2015-12-12
  • Java中獲取List中最后一個(gè)元素的三種方法

    Java中獲取List中最后一個(gè)元素的三種方法

    在Java編程中我們經(jīng)常需要獲取一個(gè)List集合中的最后一個(gè)元素,這篇文章主要給大家介紹了關(guān)于Java中獲取List中最后一個(gè)元素的三種方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • 詳解Java中Duration類的使用方法

    詳解Java中Duration類的使用方法

    Duration類通過秒和納秒相結(jié)合來描述一個(gè)時(shí)間量,最高精度是納秒。本文將通過示例詳細(xì)為大家講講Duration類的使用,需要的可以參考一下
    2022-05-05
  • 利用Spring MVC+Mybatis實(shí)現(xiàn)Mysql分頁(yè)數(shù)據(jù)查詢的過程詳解

    利用Spring MVC+Mybatis實(shí)現(xiàn)Mysql分頁(yè)數(shù)據(jù)查詢的過程詳解

    這篇文章主要給大家介紹了關(guān)于利用Spring MVC+Mybatis實(shí)現(xiàn)Mysql分頁(yè)數(shù)據(jù)查詢的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-08-08
  • java框架基礎(chǔ)之SPI機(jī)制實(shí)現(xiàn)及源碼解析

    java框架基礎(chǔ)之SPI機(jī)制實(shí)現(xiàn)及源碼解析

    這篇文章主要為大家介紹了java框架基礎(chǔ)之SPI機(jī)制實(shí)現(xiàn)及源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09

最新評(píng)論