当前位置: 首页 > news >正文

quartz详细介绍

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

quartz常用api

  • Scheduler 调度程序交互的主要API。
  • Job 希望由调度程序执行的组件实现的接口。
  • JobDetail 用于定义作业的实例。
  • JobDataMap 可以包含不限量的序列化数据,在job运行的时候可以使用
  • JobBuilder 用于定义/构建JobDetail实例,用于定义作业的实例。
  • Trigger 定义执行给定作业的计划的组件。
  • TriggerBuilder 用于定义/构建触发器实例。
  • JobListener TriggerListener SchedulerListener监听器 用于对组件的监听

入门小案列

package com;

import org.quartz.*;
import org.quartz.impl.StdScheduler;
import org.quartz.impl.StdSchedulerFactory;

/**
 * @program: hello
 * @description: 任务调度入口
 * @author: lee
 * @create: 2019-01-13
 **/
public class PScheduller {
    public static void main(String[] args) {
        try {
            //调度器,从工厂中获得调度的实例
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            JobDetail jobDetail = JobBuilder.newJob(PJob.class)
                    .withIdentity("jobid", "job分组")//jobid必须唯一
                    .build();
            Trigger trigger=TriggerBuilder.newTrigger()
                    .withIdentity("triggerid", "trigger分组")
                    //每两秒重复一次,不断的重复下去
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMilliseconds(2000).repeatForever())
                    .build();
            //让调度区关联任务和触发器
            scheduler.scheduleJob(jobDetail,trigger);
            //启动调度器
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}    

Job任务类

package com;

import com.sun.org.apache.xpath.internal.SourceTree;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @program: hello
 * @description: 自定义Job
 * @author: lee
 * @create: 2019-01-13
 **/
public class PJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        Date date=new Date();
        SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString=dateFormat.format(date);
        System.out.println("正在运行job任务,运行时间是:"+dateString);
    }
}

运行结果

正在运行job任务,运行时间是:2019-01-13 10:47:44
正在运行job任务,运行时间是:2019-01-13 10:47:47
正在运行job任务,运行时间是:2019-01-13 10:47:47
正在运行job任务,运行时间是:2019-01-13 10:47:44
正在运行job任务,运行时间是:2019-01-13 10:47:48
...

Job与JobDetail介绍

程序定义了一个实现Job接口的类,这个类仅仅表明该job需要完成什么类型的任务,除此之外,Quartz还需要知道该Job实例所包含的属性;这将由JobDetail类来完成。

Job:工作任务调度的接口,任务类需要实现该接口,需要在接口中的execute方法中实现自己的业务逻辑。

每次调度器执行Job的时候,它在调用execute方法的时候会创建一个新的Job实例,当调用完成以后,关联的Job对象会被释放,释放的实例会被垃圾回收机制回收。

JobDetail:JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来存储特定实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。

JobDetail的重要属性:name,group,jobclass,jobDataMap;

JobDataMap

JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。

 JobDetail jobDetail = JobBuilder.newJob(PJob.class)
                    .withIdentity("jobid", "job分组")//jobid必须唯一
                    .usingJobData("nihao","fasdfsad")
                    .usingJobData("nihao2","fasdfsad2")
                    .build();

获取传递的数据

public class PJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        Date date=new Date();
        SimpleDateFormat dateFormat= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateString=dateFormat.format(date);
        System.out.println("正在运行job任务,运行时间是:"+dateString);
	//后续讲到的删除定时任务就需要用到Jobkey
        JobKey jobKey=jobExecutionContext.getJobDetail().getKey();

        JobDataMap jobDataMap=jobExecutionContext.getJobDetail().getJobDataMap();

        String s1=jobDataMap.getString("nihao");
        String s2=jobDataMap.getString("nihao2");
        System.out.println(s1);
        System.out.println(s2);
        System.err.println(jobKey);
    }
}

Triggers

所有类型的trigger都有TriggerKey这个属性,表示trigger的身份;除此之外,trigger还有很多其它的公共属性。这些属性,在构建trigger的时候可以通过TriggerBuilder设置。

