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

spring設(shè)置定時(shí)任務(wù)方式(@Scheduled)

 更新時(shí)間:2024年04月15日 09:07:33   作者:雜說(shuō)  
這篇文章主要介紹了spring設(shè)置定時(shí)任務(wù)方式(@Scheduled),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

以前用過(guò)這個(gè)注解實(shí)現(xiàn)定時(shí)任務(wù),但是只是使用,現(xiàn)在做項(xiàng)目又用到了這個(gè)功能,系統(tǒng)的學(xué)習(xí)一下~

spring定時(shí)任務(wù)設(shè)置有兩種方式,注解和xml配置。

推薦使用注解,在本文章也主要介紹注解方式配置

一:注解方式配置定時(shí)任務(wù)

下面的步驟默認(rèn)spring的其他配置項(xiàng)都已經(jīng)配置好(比如啟動(dòng)注解配置,包路徑掃描等)

1:在spring配置文件中配置,添加命名空間

xmlns添加:

xmlns:task="http://www.springframework.org/schema/task"

xsi:schemaLocation添加 注意"4.3"這是版本號(hào),要修改和你的其他xsd版本號(hào)一致

http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd"

啟動(dòng)注解驅(qū)動(dòng) 注意“dataScheduler”為自定義名稱,可以通過(guò)自己的業(yè)務(wù)定義 合適 的名稱

<task:annotation-driven scheduler="dataScheduler"/>

開(kāi)啟任務(wù)調(diào)度器,并配置線程池大小

  • 注意此處的id指定的就是上面的自定義名稱
  • spring的任務(wù)調(diào)度默認(rèn)是單線程的,如果你的項(xiàng)目會(huì)有多任務(wù)定時(shí)執(zhí)行,并且執(zhí)行時(shí)間會(huì)相交的話,應(yīng)該根據(jù)任務(wù)的具體執(zhí)行情況配置線程池大小
  • 如果不配置線程池,并且A和B任務(wù)在同一時(shí)間執(zhí)行,A先執(zhí)行的話,B要等待A執(zhí)行完才可以執(zhí)行,AB不會(huì)同時(shí)執(zhí)行
<task:scheduler id="dataScheduler" pool-size="5"/>

2:使用注解配置定時(shí)任務(wù)

在你需要配置定時(shí)任務(wù)的方法上使用注解@Scheduled即可,下面一個(gè)簡(jiǎn)單案例:

  • 注意 下面的案例是在每天的早上2點(diǎn)執(zhí)行
  • “0 0 2 * * *”是怎么組合的?下面會(huì)詳細(xì)介紹@Scheduled()注解
@Scheduled(cron = "0 0 2 * * *")
public void init(){
    todo...
}

在此需要注意:@Scheduled只能注釋在無(wú)參的方法上,我看網(wǎng)上有許多博客說(shuō)必須無(wú)參無(wú)返回值的,但是經(jīng)過(guò)我的測(cè)試有返回值是可以的,可能是版本更新了吧。

現(xiàn)在就算是完成spring定時(shí)器的使用了,下面讓我們來(lái)詳細(xì)的看一下@Scheduled注解吧~

二:@Scheduled

@Scheduled注解是Spring專門為定時(shí)任務(wù)設(shè)計(jì)的注解

首先,讓我們來(lái)看看這個(gè)注解是怎么組成的吧(適用于版本JDK8與spring4.3及其以上)

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {

    String cron() default "";
    String zone() default "";
    long fixedDelay() default -1L;
    String fixedDelayString() default "";
    long fixedRate() default -1L;
    String fixedRateString() default "";
    long initialDelay() default -1L;
    String initialDelayString() default "";
}

從上述代碼中看以看出:

1:@Scheduled被注解部分:

  • 元注解@Target表明@Scheduled注解可以在方法上使用(ElementType.METHOD),也可以作為元注解對(duì)其他注解進(jìn)行注解(ElementType.ANNOTATION_TYPE)
  • 元注解@Retention表明此注解會(huì)被JVM所保留,也就是會(huì)保存在運(yùn)行時(shí)(RetentionPolicy.RUNTIME)
  • 元注解@Documented表明此注解應(yīng)該被 javadoc工具記錄。默認(rèn)情況下javadoc是不包括注解的。
  • JDK8添加的注解@Repeatable表明此注解可以在同一個(gè)地方被重復(fù)使用

上述的所涉及到的注解有不清楚作用的,可以自行baidu\google,網(wǎng)上有好多介紹的文章。

