什么是starter呢?
Spring Boot 对比 Spring MVC 最大的优点就是使用简单,约定大于配置。不会像之前用 Spring MVC 的时候,时不时被 xml 配置文件搞的晕头转向,冷不防还因为 xml 配置上的一点疏忽,导致整个项目莫名其妙的不可用,顿感生活无所依恋,简称生无可恋。
这要归功于组成了 Spring Boot 的各种各样的 starters,有官方提供的,也有第三方开源出来。可以这么说,基本上你打算用的功能都可以找到,如果没有找到,那就再找一找。
用 Spring Boot 的功能组件(例如 spring-boot-starter-actuator、 spring-boot-starter-data-redis 等)的步骤非常简单,用著名的把大象放冰箱的方法来概括的话,有以下三步就可以完成组件功能的使用:
Step1
在 pom 文件中引入对应的包,例如:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
Step2
在peroperties或者yml中加入相应的配置,配置都是组件约定好的,需要查看官方文档或者相关说明。
Step3
以上两步都正常的情况下,我们就可以使用组件提供的相关接口来开发业务功能了。
没错吧,这个过程我们在日常的开发中不知道已经实践了多少遍。那么 Spring Boot 为什么能做到如此简单易用呢,它内部是什么样的工作机制呢,不知道你有没有研究过。
@Autowired
RedisTemplete redisTemplete;
开始之前,我们要理解一下 spring boot starter 是什么呢?
它把需要用的其他依赖都囊括进来,放在starter类里面的pom中,把各种依赖的关系都给确定了,打通各个组件之间的连接,并且在中间帮我们省去了很多配置,力图做到使用最简单。
实现一个 starter 有四个要素:
1.starter 命名 ;
2.自动配置类,用于自动创建bean,并交给spring容器 ;
3.自动配置类需要的配置文件 spring.factories ;
4.业务类(参考redisTemplete类) ;
1. 给 starter 起个名字
官方的 starter 的命名格式为:spring-boot-starter-xxx
非官方的 starter 的命名格式为:xxx-spring-boot-starter
你不按人家规范取也行,只是个规范。
我这里取名为plusroax
<groupId>com.plusroax</groupId>
<artifactId>plusroax-spring-boot-starter</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
2. 引入自动配置包及其它相关依赖包
实现 starter 主要依赖自动配置注解,所以要在 pom 中引入自动配置相关的两个 jar 包**【必须引入】**
除此之外,根据你的业务引入你需要的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
3.配置类
此类是读取properties、yml中数据的。比如需要读取配置的ip和port属性,可以这么写:
@Data
@ConfigurationProperties("plusroax.address")
public class KiteProperties {
private String host;
private int port;
}
@ConfigurationProperties配置的是yml、properties中属性的前缀
4.业务类
此类是你的核心业务类,别人引入你的starter,是想用到你的方法,比如redisTemplete。
我这里就把从properties、yml中获取的数据打印出来。
public class MyTemplete {
private String host;
private int port;
public MyTemplete (MyProperties myProperties){
this.host = myProperties.getHost();
this.port = myProperties.getPort();
}
public void print(){
System.out.println(this.host + ":" +this.port);
}
}
5.自动配置类
此类是关键,能否自动创建bean,就看这个类。
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfigure {
@Autowired
private MyProperties myProperties;
/**
* 下面是要创建的bean,注意!只有满足注解上的条件,才会去自动创建bean
*/
@Bean
@ConditionalOnMissingBean(MyService.class)
@ConditionalOnProperty(prefix = "plusroax.address",value = "enabled", havingValue = "true",matchIfMissing = true)
MyTemplete myTemplete (){
return new MyTemplete (myProperties);
}
}
@Configuration:表明这是一个配置类
@ConditionalOnClass(MyService.class:只有在当前类路径下存在MyService这个类的情况下,才会解析该该自动配置类(MyAutoConfigure),否则不解析。
@EnableConfigurationProperties(MyProperties.class) :用哪个类读取yml、properties中配置的参数
@ConditionalOnMissingBean(MyService.class) :当前上下文还没有 MyService的 bean 实例的情况下,才会执行 kiteService() 方法,从而实例化一个 bean 实例出来。反则不会执行,自然也不会创建
@ConditionalOnProperty(prefix = “plusroax.address”,value = “enabled”, havingValue = “true”,matchIfMissing = true) :绑定指定前缀去读取,matchIfMissing默认为false,只有去配置plusroax.address.enabled=true才会创建bean。改成ture,无论如何都会创建bean
6.创建 spring.factories 文件
在 resource/META-INF 目录下创建名称为 spring.factories 的文件,当 Spring Boot 启动的时候,会在 classpath 下寻找所有名称为 spring.factories 的文件。在这里指定的自动配置类,多个值使用逗号隔开。
例如本例中的配置信息是这样的:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.plusroax.autoconfig.MyAutoConfigure
=号前面的是固定的;后面是配置你的全路径类名
这里的 反斜杠 是换行的意思,不想换行就直接把全路径类名写在等号后面。
7.打包项目(install)
打包完之后,就大功告成了,你已经会手写starter。
starter的使用(别说你不会)
1.在别的项目中引入依赖
<dependency>
<groupId>com.plusroax</groupId>
<artifactId>plusroax-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2.配置yml或者properties
server:
port: 8848
plusroax:
address:
enabled: true # 开启才生效
host: 127.0.0.1
port: 8848
3.使用controller测试一下
我们引入的自己写的starter,已经把MyTemplete放入容器中了,可以直接用。
@RestController
@RequestMapping(value = "use")
public class UseController {
@Autowired
private MyTemplete myTemplete;
@GetMapping(value = "print")
public void print(){
myTemplete.print();
}
}