一.Spring Boot Starter简介

Starter是Spring Boot中的一个非常重要的概念,Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的Bean根据环境( 条件)进行自动配置。使用者只需要依赖相应功能的Starter,无需做过多的配置和依赖,Spring Boot就能自动扫描并加载相应的模块。
总结:

1.它整合了这个模块需要的依赖库;
2.提供对模块的配置项给使用者;
3.提供自动配置类对模块内的Bean进行自动装配;

例如,在Maven的依赖中加入spring-boot-starter-web就能使项目支持Spring MVC,并且Spring Boot还为我们做了很多默认配置,无需再依赖spring-web、spring-webmvc等相关包及做相关配置就能够立即使用起来。

二.Starter的开发步骤

编写Starter非常简单,与编写一个普通的Spring Boot应用没有太大区别,总结如下:

1.新建Maven项目,在项目的POM文件中定义使用的依赖;
2.新建配置类,写好配置项和默认的配置值,指明配置项前缀;
3.新建自动装配类,使用@Configuration和@Bean来进行自动装配;
4.新建spring.factories文件,指定Starter的自动装配类;

三.Starter的开发示例

下面,我就以创建一个自动配置并连接ElasticSearch的Starter来讲一下各个步骤及细节。
1.新建Maven项目,在项目的POM文件中定义使用的依赖。

    <?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">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>es-starter</artifactId>
<version>1.0.0.SNAPSHORT</version> <dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.4.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency> <dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>x-pack-transport</artifactId>
<version>5.6.4</version>
</dependency>
</dependencies>
</project>

由于本starter主要是与ElasticSearch建立连接,获得TransportClient对象,所以需要依赖x-pack-transport包。

2.新建配置类,写好配置项和默认的配置值,指明配置项前缀。

package cn.sxw.commons.data.es.starter;

import org.springframework.boot.context.properties.ConfigurationProperties;

import lombok.Data;

/**
* Created by William on 2018/8/7.
*/
@Data
@ConfigurationProperties(prefix = "sxw.elasticsearch")
public class ElasticSearchProperties { private String clusterName = "elasticsearch"; private String clusterNodes = "127.0.0.1:9300"; private String userName = "elastic"; private String password = "changeme"; }

指定配置项前缀为sxw.elasticsearch,各配置项均有默认值,默认值可以通过模块使用者的配置文件进行覆盖。

3.新建自动装配类,使用@Configuration@Bean来进行自动装配。

package cn.sxw.commons.data.es.starter;

import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.stream.Collectors; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; /**
* Created by William on 2018/8/7.
*/
@Slf4j
@Configuration
@EnableConfigurationProperties(ElasticSearchProperties.class)
public class ElasticSearchAutoConfiguration implements DisposableBean{ private TransportClient transportClient;
@Resource
private ElasticSearchProperties properties; @Bean
@ConditionalOnMissingBean(TransportClient.class)
public TransportClient transportClient() {
log.debug("=======" + properties.getClusterName());
log.debug("=======" + properties.getClusterNodes());
log.debug("=======" + properties.getUserName());
log.debug("=======" + properties.getPassword());
log.info("开始建立es连接");
transportClient = new PreBuiltXPackTransportClient(settings());
TransportAddress[] transportAddresses= Arrays.stream(properties.getClusterNodes().split(",")).map (t->{
String[] addressPortPairs = t.split(":");
String address = addressPortPairs[0];
Integer port = Integer.valueOf(addressPortPairs[1]);
try {
return new InetSocketTransportAddress(InetAddress.getByName(address), port);
} catch (UnknownHostException e) {
log.error("连接ElasticSearch失败", e);
throw new RuntimeException ("连接ElasticSearch失败",e);
}
}).collect (Collectors.toList ()).toArray (new TransportAddress[0]);
transportClient.addTransportAddresses(transportAddresses);
return transportClient;
} private Settings settings() {
return Settings.builder()
.put("cluster.name", properties.getClusterName())
.put("xpack.security.user", properties.getUserName() +
":" + properties.getPassword())
.build();
} @Override
public void destroy() throws Exception {
log.info("开始销毁Es的连接");
if (transportClient != null) {
transportClient.close();
}
}
}