2:@Scheduled參數(shù)部分,總共包含8各部分,我們來(lái)分別看一下其作用:

  • cron:一個(gè)類似cron的表達(dá)式,擴(kuò)展了通常的UN * X定義,包括秒,分,時(shí),星期,月,年的觸發(fā)器。
  • fixedDelay:在最后一次調(diào)用結(jié)束和下一次調(diào)用開(kāi)始之間以固定周期(以毫秒為單位)執(zhí)行帶注釋的方法。(要等待上次任務(wù)完成后)
  • fixedDelayString:同上面作用一樣,只是String類型
  • fixedRate:在調(diào)用之間以固定的周期(以毫秒為單位)執(zhí)行帶注釋的方法。(不需要等待上次任務(wù)完成)
  • fixedRateString:同上面作用一樣,只是String類型
  • initialDelay:第一次執(zhí)行fixedRate()或fixedDelay()任務(wù)之前延遲的毫秒數(shù) 。
  • initialDelayString:同上面作用一樣,只是String類型
  • zone:指明解析cron表達(dá)式的時(shí)區(qū)。

cron可以組合出更多的定時(shí)情況,fixedDelay和fixedRate只能定義每隔多長(zhǎng)時(shí)間執(zhí)行一次。

在上述cron、fixedDelay、fixedRate 只能同時(shí)存在一個(gè),使用其中一個(gè)就不能使用另外的一個(gè),否則會(huì)報(bào)錯(cuò)“java.lang.IllegalStateException”

3:cron參數(shù)

一個(gè)cron表達(dá)式可以有6個(gè)元素或者7個(gè)元素組成(“年”這個(gè)元素可以省略,省略之后就是默認(rèn)“每一年”)

3.1:按順序依次為:

  • 秒(0~59)
  • 分鐘(0~59)
  • 小時(shí)(0~23)
  • 天(0~31)
  • 月(0~11)
  • 星期(1~7 )或者( SUN,MON,TUE,WED,THU,F(xiàn)RI,SAT。其中SUN = 1)
  • 年份(1970-2099)

3.2:每個(gè)元素可以接受的值:

字段允許值允許的特殊字符
0-59, - * /
0-59, - * /
小時(shí)0-23, - * /
日期1-31, - * ? / L W C
月份1-12 或者 JAN-DEC, - * /
星期1-7 或者 SUN-SAT, - * ? / L C #
空, 1970-2099, - * /

3.3:一些特殊字符解釋與注意事項(xiàng),可以結(jié)合下面的小案例來(lái)理解:

其中每個(gè)元素可以是一個(gè)值(如6),一個(gè)連續(xù)區(qū)間(9-12),一個(gè)間隔時(shí)間(8-18/4)(/表示每隔4小時(shí)),一個(gè)列表(1,3,5),通配符。

其中的“日”由于"月份中的日期"和"星期"這兩個(gè)元素互斥的,必須要對(duì)其中一個(gè)設(shè)置“?”。

有些子表達(dá)式能包含一些范圍或列表

  • 例如:子表達(dá)式(天(星期))可以為 “MON-FRI”,“MON,WED,F(xiàn)RI”,“MON-WED,SAT”

“*”字符代表所有可能的值

“/”字符用來(lái)指定數(shù)值的增量

  • 例如:在子表達(dá)式(分鐘)里的“0/15”表示從第0分鐘開(kāi)始,每15分鐘
  • 在子表達(dá)式(分鐘)里的“3/20”表示從第3分鐘開(kāi)始,每20分鐘(它和“3,23,43”)的含義一樣

“?”字符僅被用于天(月)和天(星期)兩個(gè)子表達(dá)式,表示不指定值

  • 當(dāng)2個(gè)子表達(dá)式其中之一被指定了值以后,為了避免沖突,需要將另一個(gè)子表達(dá)式的值設(shè)為“?”

“L” 字符僅被用于天(月)和天(星期)兩個(gè)子表達(dá)式,它是單詞“last”的縮寫

  • 如果在“L”前有具體的內(nèi)容,它就具有其他的含義了。例如:“6L”表示這個(gè)月的倒數(shù)第6天
  • 注意:在使用“L”參數(shù)時(shí),不要指定列表或范圍,因?yàn)檫@會(huì)導(dǎo)致問(wèn)題

