【SpringBoot实践】编写一个自定义的starter,简单聊聊自动装配原理
文章目录
- 1. 概述
- 2.自动装配原理及自定义starter编写
- 2.1.简单聊聊SpringBoot的自动装配原理
- 2.2.自定义starter编写
- 2.3.1.项目初始化及代码编写
- 2.3.2.配置文件编写
- 2.3.3.校验结果
- 3.总结
1. 概述
我们在使用SpringBoot做开发的时候经常会使用到starter,starter给我们提供了一些开箱即用的功能,例如我们想使用某个中间件的时候,只需要引入并在配置文件中写一些少量的配置就可以直接使用了,像Redis,Spring Data,mybatis以及RabbitMQ等等。
既然用来这么方便,那在我们做项目的脚手架的时候,通过starter做一些团队都能用上的基础包封装,后续团队开发起来需要使用的时候就方便了。那么本篇就来聊一聊如何的编写一个自定义的starter。
在之前的文章《XXL-JOB的执行器配置》中曾写到了,如果不想在每个使用到定时任务的服务中都去编写一套重复的配置,就可以使用SpringBoot的starter做一下封装,今天就来把这个坑填上。
使用版本为SpringBoot2的最后一个版本,2.7.18
2.自动装配原理及自定义starter编写
下面先简单的聊一下SpringBoot的自动装配原理是怎么回事,熟悉原理之后再自定义一个starer就是水到渠成的事情了。
2.1.简单聊聊SpringBoot的自动装配原理
我们可以知道的是,SpringBoot是通过扫描包获取的Bean定义,再经过实例化与初始化获得最终可以执行任务的bean对象,这种对Bean扫描再初始化的方式,依赖的是对扫描路径的配置(当然,SpringBoot的默认扫描路径就是Application类下的所有子包)。
那么问题来了,我们在使用三方jar包的时候,三方包的包路径与我们项目中的路径并不一致,这时候Spring应该如何扫描到jar包中Bean定义呢?
手动配置三方jar包的依赖路径倒是可以解决这个问题,但是不同的三方包,其包路径也不一样,我们不可能没新增一个新的包,就配置一下路径,这个时候就需要使用的SpringBoot的自动装配机制了。
自动装配机制有点类似于SPI的思想,简单的说,就是在需要封装成starter的项目的META-INF
加入一个配置文件,这个配置文件里面写了需要扫描的类的全类名,SpringBoot在启动的时候会获取到这个文件,扫描对应的类,并把需要加载的Bean加载到Spring的容器中。
配置文件的路径有两种,分别是:
META-INF/spring.factories
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
前者是相对旧一点的版本,并且在SpringBoot3.0后弃用,后者大概是在2.7版本后才新增,并且在3.0的时候正式使用(原因大概使用因为后者支持3.0的新特性,可以在编译时处理注解,性能优于前者)。
通过上图打开的文件,从左到右的顺序可以找到这行代码,同时也能找到下面的代码。
也就是说,需要扫描的Bean不管是写在其中的哪一个文件中,都可以被扫描到。
2.2.自定义starter编写
有了上面基础之后,就可以开始编写starter了,对于XXL-JOB执行器的封装,只需要简单的四步就可以了。
- 创建一个2.7.18版本的SpringBoot项目
- 将项目中的XXL-JOB执行器配置拷贝过来
- 编写配置文件
- 打包供其他项目引用
2.3.1.项目初始化及代码编写
初始化项目的时候需要注意命名规则,一般来说有两种规则:
- Spring官方的starter:一般是
spring-boot-starter-xxx
的形式 - 三方的starter:一般是
xxx-spring-boot-starter
的形式
可以看到,spring-boot-starter 一个在开头,一个在结尾,我们命名的时候也要注意这个规范,这里命名为my-xxl-job-spring-boot-starter
。
项目建好之后,将之前xxl-job文中的配置搬过来,新建两个类:
@ConfigurationProperties(prefix = "xxl.job.executor")
public class XxlJobProperties {/*** 在业务服务配置文件中配置*/private String appname;/*** 下面直接写死公司的配置*/private String adminAddresses = "http://ls.xxljob.cn/xxl-job-admin";private String accessToken = "default_token";private int port = 9999;private String logPath = "/data/applogs/xxl-job/jobhandler";private int logRetentionDays = 30;private String address ;private String ip;……忽略getter,setter
}
@Configuration
@EnableConfigurationProperties({XxlJobProperties.class})
public class XxlJobConfig {@Bean@ConditionalOnMissingBean@ConditionalOnProperty(prefix = "xxl.job.executor", value = "appname")public XxlJobSpringExecutor xxlJobExecutor(XxlJobProperties xxlJobProperties) {XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());xxlJobSpringExecutor.setAppname(xxlJobProperties.getAppname());xxlJobSpringExecutor.setAddress(xxlJobProperties.getAddress());xxlJobSpringExecutor.setIp(xxlJobProperties.getIp());xxlJobSpringExecutor.setPort(xxlJobProperties.getPort());xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());xxlJobSpringExecutor.setLogPath(xxlJobProperties.getLogPath());xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getLogRetentionDays());return xxlJobSpringExecutor;}
}
需要说明一下的是,@Conditional
是Spring中的一个条件注解,其作用是让Bean在满足一定条件之后才会加载到Spring容器中,下面例举几个常见的注解:
- @
ConditionalOnProperty
:根据配置文件中的属性是否存在、是否为某个值来加载 Bean。- prefix:配置属性的前缀
- value:属性名
- havingValue:当属性值等于指定值时条件成立
- matchIfMissing:属性缺失时,是否视为满足条件,默认为 false
@ConditonalOnMissingBean(xx.class)
:容器中没有某个bean对象时才执行创建,不指定则默认是当前方法的返回值对应的bean对象。@ConditionalOnBean(xx.class)
:与上面的相反,容器中有某个bean对象时才创建,用于需要一些依赖关系的时候。@ConditionalOnClass(name = "com.example.SomeClass")
:当指定类存在于类路径上时加载 Bean。@ConditionalOnMissingClass("com.example.SomeClass")
:当指定类不存在于类路径时加载 Bean。
2.3.2.配置文件编写
因为是2.7.x的版本,可以在resources
下创建配置文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
,并在配置文件中加入全类名
com.ls.config.XxlJobConfig
要在较低版本中实现自动配置,需要在 META-INF/spring.factories
文件中添加以下内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ls.config.XxlJobConfig
2.3.3.校验结果
配置完了之后的结构如下:
直接通过install打包后,用其他的SpringBoot引用,加入properties配置:
xxl.job.executor.appname=my-simple-executor
打好断点启动,成功进入断点,表示starter编写成功。
3.总结
本篇主要是简单介绍了一下SpringBoot的自动装配原理,同时通过XXL-JOB演示了一下如何创建一个自定义的starter,项目开发中可以通过这样的方式来创建一些脚手架工程。