您的位置:首页 > 编程语言 > Java开发

SpringCloud(第 054 篇)简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群

2017-12-17 15:17 603 查看

SpringCloud(第 054 篇)简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群

-

一、大致介绍

1、因网友提到有没有采用注解式配置的Quartz例子,因此本人就贴上了这样一个样例;
2、至于如何修改定时任务的 cronExpression 表达式值的话,大家可以参照之前的(第 010 篇)样子看看如何修改;

3、注意:配置文件中的 mysql 数据库链接配置大家就各自配置自己的哈;


二、实现步骤

2.1 添加 maven 引用包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>

<artifactId>springms-simple-quartz-config-cluster</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>com.springms.cloud</groupId>
<artifactId>springms-spring-cloud</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
<!-- 访问数据库模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- MYSQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- Jdbc 模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<!-- quartz 模块 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>

<!-- druid 线程池模块 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.3</version>
</dependency>
</dependencies>

</project>


2.2 添加应用配置文件(springms-simple-quartz-config-cluster\src\main\resources\application.yml)

server:
port: 8405
spring:
application:
name: springms-simple-quartz-config-cluster  #全部小写

#####################################################################################################
# mysql 属性配置
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://ip:3306/hmilyylimh?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
username: username
password: password
jpa:
hibernate:
#ddl-auto: create #ddl-auto:设为create表示每次都重新建表
ddl-auto: update #ddl-auto:设为update表示每次都不会重新建表
show-sql: true
#####################################################################################################

#####################################################################################################
# 打印日志
logging:
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
com.springms: DEBUG
#####################################################################################################


2.3 添加 quartz 配置文件(springms-simple-quartz-config-cluster/src/main/resources/quartz.properties)

org.quartz.scheduler.instanceName = quartzScheduler
org.quartz.scheduler.instanceId = AUTO

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.clusterCheckinInterval = 20000

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true


2.4 添加数据源配置类(springms-simple-quartz-config-cluster\src\main\java\com\springms\cloud\config\DataSourceConfig.java)

package com.springms.cloud.config;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import javax.annotation.Resource;

/**
* 数据源配置。
*
* @author hmilyylimh
*
* @version 0.0.1
*
* @date 2017/12/17
*
*/
@Configuration
public class DataSourceConfig {

@Resource
private Environment env;

@Bean
public DataSource dataSource(){
DataSource configDataSource = new DataSource();
configDataSource.setUrl("jdbc:mysql://ip:3306/hmilyylimh?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true");
configDataSource.setDriverClassName("com.mysql.jdbc.Driver");
configDataSource.setUsername("username");
configDataSource.setPassword("password");
configDataSource.setInitialSize(5);
configDataSource.setDefaultAutoCommit(true);

if(configDataSource.getUrl().startsWith("jdbc:mysql://ip")){
throw new RuntimeException("请配置数据源地址");
}
if(configDataSource.getUsername().startsWith("username")){
throw new RuntimeException("请配置数据源用户名");
}
if(configDataSource.getPassword().startsWith("password")){
throw new RuntimeException("请配置数据源密码");
}

// 这些配置看个人需要,需要的话可以全部配置起来,我这里写Demo的话就没有全部配置起来了
//configDataSource.setInitialSize();
//configDataSource.setMinEvictableIdleTimeMillis();
//configDataSource.setNumTestsPerEvictionRun();
//configDataSource.setTestWhileIdle();
//configDataSource.setMaxActive();
//configDataSource.setMaxIdle();
//configDataSource.setMinIdle();
//configDataSource.setMaxWait();
//configDataSource.setRemoveAbandoned();
//configDataSource.setRemoveAbandonedTimeout();
//configDataSource.setValidationQuery();
//configDataSource.setValidationQueryTimeout();
//configDataSource.setValidationInterval();
//configDataSource.setTimeBetweenEvictionRunsMillis();

return configDataSource;
}
}


2.5 添加Quartz调度配置类(springms-simple-quartz-config-cluster\src\main\java\com\springms\cloud\config\QuartzSchedulerConfig.java)

package com.springms.cloud.config;

import com.springms.cloud.job.Job1;
import com.springms.cloud.job.Job2;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

import java.io.IOException;
import java.util.Properties;