trigger的公共属性有:

  • jobKey属性:当trigger触发时被执行的job的身份;

  • startTime属性:设置trigger第一次触发的时间;该属性的值是java.util.Date类型,表示某个指定的时间点;有些类型的trigger,会在设置的startTime时立即触发,有些类型的trigger,表示其触发是在startTime之后开始生效。比如,现在是1月份,你设置了一个trigger–“在每个月的第5天执行”,然后你将startTime属性设置为4月1号,则该trigger第一次触发会是在几个月以后了(即4月5号)。

  • endTime属性:表示trigger失效的时间点。比如,”每月第5天执行”的trigger,如果其endTime是7月1号,则其最后一次执行时间是6月5号。

优先级(priority)

如果你的trigger很多(或者Quartz线程池的工作线程太少),Quartz可能没有足够的资源同时触发所有的trigger;这种情况下,你可能希望控制哪些trigger优先使用Quartz的工作线程,要达到该目的,可以在trigger上设置priority属性。比如,你有N个trigger需要同时触发,但只有Z个工作线程,优先级最高的Z个trigger会被首先触发。如果没有为trigger设置优先级,trigger使用默认优先级,值为5;priority属性的值可以是任意整数,正数、负数都可以。

 Trigger trigger=TriggerBuilder.newTrigger()
                    .withIdentity("triggerid", "trigger分组")
                    //每两秒重复一次,不断的重复下去
                    .withSchedule(SimpleScheduleBuilder
                                  .simpleSchedule()
                                  .withIntervalInMilliseconds(2000)
                                   .repeatForever())
                    .withPriority(8)
                    .build();

注意:只有同时触发的trigger之间才会比较优先级。10:59触发的trigger总是在11:00触发的trigger之前执行。

注意:如果trigger是可恢复的,在恢复后再调度时,优先级与原trigger是一样的。

Trigger

Simple Trigger

代码实例

package com;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;

/**
 * @program: hello
 * @description: 任务调度入口
 * @author: lee
 * @create: 2019-01-13
 **/