本类主要对TransportClient类进行自动配置;
@ConditionalOnMissingBean 当Spring容器中没有TransportClient类的对象时,调用transportClient()创建对象;
关于更多Bean的条件装配用法请自行查阅Spring Boot相关文档;

4.新建spring.factories文件,指定Starter的自动装配类。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.sxw.commons.data.es.starter.ElasticSearchAutoConfiguration

spring.factories文件位于resources/META-INF目录下,需要手动创建;
org.springframework.boot.autoconfigure.EnableAutoConfiguration后面的类名说明了自动装配类,如果有多个 ,则用逗号分开;
使用者应用(SpringBoot)在启动的时候,会通过org.springframework.core.io.support.SpringFactoriesLoader读取classpath下每个Starter的spring.factories文件,加载自动装配类进行Bean的自动装配;

至此,整个Starter开发完毕,Deploy到中央仓库或Install到本地仓库后即可使用。

四.Starter的使用

1.创建Maven项目,依赖刚才发布的es-starter包。

<?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">
<parent>
<artifactId>spring-boot-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.0.4.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion> <artifactId>es-example</artifactId>
<version>1.0.0-SNAPSHOT</version> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>es-starter</artifactId>
<version>1.0.0.SNAPSHORT</version>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

只需依赖刚才开发的es-starter即可

2.编写应用程序启动类。

package cn.sxw.commons.data.es.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan; /**
* Created by William on 2018/8/7.
*/
@SpringBootApplication
@ComponentScan("cn.sxw.commons.data.es.example")
public class ExampleApplication { public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}

@SpringBootApplication由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解组合而成,其中@EnableAutoConfiguration注解让Spring Boot根据类路径中的jar包依赖为当前项目进行自动配置。

3.编写查询ElasticSearch的使用类

package cn.sxw.commons.data.es.example;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component; import java.util.Map; import lombok.extern.slf4j.Slf4j; /**
* Created by William on 2018/8/7.
*/
@Slf4j
@Component
public class ExampleRunner implements ApplicationRunner { private static final String INDEX_NAME = "tb_question"; @Autowired
private TransportClient transportClient; @Override
public void run(ApplicationArguments applicationArguments) throws Exception {
SearchResponse response = transportClient.prepareSearch(INDEX_NAME)
.setTypes(INDEX_NAME)
.setQuery(QueryBuilders.matchAllQuery())
.setFrom(0).setSize(5).execute().actionGet();
SearchHits hits = response.getHits();
log.info(String.format("=======总共找到%d条记录", hits.getTotalHits()));
log.info("=======第一页数据:");
for (SearchHit searchHit : hits) {
Map<String, Object> source = searchHit.getSource();
String question = source.get("question").toString();
log.info(question);
}
}
}

通过实现ApplicationRunner或CommandLineRunner接口,可以实现应用程序启动完成后自动运行run方法,达到测试es-starter模块目的。
索引名称tb_question是公司测试环境ElasticSearch中的索引,已存在数据。

4.应用程序配置

sxw:
elasticsearch:
cluster-name: docker-cluster
cluster-nodes: 192.168.2.180:9300,192.168.2.181:9300
user-name: elastic
password: changeme

在application.yml文件中配置es-starter需要的配置信息,这里连接公司测试环境中的ElasticSearch。
这里配置的值可以覆盖es-starter中默认值,也就是之前ElasticSearchProperties文件中的默认值。

5.运行程序测试