/**
* Quartz调度配置类。
*
* @author hmilyylimh
*
* @version 0.0.1
*
* @date 2017/12/17
*
*/
@Configuration
public class QuartzSchedulerConfig {

@Autowired
private DataSource dataSource;

private static final Logger Logger = LoggerFactory.getLogger(QuartzSchedulerConfig.class);
private static final String QUARTZ_PROPERTIES_NAME = "/quartz.properties";

@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}

@Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory, CronTrigger[] cronTrigger, JobDetail[]
jobDetails) {
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
try {
factoryBean.setQuartzProperties(quartzProperties());
factoryBean.setDataSource(dataSource);
factoryBean.setJobFactory(jobFactory);
factoryBean.setTriggers(cronTrigger);
factoryBean.setJobDetails(jobDetails);
factoryBean.setOverwriteExistingJobs(true);
} catch (Exception e) {
Logger.error("加载 {} 配置文件失败.", QUARTZ_PROPERTIES_NAME, e);
throw new RuntimeException("加载配置文件失败", e);
}

return factoryBean;
}

@Bean(name = "job1Trigger")
public CronTriggerFactoryBean job1Trigger(@Qualifier("job1Detail") JobDetail jobDetail) {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(jobDetail);
cronTriggerFactoryBean.setCronExpression("0/15 * * * * ?");
return cronTriggerFactoryBean;
}

@Bean(name = "job1Detail")
public JobDetailFactoryBean job1Detail() {
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setJobClass(Job1.class);
jobDetailFactoryBean.setDurability(true);
return jobDetailFactoryBean;
}

@Bean(name = "job2Trigger")
public CronTriggerFactoryBean job2Trigger(@Qualifier("job2Detail") JobDetail jobDetail) {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(jobDetail);
cronTriggerFactoryBean.setCronExpression("0/5 * * * * ?");
return cronTriggerFactoryBean;
}

@Bean(name = "job2Detail")
public JobDetailFactoryBean job2Detail() {
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setJobClass(Job2.class);
jobDetailFactoryBean.setDurability(true);
return jobDetailFactoryBean;
}

@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource(QUARTZ_PROPERTIES_NAME));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}

class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

private transient AutowireCapableBeanFactory beanFactory;

@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}

@Override
protected Object createJobInstance(final TriggerFiredBundle bundle)
throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}
}


2.6 添加定时调度任务Job1(springms-simple-quartz-config-cluster\src\main\java\com\springms\cloud\job\Job1.java)

package com.springms.cloud.job;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

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

/**
* 定时调度任务Job1.
*
* @author hmilyylimh
*
* @version 0.0.1
*
* @date 2017/12/17
*
*/
@Component
public class Job1 extends QuartzJobBean {

@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {

System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
System.out.println("@@@@@@@@@@@@        "+(new SimpleDateFormat("yyyy-mm-dd HH:mm:ss.SSSSSS")).format(new Date())+"         @@@@@@@@@@@");
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@        Job1         @@@@@@@@@@@@@@@@@@@@@@");
System.out.println("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
System.out.println();
}
}


2.7 添加定时调度任务Job2(springms-simple-quartz-config-cluster\src\main\java\com\springms\cloud\job\Job2.java)

package com.springms.cloud.job;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;

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

/**
* 定时调度任务Job2.
*
* @author hmilyylimh
*
* @version 0.0.1
*
* @date 2017/12/17
*
*/
public class Job2 extends QuartzJobBean {

@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("==================================================================");
System.out.println("===========        "+(new SimpleDateFormat("yyyy-mm-dd HH:mm:ss.SSSSSS")).format(new Date())+"         ============");
System.out.println("=======================        Job2         ======================");
System.out.println("==================================================================");
System.out.println();
}
}


2.8 执行 Quartz 的 11 张表入数据库(springms-simple-quartz-config-cluster/quartz-tables.log)

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;