“W” 字符代表著平日(Mon-Fri),并且僅能用于日域中。它用來(lái)指定離指定日的最近的一個(gè)平日。大部分的商業(yè)處理都是基于工作周的,所以 W 字符可能是非常重要的。

  • 例如,日域中的 15W 意味著 “離該月15號(hào)的最近一個(gè)平日。” 假如15號(hào)是星期六,那么 trigger 會(huì)在14號(hào)(星期五)觸發(fā),因?yàn)樾瞧谒谋刃瞧谝浑x15號(hào)更近。

“C”:代表“Calendar”的意思。它的意思是計(jì)劃所關(guān)聯(lián)的日期,如果日期沒(méi)有被關(guān)聯(lián),則相當(dāng)于日歷中所有日期。例如5C在日期字段中就相當(dāng)于日歷5日以后的第一天。1C在星期字段中相當(dāng)于星期日后的第一天。

3.4:一些小案例:

  • “0 0 10,14,16 * * ?” 每天上午10點(diǎn),下午2點(diǎn),4點(diǎn)
  • “0 0/30 9-17 * * ?” 朝九晚五工作時(shí)間內(nèi)每半小時(shí)
  • “0 0 12 ? * WED” 表示每個(gè)星期三中午12點(diǎn)
  • “0 0 12 * * ?” 每天中午12點(diǎn)觸發(fā)
  • “0 15 10 ? * *” 每天上午10:15觸發(fā)(這個(gè)和下一個(gè)案例說(shuō)明,必須"月份中的日期"和"星期"中有一個(gè)設(shè)置為“?”)
  • “0 15 10 * * ?” 每天上午10:15觸發(fā)
  • “0 15 10 * * ? *” 每天上午10:15觸發(fā)(7個(gè)元素類型案例,第七個(gè)元素代表年)
  • “0 15 10 * * ? 2005” 2005年的每天上午10:15觸發(fā)
  • “0 * 14 * * ?” 在每天下午2點(diǎn)到下午2:59期間的每1分鐘觸發(fā)
  • “0 0/5 14 * * ?” 在每天下午2點(diǎn)到下午2:55期間的每5分鐘觸發(fā)
  • “0 0/5 14,18 * * ?” 在每天下午2點(diǎn)到2:55期間和下午6點(diǎn)到6:55期間的每5分鐘觸發(fā)
  • “0 0-5 14 * * ?” 在每天下午2點(diǎn)到下午2:05期間的每1分鐘觸發(fā)
  • “0 10,44 14 ? 3 WED” 每年三月的星期三的下午2:10和2:44觸發(fā)
  • “0 15 10 ? * MON-FRI” 周一至周五的上午10:15觸發(fā)
  • “0 15 10 15 * ?” 每月15日上午10:15觸發(fā)
  • “0 15 10 L * ?” 每月最后一日的上午10:15觸發(fā)
  • “0 15 10 ? * 6L” 每月的最后一個(gè)星期五上午10:15觸發(fā)
  • “0 15 10 ? * 6L 2002-2005” 2002年至2005年的每月的最后一個(gè)星期五上午10:15觸發(fā)
  • “0 15 10 ? * 6#3” 每月的第三個(gè)星期五上午10:15觸發(fā)

到這個(gè)地方你應(yīng)該對(duì)@Scheduled有一個(gè)較全面的理解了,下面我們就來(lái)簡(jiǎn)單的看一下其實(shí)現(xiàn)原理吧~

三:原理簡(jiǎn)介

1:主要過(guò)程:

spring在使用applicationContext將類全部初始化。

調(diào)用ScheduledAnnotationBeanPostProcessor類中的postProcessAfterInitialization方法獲取項(xiàng)目中所有被注解 @Scheduled注解的方法 。

通過(guò)processScheduled方法將所有定時(shí)的方法存放在Set tasks = new LinkedHashSet(4); 定時(shí)任務(wù)隊(duì)列中,并解析相應(yīng)的參數(shù)。順序存放,任務(wù)也是順序執(zhí)行。存放順序?yàn)閏ron>fixedDelay>fixedRate

將解析參數(shù)后的定時(shí)任務(wù)存放在一個(gè)初始容量為16 的map中,key為bean name,value為定時(shí)任務(wù):private final Map<Object, Set> scheduledTasks = new IdentityHashMap(16);

之后交給ScheduledTaskRegistrar類的方法scheduleTasks去添加定時(shí)任務(wù)。

2:上述就是一個(gè)大致過(guò)程,下面看一下相應(yīng)的源碼:

注意 :spring對(duì)定時(shí)任務(wù)的操作的源碼全部在spring-context.jar包下的org.springframework.scheduling包下面,主要包含三部分:annotation、config、 support,大家有興趣的話可以去看看