/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/bin/java "-javaagent:/Applications/开发/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=52434:/Applications/开发/IntelliJ IDEA.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath cn.sxw.commons.data.es.example.ExampleApplication
objc[2017]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/bin/java (0x1022a24c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x1023254e0). One of the two will be used. Which one is undefined. . ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.4.RELEASE) 2018-08-08 16:26:43.161 INFO 2017 --- [ main] c.s.c.d.es.example.ExampleApplication : Starting ExampleApplication on William.local with PID 2017 (/Users/William/Git/sxw-java/es-spring-boot-starter/es-example/target/classes started by William in /Users/William/Git/sxw-java/es-spring-boot-starter)
2018-08-08 16:26:43.167 INFO 2017 --- [ main] c.s.c.d.es.example.ExampleApplication : No active profile set, falling back to default profiles: default
2018-08-08 16:26:43.365 INFO 2017 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@635eaaf1: startup date [Wed Aug 08 16:26:43 CST 2018]; root of context hierarchy
2018-08-08 16:26:45.078 INFO 2017 --- [ main] s.c.d.e.s.ElasticSearchAutoConfiguration : =======docker-cluster
2018-08-08 16:26:45.079 INFO 2017 --- [ main] s.c.d.e.s.ElasticSearchAutoConfiguration : =======192.168.2.180:9300,192.168.2.181:9300
2018-08-08 16:26:45.081 INFO 2017 --- [ main] s.c.d.e.s.ElasticSearchAutoConfiguration : =======elastic
2018-08-08 16:26:45.081 INFO 2017 --- [ main] s.c.d.e.s.ElasticSearchAutoConfiguration : =======changeme
2018-08-08 16:26:45.082 INFO 2017 --- [ main] s.c.d.e.s.ElasticSearchAutoConfiguration : 开始建立es连接
2018-08-08 16:26:46.200 INFO 2017 --- [ main] o.elasticsearch.plugins.PluginsService : no modules loaded
2018-08-08 16:26:46.201 INFO 2017 --- [ main] o.elasticsearch.plugins.PluginsService : loaded plugin [org.elasticsearch.index.reindex.ReindexPlugin]
2018-08-08 16:26:46.202 INFO 2017 --- [ main] o.elasticsearch.plugins.PluginsService : loaded plugin [org.elasticsearch.join.ParentJoinPlugin]
2018-08-08 16:26:46.202 INFO 2017 --- [ main] o.elasticsearch.plugins.PluginsService : loaded plugin [org.elasticsearch.percolator.PercolatorPlugin]
2018-08-08 16:26:46.202 INFO 2017 --- [ main] o.elasticsearch.plugins.PluginsService : loaded plugin [org.elasticsearch.script.mustache.MustachePlugin]
2018-08-08 16:26:46.202 INFO 2017 --- [ main] o.elasticsearch.plugins.PluginsService : loaded plugin [org.elasticsearch.transport.Netty3Plugin]
2018-08-08 16:26:46.202 INFO 2017 --- [ main] o.elasticsearch.plugins.PluginsService : loaded plugin [org.elasticsearch.transport.Netty4Plugin]
2018-08-08 16:26:46.202 INFO 2017 --- [ main] o.elasticsearch.plugins.PluginsService : loaded plugin [org.elasticsearch.xpack.XPackPlugin]
2018-08-08 16:26:49.137 INFO 2017 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2018-08-08 16:26:49.157 INFO 2017 --- [ main] c.s.c.d.es.example.ExampleApplication : Started ExampleApplication in 6.6 seconds (JVM running for 7.915)
2018-08-08 16:26:49.215 INFO 2017 --- [ main] c.s.c.data.es.example.ExampleRunner : =======总共找到907条记录
2018-08-08 16:26:49.215 INFO 2017 --- [ main] c.s.c.data.es.example.ExampleRunner : =======第一页数据:
2018-08-08 16:26:49.230 INFO 2017 --- [ main] c.s.c.data.es.example.ExampleRunner : <p>下列诗句朗读节奏有错误的一项是( )</p>
2018-08-08 16:26:49.230 INFO 2017 --- [ main] c.s.c.data.es.example.ExampleRunner : <p><span style=";font-family:宋体;color:rgb(0,0,0);font-size:14px"><span style="font-family:宋体">《卧薪尝胆》这个故事出自于(</span> A &nbsp;&nbsp;<span style="font-family:宋体">)</span></span></p><p><span style=";font-family:宋体;color:rgb(0,0,0);font-size:14px">A<span style="font-family:宋体">、司马迁《史记》 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family:Times New Roman">B</span><span style="font-family:宋体">、司马光 《资治通鉴》</span></span></p><p><span style=";font-family:宋体;color:rgb(0,0,0);font-size:14px">C<span style="font-family:宋体">、孔子 &nbsp;&nbsp;《论语》 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-family:Times New Roman">D</span><span style="font-family:宋体">、司马迁《春秋》</span></span></p><p><br/></p>
2018-08-08 16:26:49.230 INFO 2017 --- [ main] c.s.c.data.es.example.ExampleRunner : <p style="margin-bottom:7px;margin-bottom:auto;vertical-align:middle"><span style=";font-family:'Cambria Math';font-size:14pxfont-family:宋体,新宋体">填空题</span></p><p style="margin-bottom:7px;margin-bottom:auto;vertical-align:middle"><span style=";font-family:'Cambria Math';font-size:14pxfont-family:宋体,新宋体">该模式给当地带来的主要影响是 </span><span style=";font-family:'Cambria Math';font-size:14px"><br/></span><br/></p><p><br/></p>
2018-08-08 16:26:49.231 INFO 2017 --- [ main] c.s.c.data.es.example.ExampleRunner : <p>下列词语没有错别字的一项是( )</p>
2018-08-08 16:26:49.231 INFO 2017 --- [ main] c.s.c.data.es.example.ExampleRunner : <p>语文第16题</p>
2018-08-08 16:26:49.232 INFO 2017 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@635eaaf1: startup date [Wed Aug 08 16:26:43 CST 2018]; root of context hierarchy
2018-08-08 16:26:49.234 INFO 2017 --- [ Thread-2] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
2018-08-08 16:26:49.328 INFO 2017 --- [ Thread-2] s.c.d.e.s.ElasticSearchAutoConfiguration : 开始销毁Es的连接 Process finished with exit code 0