CREATE TABLE QRTZ_JOB_DETAILS
(
SCHED_NAME VARCHAR(120) NOT NULL,
JOB_NAME  VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
IS_DURABLE VARCHAR(1) NOT NULL,
IS_NONCONCURRENT VARCHAR(1) NOT NULL,
IS_UPDATE_DATA VARCHAR(1) NOT NULL,
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
JOB_NAME  VARCHAR(200) NOT NULL,
JOB_GROUP VARCHAR(200) NOT NULL,
DESCRIPTION VARCHAR(250) NULL,
NEXT_FIRE_TIME BIGINT(13) NULL,
PREV_FIRE_TIME BIGINT(13) NULL,
PRIORITY INTEGER NULL,
TRIGGER_STATE VARCHAR(16) NOT NULL,
TRIGGER_TYPE VARCHAR(8) NOT NULL,
START_TIME BIGINT(13) NOT NULL,
END_TIME BIGINT(13) NULL,
CALENDAR_NAME VARCHAR(200) NULL,
MISFIRE_INSTR SMALLINT(2) NULL,
JOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
REPEAT_COUNT BIGINT(7) NOT NULL,
REPEAT_INTERVAL BIGINT(12) NOT NULL,
TIMES_TRIGGERED BIGINT(10) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
CRON_EXPRESSION VARCHAR(200) NOT NULL,
TIME_ZONE_ID VARCHAR(80),
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
STR_PROP_1 VARCHAR(512) NULL,
STR_PROP_2 VARCHAR(512) NULL,
STR_PROP_3 VARCHAR(512) NULL,
INT_PROP_1 INT NULL,
INT_PROP_2 INT NULL,
LONG_PROP_1 BIGINT NULL,
LONG_PROP_2 BIGINT NULL,
DEC_PROP_1 NUMERIC(13,4) NULL,
DEC_PROP_2 NUMERIC(13,4) NULL,
BOOL_PROP_1 VARCHAR(1) NULL,
BOOL_PROP_2 VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
BLOB_DATA BLOB NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS
(
SCHED_NAME VARCHAR(120) NOT NULL,
CALENDAR_NAME  VARCHAR(200) NOT NULL,
CALENDAR BLOB NOT NULL,
PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
SCHED_NAME VARCHAR(120) NOT NULL,
TRIGGER_GROUP  VARCHAR(200) NOT NULL,
PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
(
SCHED_NAME VARCHAR(120) NOT NULL,
ENTRY_ID VARCHAR(95) NOT NULL,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
FIRED_TIME BIGINT(13) NOT NULL,
SCHED_TIME BIGINT(13) NOT NULL,
PRIORITY INTEGER NOT NULL,
STATE VARCHAR(16) NOT NULL,
JOB_NAME VARCHAR(200) NULL,
JOB_GROUP VARCHAR(200) NULL,
IS_NONCONCURRENT VARCHAR(1) NULL,
REQUESTS_RECOVERY VARCHAR(1) NULL,
PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
(
SCHED_NAME VARCHAR(120) NOT NULL,
INSTANCE_NAME VARCHAR(200) NOT NULL,
LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
CHECKIN_INTERVAL BIGINT(13) NOT NULL,
PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
(
SCHED_NAME VARCHAR(120) NOT NULL,
LOCK_NAME  VARCHAR(40) NOT NULL,
PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);

commit;


2.9 添加 Quartz-Cluster 启动类(springms-simple-quartz-config-cluster\src\main\java\com\springms\cloud\SimpleQuartzConfigClusterApplication.java)

package com.springms.cloud;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

/**
* 简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群。
*
* @author hmilyylimh
*
* @version 0.0.1
*
* @date 2017/12/17
*
*/
@SpringBootApplication
public class SimpleQuartzConfigClusterApplication {

private static final Logger Logger = LoggerFactory.getLogger(SimpleQuartzConfigClusterApplication.class);

public static void main(String[] args) {
SpringApplication.run(SimpleQuartzConfigClusterApplication.class, args);

System.out.println("【【【【【【 简单Quartz-Config-Cluster微服务 】】】】】】已启动.");
}
}


三、测试

/****************************************************************************************
一、简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群:

1、添加 Quartz 相关配置文件;
2、启动 springms-simple-quartz-config-cluster 模块服务,启动3个端口(8405、8406、8407);
3、然后看到 3 台服务器只有 1 台服务器调用了 Job1 类中的方法,因此 Quartz 的集群分布式也算是部署成功了;
4、然后关闭 1 台活跃 Quartz 服务器;
5、再等一会儿就看到 2 台服务器中的 1 台服务器每隔一定的时间调用 Job1 类中的方法;
****************************************************************************************/


四、下载地址

https://gitee.com/ylimhhmily/SpringCloudTutorial.git

SpringCloudTutorial交流QQ群: 235322432

SpringCloudTutorial交流微信群: 微信沟通群二维码图片链接

欢迎关注,您的肯定是对我最大的支持!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