title: Springboot自定义启动器
author: Xoni
tags:
Spring Boot Starter是在SpringBoot组件中被提出来的一种概念,stackoverflow上面已经有人概括了这个starter是什么东西,想看完整的回答戳
Starter POMs are a set of convenient dependency descriptors that you can include in your application. You get a one-stop-shop for all the Spring and related technology that you need, without having to hunt through sample code and copy paste loads of dependency descriptors. For example, if you want to get started using Spring and JPA for database access, just include the spring-boot-starter-data-jpa dependency in your project, and you are good to go.
大概意思就是说starter是一种对依赖的synthesize(合成),这是什么意思呢?我可以举个例子来说明。
在没有starter之前,假如我想要在Spring中使用jpa,那我可能需要做以下操作:
需要注意的是,这里操作在我们每次新建一个需要用到jpa的项目的时候都需要重复的做一次。也许你在第一次自己建立项目的时候是在Google上自己搜索了一番,花了半天时间解决掉了各种奇怪的问题之后,jpa终于能正常运行了。有些有经验的人会在OneNote上面把这次建立项目的过程给记录下来,包括操作的步骤以及需要用到的配置文件的内容,在下一次再创建jpa项目的时候,就不需要再次去Google了,只需要照着笔记来,之后再把所有的配置文件copy&paste就可以了。
像上面这样的操作也不算不行,事实上我们在没有starter之前都是这么干的,但是这样做有几个问题:
starter的主要目的就是为了解决上面的这些问题。
starter会把所有用到的依赖都给包含进来,避免了开发者自己去引入依赖所带来的麻烦。需要注意的是不同的starter是为了解决不同的依赖,所以它们内部的实现可能会有很大的差异,例如jpa的starter和Redis的starter可能实现就不一样,这是因为starter的本质在于synthesize,这是一层在逻辑层面的抽象,也许这种理念有点类似于Docker,因为它们都是在做一个“包装”的操作,如果你知道Docker是为了解决什么问题的,也许你可以用Docker和starter做一个类比。
上面的starter依赖的jar和我们自己手动配置的时候依赖的jar并没有什么不同,所以我们可以认为starter其实是把这一些繁琐的配置操作交给了自己,而把简单交给了用户。除了帮助用户去除了繁琐的构建操作,在“约定大于配置”的理念下,ConfigurationProperties还帮助用户减少了无谓的配置操作。并且因为 application.properties
文件的存在,即使需要自定义配置,所有的配置也只需要在一个文件中进行,使用起来非常方便。
了解了starter其实就是帮助用户简化了配置的操作之后,要理解starter和被配置了starter的组件之间并不是竞争关系,而是辅助关系,即我们可以给一个组件创建一个starter来让最终用户在使用这个组件的时候更加的简单方便。基于这种理念,我们可以给任意一个现有的组件创建一个starter来让别人在使用这个组件的时候更加的简单方便,事实上Spring Boot团队已经帮助现有大部分的流行的组件创建好了它们的starter,你可以在这里查看这些starter的列表。
springboot项目会有很多spring-boot-starter打头的引用,这些引用是干什么用的,都有哪些,带着这些问题去官网看了一下官方文档,现将结果整理一下。
引用 | 说明 |
---|---|
spring-boot-starter | 核心启动器,包括自动配置支持,日志记录和YAML |
spring-boot-starter-activemq | 使用Apache ActiveMQ的消息队列 |
spring-boot-starter-amqp | 使用Spring AMQP和Rabbit MQ消息队列 |
spring-boot-starter-aop | 使用SpringAOP和AspectJ进行切面编程包 |
spring-boot-starter-artemis | 使用Apache Artemis的消息队列 |
spring-boot-starter-batch | 使用轻量级的大数据量的并行处理(批处理)框架SpringBatch |
spring-boot-starter-cache | Spring缓存框架 |
spring-boot-starter-cloud-connectors | Spring Cloud Connectors,云连接器,便于PaaS应用在各种平台上连接到后端像数据库和消息经纪服务 |
spring-boot-starter-data-cassandra | NoSQL数据库cassandra |
spring-boot-starter-data-cassandra-reactive | NoSQL数据库cassandra响应式编程 |
spring-boot-starter-data-couchbase | NoSQL数据库Couchbase |
spring-boot-starter-data-couchbase-reactive | NoSQL数据库Couchbase响应式编程 |
spring-boot-starter-data-elasticsearch | 使用Elasticsearch搜索和分析引擎 |
spring-boot-starter-data-jdbc | 使用jdbc连接数据库进行数据支持久化 |
spring-boot-starter-data-jpa | 使用jpa连接数据库进行数据支持久化 |
spring-boot-starter-data-ldap | 使用ldap目录服务 |
spring-boot-starter-data-mongodb | 使用NoSQL数据库mongodb |
spring-boot-starter-data-mongodb-reactive | 使用NoSQL数据库mongodb响应式编程 |
spring-boot-starter-data-neo4j | 使用NoSQL数据库neo4j |
spring-boot-starter-data-redis | 使用NoSQL数据库redis |
spring-boot-starter-data-redis-reactive | 使用NoSQL数据库redis响应式编程 |
spring-boot-starter-data-rest | rest风格使用springData |
spring-boot-starter-data-solr | 使用solr搜索和分析引擎 |
spring-boot-starter-freemarker | 使用FreeMarker视图构建MVC Web应用程序 |
spring-boot-starter-groovy-templates | 使用Groovy模板视图构建MVC Web应用程序 |
spring-boot-starter-hateoas | 使用SpringMVC和SpringHATEOAS构建基于超媒体的RESTful Web应用程序 |
spring-boot-starter-integration | 使用SpringIntegration,类似消息队列,用于企业集成的框架 |
spring-boot-starter-jdbc | 使用jdbc连接池 |
spring-boot-starter-jersey | 使用jersey构建基于超媒体的RESTful Web应用程序 |
spring-boot-starter-jooq | 使用jooq数据库ORM框架 |
spring-boot-starter-json | 支持json读写 |
spring-boot-starter-jta-atomikos | 使用atomikos解决分布式事务 |
spring-boot-starter-jta-bitronix | 使用bitronix解决分布式事务 |
spring-boot-starter-mail | 使用Java Mail发送邮件 |
spring-boot-starter-mustache | 使用mustache轻量级模板语言视图构建MVC Web应用程序 |
spring-boot-starter-oauth2-client | 使用oauth2授权框架客户端 |
spring-boot-starter-oauth2-resource-server | 使用oauth2授权框架服务端 |
spring-boot-starter-quartz | 使用quartz任务调度框架 |
spring-boot-starter-security | 使用security安全框架 |
spring-boot-starter-test | 使用单元测试包含JUnit、 Hamcrest和 Mockito |
spring-boot-starter-thymeleaf | 使用thymeleaf模板语言视图构建MVC Web应用程序 |
spring-boot-starter-validation | 使用基于Hibernate Validator的类数据校验 |
spring-boot-starter-web | 构建网站开发,包括RESTful,Spring MVC,使用Tomcat作为默认嵌入容器 |
spring-boot-starter-web-services | 使用WebServices |
spring-boot-starter-webflux | 使用WebFlux响应式编程,与springMVC功能类似 |
spring-boot-starter-websocket | 使用websocket通讯服务 |
spring-boot-starter-actuator | 使用actuator监控应用健康 |
spring-boot-starter-jetty | 使用jetty作为嵌入式容器 |
spring-boot-starter-log4j2 | 使用log4j2记录日志 |
spring-boot-starter-logging | 使用logging记录日志 默认 |
spring-boot-starter-reactor-netty | 使用netty作为嵌入式容器 |
spring-boot-starter-tomcat | 使用tomcat作为嵌入式容器 默认 |
spring-boot-starter-undertow | 使用undertow作为嵌入式容器 |
@Configuration //指定这个类是一个配置类
@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
@ConfigurationPropertie//结合相关xxxProperties类来绑定相关的配置
自动配置类要能加载
将需要启动就加载的自动配置类,配置在META‐INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
启动器只用来做依赖导入,专门来写一个自动配置模块,启动器依赖自动配置;别人只需要引入启动器(starter),mybatis-spring-boot-starter;自定义启动器名-spring-boot-starter
启动器模块是一个空JAR 文件,仅提供辅助性依赖管理,这些依赖可能用于自动装配或者其他类库
推荐使用以下命名规约
如果你想要自己创建一个starter,那么基本上包含以下几步
项目名称是 httpclient-spring-boot-autoconfiguration
对HttpClient封装一个Starter
首先新建一个Maven项目,设置
pom.xml
文件如下:
<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">
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.2.RELEASEversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>httpclient-spring-boot-autoconfigurationartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpcoreartifactId>
dependency>
<dependency>
<groupId>commons-codecgroupId>
<artifactId>commons-codecartifactId>
<version>1.11version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
<version>3.4version>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>META-INF/*include>
includes>
resource>
resources>
build>
project>
创建proterties类来保存配置信息
//自动获取配置文件中前缀为httprequest的属性,把值传入对象参数
@ConfigurationProperties(prefix = "httprequest")
public class HttpRequestProperties {
//是否启动
private boolean enabled;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
/**
* Http工具类
*/
public class HttpRequestClient {
/**
* 发送POST请求
*
* @param url 请求url
* @param data 请求数据
* @return 结果
*/
@SuppressWarnings("deprecation")
public static String doPost(String url, String data) {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(10000).setConnectTimeout(20000)
.setConnectionRequestTimeout(10000).build();
httpPost.setConfig(requestConfig);
String context = StringUtils.EMPTY;
if (!StringUtils.isEmpty(data)) {
StringEntity body = new StringEntity(data, "utf-8");
httpPost.setEntity(body);
}
// 设置回调接口接收的消息头
httpPost.addHeader("Content-Type", "application/json");
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
context = EntityUtils.toString(entity, HTTP.UTF_8);
} catch (Exception e) {
e.getStackTrace();
} finally {
try {
response.close();
httpPost.abort();
httpClient.close();
} catch (Exception e) {
e.getStackTrace();
}
}
return context;
}
/**
* 解析出url参数中的键值对
*
* @param url url参数
* @return 键值对
*/
public static Map<String, String> getRequestParam(String url) {
Map<String, String> map = new HashMap<String, String>();
String[] arrSplit = null;
// 每个键值为一组
arrSplit = url.split("[&]");
for (String strSplit : arrSplit) {
String[] arrSplitEqual = null;
arrSplitEqual = strSplit.split("[=]");
// 解析出键值
if (arrSplitEqual.length > 1) {
// 正确解析
map.put(arrSplitEqual[0], arrSplitEqual[1]);
} else {
if (arrSplitEqual[0] != "") {
map.put(arrSplitEqual[0], "");
}
}
}
return map;
}
}
这个工具类很简单就是一个HttpClient的操作工具类
@Configuration
@EnableConfigurationProperties(HttpRequestProperties.class)
public class HttpAutoConfiguration {
// 在Spring上下文中创建一个对象
@Bean
//只有当httprequest.enabled=true时才创建对象
@ConditionalOnProperty(prefix = "httprequest", name = "enabled", havingValue = "true")
@ConditionalOnMissingBean(HttpRequestClient.class)
public HttpRequestClient getHttpRequestClient() {
return new HttpRequestClient();
}
}
在上面的AutoConfiguration中我们实现了自己要求:在Spring的上下文中创建了一个HttpClient类的bean,并且我们把properties中的一个参数赋给了该bean。
最后,我们在 resources
文件夹下新建目录 META-INF
,在目录中新建 spring.factories
文件,并且在 spring.factories
中配置AutoConfiguration:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.autoconfiguration.HttpAutoConfiguration
到此,我们的starter已经创建完毕了,使用Maven打包该项目。之后创建一个SpringBoot项目,在项目中添加我们之前打包的starter作为依赖,然后使用SringBoot来运行我们的starter,代码如下:
这个项目中只引入autoconfiguration项目,其他什么也不做
<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">
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.2.RELEASEversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>httpclient-springboot-starterartifactId>
<dependencies>
<dependency>
<groupId>org.examplegroupId>
<artifactId>httpclient-spring-boot-autoconfigurationartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
project>
导入Starter的坐标
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.2.RELEASEversion>
parent>
<groupId>org.examplegroupId>
<artifactId>HttpAutoConfigTestartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.examplegroupId>
<artifactId>httpclient-springboot-starterartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
dependencies>
project>
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
httprequest.enabled=true
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {Application.class})
public class ApplicationTest {
@Resource
private HttpRequestClient httpRequestClient;
@Test
public void test() {
String str = httpRequestClient.doPost("http://www.baidu.com", null);
System.out.println(str);
Assert.assertNotNull(str);
}
}
运行的时候我们发现直接就能够注入使用HttpRequestClient,不需要我们在每一个项目中进行配置httpclient了,很方便,如果不需要注入可以将httprequest.enabled设置为false或者去掉