运行程序,观察控制台输出,es-starter成功与ElasticSearch建立连接,且应用程序启动完后ExampleRunner的run方法查询出5条数据。

作者:liushiping
链接:https://www.jianshu.com/p/bbf439c8a203

从零开始开发一个Spring Boot Starter的更多相关文章

  1. 开发一个Spring Boot Starter!

    在上一篇文章中,我们已经了解了一个starter实现自动配置的基本流程,在这一小结我们将复现上一过程,实现一个自定义的starter. 先来分析starter的需求: 在项目中添加自定义的starte ...

  2. 自己写一个spring boot starter

    https://blog.csdn.net/liuchuanhong1/article/details/55057135

  3. 年轻人的第一个自定义 Spring Boot Starter!

    陆陆续续,零零散散,栈长已经写了几十篇 Spring Boot 系列文章了,其中有介绍到 Spring Boot Starters 启动器,使用的.介绍的都是第三方的 Starters ,那如何开发一 ...

  4. 创建自己的Spring Boot Starter

    抽取通用模块作为项目的一个spring boot starter.可参照mybatis的写法. IDEA创建Empty Project并添加如下2个module,一个基本maven模块,另一个引入sp ...

  5. 最详细的自定义Spring Boot Starter开发教程

    1. 前言 随着Spring的日渐臃肿,为了简化配置.开箱即用.快速集成,Spring Boot 横空出世. 目前已经成为 Java 目前最火热的框架了.平常我们用Spring Boot开发web应用 ...

  6. 手把手教你手写一个最简单的 Spring Boot Starter

    欢迎关注微信公众号:「Java之言」技术文章持续更新,请持续关注...... 第一时间学习最新技术文章 领取最新技术学习资料视频 最新互联网资讯和面试经验 何为 Starter ? 想必大家都使用过 ...

  7. 一个简单易上手的短信服务Spring Boot Starter

    前言 短信服务在用户注册.登录.找回密码等相关操作中,可以让用户使用更加便捷,越来越多的公司都采用短信验证的方式让用户进行操作,从而提高用户的实用性. Spring Boot Starter 由于 S ...

  8. Spring Boot Starter 开发指南

    Spring Boot Starter是什么? 依赖管理是任何复杂项目的关键部分.以手动的方式来实现依赖管理不太现实,你得花更多时间,同时你在项目的其他重要方面能付出的时间就会变得越少. Spring ...

  9. 从零开始的Spring Boot(1、搭建一个Spring Boot项目Hello World)

    搭建一个Spring Boot项目Hello World 写在前面 从零开始的Spring Boot(2.在Spring Boot中整合Servlet.Filter.Listener的方式):http ...