1:獲取項(xiàng)目中所有被注解 @Scheduled注解的方法

public Object postProcessAfterInitialization(Object bean, String beanName) {
    Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
    if (!this.nonAnnotatedClasses.contains(targetClass)) {
        Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MetadataLookup<Set<Scheduled>>() {
            public Set<Scheduled> inspect(Method method) {
                //獲取注解方法
                **Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);**
                return !scheduledMethods.isEmpty() ? scheduledMethods : null;
            }
        });
        if (annotatedMethods.isEmpty()) {
            ...
        } else {
            Iterator var5 = annotatedMethods.entrySet().iterator();
            while(var5.hasNext()) {

                Entry<Method, Set<Scheduled>> entry = (Entry)var5.next();
                Method method = (Method)entry.getKey();
                Iterator var8 = ((Set)entry.getValue()).iterator();

                while(var8.hasNext()) {
                    Scheduled scheduled = (Scheduled)var8.next();
                    //將獲取的任務(wù)進(jìn)行參數(shù)解析并存放到任務(wù)隊(duì)列
                    this.processScheduled(scheduled, method, bean);
                }
            }
           ...
        }
    }
    return bean;
}

2:通過(guò)processScheduled方法將所有定時(shí)的方法存放在定時(shí)任務(wù)隊(duì)列中

protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
    try {
        ...
        //解析initialDelayString參數(shù)
        String initialDelayString = scheduled.initialDelayString();
        if (StringUtils.hasText(initialDelayString)) {
           ...
        }
        //解析cron參數(shù)
        String cron = scheduled.cron();
        if (StringUtils.hasText(cron)) {
            ...
            //存放到任務(wù)隊(duì)列中
            tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
        }
        ...
        //解析fixedDelay參數(shù)
        long fixedDelay = scheduled.fixedDelay();
        if (fixedDelay >= 0L) {
            Assert.isTrue(!processedSchedule, errorMessage);
            processedSchedule = true;
            tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
        }
        String fixedDelayString = scheduled.fixedDelayString();
        if (StringUtils.hasText(fixedDelayString)) {
            ...
            //存放到任務(wù)隊(duì)列中
            tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));
        }
        //解析fixedRate參數(shù)
        long fixedRate = scheduled.fixedRate();
        if (fixedRate >= 0L) {
            Assert.isTrue(!processedSchedule, errorMessage);
            processedSchedule = true;
            tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
        }
        String fixedRateString = scheduled.fixedRateString();
        if (StringUtils.hasText(fixedRateString)) {
            ...
            //存放到任務(wù)隊(duì)列中
            tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));
        }
        Assert.isTrue(processedSchedule, errorMessage);
        Map var19 = this.scheduledTasks;
        //并發(fā)控制并將任務(wù)存放在map中
        synchronized(this.scheduledTasks) {
            Set<ScheduledTask> registeredTasks = (Set)this.scheduledTasks.get(bean);
            if (registeredTasks == null) {
                registeredTasks = new LinkedHashSet(4);
                //將任務(wù)存放在map中
                this.scheduledTasks.put(bean, registeredTasks);
            }
            ((Set)registeredTasks).addAll(tasks);
        }
    } catch (IllegalArgumentException var26) {
        throw new IllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " + var26.getMessage());
    }
}

3:之后交給ScheduledTaskRegistrar類的方法scheduleTasks去添加定時(shí)任務(wù)

protected void scheduleTasks() {
    if (this.taskScheduler == null) {
        this.localExecutor = Executors.newSingleThreadScheduledExecutor();
        this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
    }
    Iterator var1;
    if (this.triggerTasks != null) {
        var1 = this.triggerTasks.iterator();
        while(var1.hasNext()) {
            TriggerTask task = (TriggerTask)var1.next();
            this.addScheduledTask(this.scheduleTriggerTask(task));
        }
    }
    if (this.cronTasks != null) {
        var1 = this.cronTasks.iterator();
        while(var1.hasNext()) {
            CronTask task = (CronTask)var1.next();
            this.addScheduledTask(this.scheduleCronTask(task));
        }
    }
    IntervalTask task;
    if (this.fixedRateTasks != null) {
        var1 = this.fixedRateTasks.iterator();
        while(var1.hasNext()) {
            task = (IntervalTask)var1.next();
            this.addScheduledTask(this.scheduleFixedRateTask(task));
        }
    }
    if (this.fixedDelayTasks != null) {
        var1 = this.fixedDelayTasks.iterator();
        while(var1.hasNext()) {
            task = (IntervalTask)var1.next();
            this.addScheduledTask(this.scheduleFixedDelayTask(task));
        }
    }
}