public class PScheduller {
    public static void main(String[] args) throws SchedulerException {
        // 通过SchedulerFactory获取一个调度器实例
        StdSchedulerFactory sf = new StdSchedulerFactory();
        // 代表一个Quartz的独立运行容器
        Scheduler scheduler = sf.getScheduler();
        // 获取当前时间15秒之后的时间
        Date startDate = DateBuilder.nextGivenSecondDate(null,15);

        // ------------------------------------------Job1 start-------------------------------------------------
        // 创建一个JobDetail实例,此版本JobDetail已经作为接口(interface)存在,通过JobBuilder创建
        // 并指定Job在Scheduler中所属组及名称
        JobDetail jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job1","group1").build();

        // SimpleTrigger实现Trigger接口的子接口。此处只指定了开始执行定时任务的时间,使用默认的重复次数(0次)和重复间隔(0秒)
        SimpleTrigger simpleTrigger = (SimpleTrigger) TriggerBuilder.newTrigger().withIdentity("trigger1","group1").
                startAt(startDate).build();
        // 添加JobDetail到Scheduler容器中,并且和Trigger进行关联,返回执行时间
        Date date = scheduler.scheduleJob(jobDetail,simpleTrigger);
        System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
                + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
        // ------------------------------------------Job1 end-------------------------------------------------
        // ------------------------------------------Job2 start-------------------------------------------------
        // 与job1相同处理方式的job2,几乎同时执行两个job1和job2任务
        jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job2","group1").build();
        simpleTrigger = (SimpleTrigger) TriggerBuilder.newTrigger().withIdentity("trigger2","group1").startAt(startDate).build();
        date = scheduler.scheduleJob(jobDetail,simpleTrigger);
        System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
                + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
        // ------------------------------------------Job2 end-------------------------------------------------
        // ------------------------------------------Job3 start-------------------------------------------------
        jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job3","group1").build();
        // 设置定时任务执行规则为在指定的开始时间进行执行,每个十秒执行一次,重复执行十次。
        // 当指定为SimpleScheduleBuilder时,会发现不用对结果进行强制转换为SimpleTrigger了。
        simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger3", "group1").startAt(startDate)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(10)).build();
        System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
                + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
        scheduler.scheduleJob(jobDetail,simpleTrigger);
        // forJob 指定trigger对应的定时任务,另外一种实现形式。此Trigger和上面的Trigger共同出发一个任务,执行各自均执行
        simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger3","group2").startAt(startDate)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(2)).forJob(jobDetail).build();
        scheduler.scheduleJob(simpleTrigger);
        System.out.println(jobDetail.getKey() + " will [also] run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
                + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
        // ------------------------------------------Job3 end-------------------------------------------------
        // ------------------------------------------Job4 start-----------------------------------------------
        jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job4","group1").build();
        simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger4", "group1").startAt(startDate).
                withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(5)).build();
        date = scheduler.scheduleJob(jobDetail, simpleTrigger);
        System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
                + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");

        // ------------------------------------------Job4 end-------------------------------------------------
        // ------------------------------------------Job5 start-------------------------------------------------
        jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job5", "group1").build();
        simpleTrigger = (SimpleTrigger)TriggerBuilder.newTrigger().withIdentity("trigger5", "group1")
                .startAt(DateBuilder.futureDate(5, DateBuilder.IntervalUnit.MINUTE)).build();
        date = scheduler.scheduleJob(jobDetail, simpleTrigger);
        System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
                + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
        // ------------------------------------------Job5 end-------------------------------------------------
        // ------------------------------------------Job6 start-----------------------------------------------
        jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job6", "group1").build();
        // 此定时任务的规则为每隔40秒执行一次,永远执行下去
        simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger6", "group1").startAt(startDate)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(40).repeatForever()).build();
        date = scheduler.scheduleJob(jobDetail, simpleTrigger);
        System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount() + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
        // ------------------------------------------Job6 end-------------------------------------------------
        System.out.println("------- Starting Scheduler ----------------");
        // 在scheduler.start之后调用,可以重新定义trigger,重新注册
        scheduler.start();
        System.out.println("------- Started Scheduler -----------------");
        // ------------------------------------------Job7 start-----------------------------------------------
        jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job7", "group1").build();
        // 每隔五分钟执行一次,执行20次
        simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger7", "group1").startAt(startDate)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMinutes(5).withRepeatCount(20)).build();
        date = scheduler.scheduleJob(jobDetail, simpleTrigger);
        System.out.println(jobDetail.getKey() + " will run at: " + date + " and repeat: " + simpleTrigger.getRepeatCount()
                + " times, every " + simpleTrigger.getRepeatInterval() / 1000L + " seconds");
        // ------------------------------------------Job7 end-------------------------------------------------
        // ------------------------------------------Job8 start-----------------------------------------------
        // storeDurably(),没有触发器指向任务的时候,将任务保存在队列中,然后可以通过手动进行出发
        jobDetail = JobBuilder.newJob(PJob.class).withIdentity("job8", "group1").storeDurably().build();
        // 任务立即执行
        scheduler.addJob(jobDetail, true);
        System.out.println("手动触发   triggering job8...");
        scheduler.triggerJob(JobKey.jobKey("job8", "group1"));
        // ------------------------------------------Job8 end-------------------------------------------------
        scheduler.start();
        try {
            Thread.sleep(60000L);
        } catch (InterruptedException e) {

        }

        // 对Job7进行重新安排
        System.out.println("------- Rescheduling... --------------------");
        simpleTrigger = TriggerBuilder.newTrigger().withIdentity("trigger7", "group1").startAt(startDate).
                withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMinutes(5).withRepeatCount(20)).build();
        date = scheduler.rescheduleJob(simpleTrigger.getKey(), simpleTrigger);
        System.out.println("job7 rescheduled to run at: " + date);
        System.out.println("------- Waiting five minutes... ------------");

        try {
            Thread.sleep(60000L);
        } catch (Exception ex) {
        }

        scheduler.shutdown(true);