随机推荐

  1. Asp.net Core CORS 跨域

    本文主要介绍在Asp.net Core采用CORS方式解决跨域 关于跨域的原理介绍可参考Asp.net Web API 解决跨域详解 1 在Startup添加允许跨域的策略 services.AddC ...

  2. linux 下用C实现 ATM 自动取款机功能 (进程间通信)

    直接先上图: 项目需求: 主要分为两人大模块: 客户端 .进入时的功能开户.销户.登录.解锁 开户:输入姓名.身份证号.设置密码,如果开户成功,则服务器上保存一个账号信号(一个账号存一个文件,文件名建 ...

  3. Manjaro 安装笔记

    双系统基本知识 [折腾日记]win10 ,ubuntu双系统安装避坑指南 Windows 下安装 Ubuntu 双系统(更新) rEFInd 总结注意点: 制作U盘启动盘一开始使用的是 Ulrstro ...

  4. 阿里云服务器远程连接错误:由于一个协议错误(代码:0x112f),远程会话将被中断。

    2019年10月,阿里云服务器远程连接忽然无法登录.当时正在清理c盘空间,C盘只剩下30+M,忽然远程桌面掉线,以为断网了,再次远程桌面连接时,就出现一下错误. 解决方案:万能的重启!!!具体错误原因 ...

  5. Android自动化测试探索(一)adb详细介绍

    adb详细介绍 #1. 基本简介 adb,即Android Debug Bridge,它是Android开发/测试人员不可替代的强大工具 #2. Mac上安装adb 安装brew /usr/bin/r ...

  6. 腾讯云服务器搭建WampServer环境

    软件环境Windows Server 2008 R2 企业版 SP1 64位 刚刚进入 Windows Server ,你会看到以下界面: 列出了服务器的基础信息和常用配置下载 XAMPP https ...

  7. python接口自动化11-post传data参数案例

    前言: 前面登录博客园的是传json参数,有些登录不是传json的,如jenkins的登录,本篇以jenkins登录为案例,传data参数. 一.登录jenkins抓包 1.登录jenkins,输入账 ...

  8. 使用Junit测试框架学习Java

    前言 在日常的开发中,离不开单元测试,而且在学习Java时,特别是在测试不同API使用时要不停的写main方法,显得很繁琐,所以这里介绍使用Junit学习Java的方法.此外,我使用log4j将结果输 ...

  9. Windows通过SSH远程登录Linux主机

    准备工作:1.Windows系统下装有VMware虚拟机且是Linux系统2.终端连接工具Xshell 63.本次实验系统IP如下 系统 IP Windows10 192.168.37.111 Cen ...

  10. 关于CSDN的一些信息

    CSDN资源共享规范 为广大用户提供资源(包括但不限于文章.文档.音频.视频.图片.课程.软件.源代码等相关资源)共享的网络存储平台 与不得违反国家法律法规相关的规定 不得上传与CSDN提供的服务内容 ...