自己写spring-boot-starter-xxx

什么是Boot

过去只是用人家的xxx-spring-boot-starter,用着是挺爽的,但随着学习和工作,发现不管是流行的开源产品还是公司内部总会存在一些自己封装的starter,刚开始我也是很不解,一直觉得原本的就是最好的,根本不需要我们改什么,直到我学着去自己写starter,才发觉脚手架的意义就在这啊,这就是boot啊!

不多废话了,接下来从开源项目yudao-cloud说起怎么在开源框架的基础上封装自己的starter

尝试自己写starter

我们就从yudao-spring-boot-starter-job开始做一个属于自己的starter吧!

前提说明

SpringBoot基础的就不用多提了,需要注意的是不同版本会有一些差异,SpringBoot2.7之后自动配置类差异

依赖

主要就是spring核心依赖和xxl-job的依赖,第一个依赖是自己写的工具类,可以不用在意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?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>
<parent>
<groupId>cn.wnhyang.cloud</groupId>
<artifactId>okay-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>okay-spring-boot-starter-job</artifactId>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>cn.wnhyang.cloud</groupId>
<artifactId>okay-common</artifactId>
</dependency>

<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>

<!-- Job 相关 -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
</dependency>

<!-- 工具类相关 -->
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<scope>provided</scope>
</dependency>

</dependencies>

</project>

配置类

主要配置两项内容

  • xxl-job配置
  • spring异步线程

异步线程配置

Spring自带其自动配置,开启便可

1
2
3
4
@AutoConfiguration
@EnableAsync
public class AsyncAutoConfiguration {
}

XXL-JOB配置

xxl-job具体配置可参考其官网,这里做了简单的封装。

首先是属性配置类,将原先xxl-job的配置项进行分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@ConfigurationProperties("xxl.job")
@Validated
@Data
public class XxlJobProperties {
/**
* 是否开启,默认为 true 开启
*/
private Boolean enabled = true;
/**
* 访问令牌
*/
private String accessToken;
/**
* 控制器配置
*/
@NotNull(message = "控制器配置不能为空")
private AdminProperties admin;
/**
* 执行器配置
*/
@NotNull(message = "执行器配置不能为空")
private ExecutorProperties executor;

/**
* XXL-Job 调度器配置类
*/
@Data
@Valid
public static class AdminProperties {

/**
* 调度器地址
*/
@NotEmpty(message = "调度器地址不能为空")
private String addresses;

}

/**
* XXL-Job 执行器配置类
*/
@Data
@Valid
public static class ExecutorProperties {

/**
* 默认端口
* <p>
* 这里使用 -1 表示随机
*/
private static final Integer PORT_DEFAULT = -1;

/**
* 默认日志保留天数
* <p>
* 如果想永久保留,则设置为 -1
*/
private static final Integer LOG_RETENTION_DAYS_DEFAULT = 30;

/**
* 应用名
*/
@NotEmpty(message = "应用名不能为空")
private String appName;
/**
* 执行器的 IP
*/
private String ip;
/**
* 执行器的 Port
*/
private Integer port = PORT_DEFAULT;
/**
* 日志地址
*/
@NotEmpty(message = "日志地址不能为空")
private String logPath;
/**
* 日志保留天数
*/
private Integer logRetentionDays = LOG_RETENTION_DAYS_DEFAULT;

}

}

具体配置同于官网

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@AutoConfiguration
@ConditionalOnClass(XxlJobSpringExecutor.class)
@ConditionalOnProperty(prefix = "xxl.job", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties({XxlJobProperties.class})
@EnableScheduling
@Slf4j
public class XxlJobAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public XxlJobExecutor xxlJobExecutor(XxlJobProperties properties) {
log.info("[xxlJobExecutor][初始化 XXL-Job 执行器的配置]");
XxlJobProperties.AdminProperties admin = properties.getAdmin();
XxlJobProperties.ExecutorProperties executor = properties.getExecutor();

// 初始化执行器
XxlJobExecutor xxlJobExecutor = new XxlJobSpringExecutor();
xxlJobExecutor.setIp(executor.getIp());
xxlJobExecutor.setPort(executor.getPort());
xxlJobExecutor.setAppname(executor.getAppName());
xxlJobExecutor.setLogPath(executor.getLogPath());
xxlJobExecutor.setLogRetentionDays(executor.getLogRetentionDays());
xxlJobExecutor.setAdminAddresses(admin.getAddresses());
xxlJobExecutor.setAccessToken(properties.getAccessToken());
return xxlJobExecutor;
}

/**
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
*
* 1、引入依赖:
* <dependency>
* <groupId>org.springframework.cloud</groupId>
* <artifactId>spring-cloud-commons</artifactId>
* <version>${version}</version>
* </dependency>
*
* 2、配置文件,或者容器启动变量
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
*
* 3、获取IP
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
*/
}

从这里的配置来看真的和xxl-job官网配置几乎一样,相比来说,我理解的好处就是,可以默认设置一些配置属性,当然这个仅仅是对于开源项目封装最简单的一个例子,另外有些公司里的boot深入源码就不是这么简单的了。

自动化配置

项目resources/META-INF/springorg.springframework.boot.autoconfigure.AutoConfiguration.imports文件添加上面的Configuration类就好

1
2
3
cn.wnhyang.okay.framework.job.config.XxlJobAutoConfiguration
cn.wnhyang.okay.framework.job.config.AsyncAutoConfiguration

使用

依赖

添加自己的boot

1
2
3
4
<dependency>
<groupId>cn.wnhyang.cloud</groupId>
<artifactId>okay-spring-boot-starter-job</artifactId>
</dependency>

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
xxl.job.enabled=false
### 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxl.job.admin.addresses=http://123.60.165.53:8083/xxl-job-admin
### 执行器通讯TOKEN [选填]:非空时启用;
xxl.job.accessToken=default_token
### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
xxl.job.executor.appname=${spring.application.name}
### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
xxl.job.executor.ip=
### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
xxl.job.executor.port=9997
### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
xxl.job.executor.logpath=logs/xxl-job/jobhandler
### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
xxl.job.executor.logretentiondays=30

# 线程池配置
spring.task.execution.thread-name-prefix=task-
spring.task.execution.pool.core-size=8
spring.task.execution.pool.max-size=20
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.queue-capacity=200
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=true
spring.task.execution.shutdown.await-termination-period=60

这样就可以愉快的玩耍了

总结

虽然只是一个简单的例子,还是可以看出来自己写starter的意义的,目前我也是在学习开源项目的同时自己做一些简单的小项目玩,还觉得挺有意思的。