此部分只是對(duì)原理進(jìn)行了簡(jiǎn)單的介紹,如果有興趣深入了解,可以去看看源碼~

四:其他

做定時(shí)任務(wù)還可以使用java自帶的原生API,Timer和TimerTask去設(shè)計(jì)。

  • Timer:一種工具,線程用其安排以后在后臺(tái)線程中執(zhí)行的任務(wù)??砂才湃蝿?wù)執(zhí)行一次,或者定期重復(fù)執(zhí)行。
  • TimerTask:定義一個(gè)被執(zhí)行的任務(wù),Timer 安排該任務(wù)為一次執(zhí)行或重復(fù)執(zhí)行的任務(wù)。

可以這樣理解Timer是一種定時(shí)器工具,用來(lái)在一個(gè)后臺(tái)線程計(jì)劃執(zhí)行指定任務(wù),而TimerTask一個(gè)抽象類,它的子類代表一個(gè)可以被Timer計(jì)劃的任務(wù)。

這里就簡(jiǎn)單的提一下,并不是本文的重點(diǎn),具體的用法自行g(shù)oogle吧~

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • springboot2版本無(wú)法加載靜態(tài)資源問(wèn)題解決

    springboot2版本無(wú)法加載靜態(tài)資源問(wèn)題解決

    這篇文章主要介紹了springboot2版本無(wú)法加載靜態(tài)資源問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • springboot打包無(wú)法讀取yml、properties等配置文件的解決

    springboot打包無(wú)法讀取yml、properties等配置文件的解決

    這篇文章主要介紹了springboot打包無(wú)法讀取yml、properties等配置文件的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • springboot中配置好登錄攔截后,swagger訪問(wèn)不了問(wèn)題

    springboot中配置好登錄攔截后,swagger訪問(wèn)不了問(wèn)題

    這篇文章主要介紹了springboot中配置好登錄攔截后,swagger訪問(wèn)不了問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • java多線程實(shí)現(xiàn)有序輸出ABC

    java多線程實(shí)現(xiàn)有序輸出ABC

    這篇文章主要為大家詳細(xì)介紹了java多線程實(shí)現(xiàn)有序輸出ABC,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • Idea配置超詳細(xì)圖文教程(2020.2版本)

    Idea配置超詳細(xì)圖文教程(2020.2版本)

    這篇文章主要介紹了Idea配置超詳細(xì)圖文教程(2020.2版本),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • SpringBoot整合MyBatis實(shí)現(xiàn)樂(lè)觀鎖和悲觀鎖的示例

    SpringBoot整合MyBatis實(shí)現(xiàn)樂(lè)觀鎖和悲觀鎖的示例

    這篇文章主要介紹了SpringBoot整合MyBatis實(shí)現(xiàn)樂(lè)觀鎖和悲觀鎖的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Java中的fail-fast機(jī)制使用詳解

    Java中的fail-fast機(jī)制使用詳解

    fail-fast機(jī)制是Java集合中用于檢測(cè)并發(fā)修改的一種機(jī)制,當(dāng)一個(gè)線程遍歷集合時(shí),如果集合被其他線程修改,就會(huì)拋出ConcurrentModificationException異常,解決fail-fast機(jī)制的方法包括使用普通for循環(huán)、Iterator
    2025-01-01
  • Springboot如何使用外部yml啟動(dòng)

    Springboot如何使用外部yml啟動(dòng)

    這篇文章主要介紹了Springboot如何使用外部yml啟動(dòng)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringBoot打印系統(tǒng)執(zhí)行的sql語(yǔ)句及日志配置指南

    SpringBoot打印系統(tǒng)執(zhí)行的sql語(yǔ)句及日志配置指南

    這篇文章主要給大家介紹了關(guān)于SpringBoot打印系統(tǒng)執(zhí)行的sql語(yǔ)句及日志配置的相關(guān)資料,在Java SpringBoot項(xiàng)目中如果使用了Mybatis框架,默認(rèn)情況下執(zhí)行的所有SQL操作都不會(huì)打印日志,需要的朋友可以參考下
    2023-10-10
  • 【java 多線程】守護(hù)線程與非守護(hù)線程的詳解

    【java 多線程】守護(hù)線程與非守護(hù)線程的詳解

    這篇文章主要介紹了java守護(hù)線程與非守護(hù)線程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04

最新評(píng)論