        SchedulerMetaData metaData = scheduler.getMetaData();
        System.out.println("Executed " + metaData.getNumberOfJobsExecuted() + " jobs.");
     
    }
}    

CronTrigger

CronTrigger通常比Simple Trigger更有用,如果您需要基于日历的概念而不是按照SimpleTrigger的精确指定间隔进行重新启动的作业启动计划。

使用CronTrigger,您可以指定号时间表,例如“每周五中午”或“每个工作日和上午9:30”,甚至“每周一至周五上午9:00至10点之间每5分钟”和1月份的星期五“。

即使如此,和SimpleTrigger一样,CronTrigger有一个startTime,它指定何时生效,以及一个(可选的)endTime,用于指定何时停止计划。

CronTrigger配置格式:

格式: [秒] [分] [小时] [日] [月] [周] [年]

说明是否必填允许填写的值允许的通配符
0-59, - * /
0-59, - * /
小时0-23, - * /
1-31, - * ? / L W
1-12 or JAN-DEC, - * /
1-7 or SUN-SAT, - * ? / L #
empty 或 1970-2099, - * /

* 表示所有值. 例如:在分的字段上设置 *,表示每一分钟都会触发。

? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为? 具体设置为 0 0 0 10 * ?

- 表示区间。例如 在小时上设置 10-12,表示 10,11,12点都会触发。

, 表示指定多个值,例如在周字段上设置 MON,WED,FRI 表示周一,周三和周五触发

/ 用于递增触发。如在秒上面设置5/15 表示从5秒开始,每增15秒触发(5,20,35,50)。 在月字段上设置'1/3'所示每月1号开始,每隔三天触发一次。

L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于7或SAT。如果在L前加上数字,则表示该数据的最后一个。例如在周字段上设置6L这样的格式,则表示本 月最后一个星期五。

-W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置15W,表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 1W,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,W前只能设置具体的数字,不允许区间-).L和W可以一组合使用。如果在日字段上设置LW,则表示在本月的最后一个工作日触发(一般指发工资) 

-#  序号(表示每月的第几个周几),例如在周字段上设置6#3表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了)

小提示	
周字段的设置,若使用英文字母是不区分大小写的 MON 与mon相同.

常用示例:


0 0 12 * * ?	每天12点触发
0 15 10 ? * *	每天10点15分触发
0 15 10 * * ?	每天10点15分触发
0 15 10 * * ? *	每天10点15分触发
0 15 10 * * ? 2005	2005年每天10点15分触发
0 * 14 * * ?	每天下午的 2点到2点59分每分触发
0 0/5 14 * * ?	每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ?	每天下午的 2点到2点59分(整点开始,每隔5分触发)
每天下午的 18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ?	每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED	3月分每周三下午的 2点10分和2点44分触发
0 15 10 ? * MON-FRI	从周一到周五每天上午的10点15分触发
0 15 10 15 * ?	每月15号上午10点15分触发
0 15 10 L * ?	每月最后一天的10点15分触发
0 15 10 ? * 6L	每月最后一周的星期五的10点15分触发
0 15 10 ? * 6L 2002-2005	从2002年到2005年每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3	每月的第三周的星期五开始触发
0 0 12 1/5 * ?	每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ?	每年的11月11号 11点11分触发(光棍节)

Job Stores

JobStore负责跟踪您提供给调度程序的所有“工作数据”:jobs,triggers,日历等。

RAMJobStore

RAMJobStore是使用最简单的JobStore,它也是性能最高的(在CPU时间方面)。RAMJobStore以其明显的方式获取其名称:它将其所有数据保存在RAM中。这就是为什么它是闪电般快的,也是为什么这么简单的配置。缺点是当您的应用程序结束(或崩溃)时,所有调度信息都将丢失 - 这意味着RAMJobStore无法履行作业和triggers上的“非易失性”设置。对于某些应用程序,这是可以接受的 - 甚至是所需的行为,但对于其他应用程序,这可能是灾难性的。

要使用RAMJobStore(并假设您使用的是StdSchedulerFactory),只需将类名称org.quartz.simpl.RAMJobStore指定为用于配置石英的JobStore类属性:

配置Quartz以使用RAMJobStore

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

JDBC JobStore

JDBCJobStore也被恰当地命名 - 它通过JDBC将其所有数据保存在数据库中。

您需要确定应用程序需要哪种类型的事务。如果您不需要将调度命令(例如添加和删除triggers)绑定到其他事务,那么可以通过使用JobStoreTX作为JobStore 来管理事务(这是最常见的选择)。

如果您需要Quartz与其他事务(即J2EE应用程序服务器)一起工作,那么您应该使用JobStoreCMT - 在这种情况下,Quartz将让应用程序服务器容器管理事务。

配置Quartz以使用JobStoreTx

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

接下来,您需要为JobStore选择一个DriverDelegate才能使用。DriverDelegate负责执行特定数据库可能需要的任何JDBC工作.StdJDBCDelegate是一个使用“vanilla”JDBC代码(和SQL语句)来执行其工作的委托。

配置JDBCJobStore以使用DriverDelegate

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

接下来,您需要通知JobStore您正在使用的表前缀(如上所述)。

使用表前缀配置JDBCJobStore

org.quartz.jobStore.tablePrefix = QRTZ_

使用要使用的DataSource的名称配置JDBCJobStore

org.quartz.jobStore.dataSource = commonDataSource

TerracottaJobStore

TerracottaJobStore提供了一种缩放和鲁棒性的手段,而不使用数据库。这意味着您的数据库可以免受Quartz的负载,可以将其所有资源保存为应用程序的其余部分。

TerracottaJobStore可以运行群集或非群集,并且在任一情况下,为应用程序重新启动之间持续的作业数据提供存储介质,因为数据存储在Terracotta服务器中。它的性能比通过JDBCJobStore使用数据库要好得多(约一个数量级更好),但比RAMJobStore要慢。

要使用TerracottaJobStore(并且假设您使用的是StdSchedulerFactory),只需将类名称org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore指定为用于配置石英的JobStore类属性,并添加一个额外的行配置来指定Terracotta服务器的位置

配置Quartz以使用TerracottaJobStore

org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore
org.quartz.jobStore.tcConfigUrl = localhost:9510

转载于:https://my.oschina.net/jiansin/blog/3002933

相关文章:

  • 回顾小程序2018年三足鼎立历程,2019年BAT火力全开
  • oschina
  • 深度学习入门:10门免费线上课程推荐
  • CF712E Memory and Casinos
  • 研究:印度气候变暖速度加剧 2040年或面临重灾
  • ImageLoader的优化写法
  • 七牛实时音视频云视频连线demo(web部分)
  • 支付宝支撑2135亿成交额的数据库架构原理
  • ZStack--工作流引擎
  • dmidecode查看linux硬件信息
  • Octave 入门
  • 第33讲 | 区块链与供应链(二)
  • Hadoop完全分布式环境搭建(四)——基于Ubuntu16.04安装和配置Hadoop大数据环境...
  • 使用parted解决大于2T的磁盘分区
  • day29:关闭服务|
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • Angular 响应式表单 基础例子
  • C++11: atomic 头文件
  • CAP理论的例子讲解
  • CSS相对定位
  • java取消线程实例
  • Laravel 实践之路: 数据库迁移与数据填充
  • mysql_config not found
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • spring boot下thymeleaf全局静态变量配置
  • Terraform入门 - 3. 变更基础设施
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • vue-cli3搭建项目
  • 创建一种深思熟虑的文化
  • 基于webpack 的 vue 多页架构
  • 理清楚Vue的结构
  • 聊聊hikari连接池的leakDetectionThreshold
  • 微信小程序--------语音识别(前端自己也能玩)
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 译米田引理
  • #HarmonyOS:基础语法
  • #数学建模# 线性规划问题的Matlab求解
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (二)hibernate配置管理
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (黑马C++)L06 重载与继承
  • (六)激光线扫描-三维重建
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (转)创业的注意事项
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • ***测试-HTTP方法
  • ./configure,make,make install的作用
  • .cfg\.dat\.mak(持续补充)
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .Net Core 中间件验签
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .NET 发展历程