我们都知道Spring Boot开发相当的便利,对于开发人员来说是一个福音,其实它就是通过引入各种的Spring Boot Starter包可以快速搭建出一个项目的脚手架。
目前提供的Spring Boot Starter包有很多,比如:
- spring-boot-starter-web : 快速构建基于Spring MVC的Web项目,使用Tomcat做默认嵌入式容器。
- spring-boot-starter-data-redis : 操作Redis。
- spring-boot-starter-data-mongodb : 操作mongodb。
- spring-boot-starter-data-jpa : 操作MySQL
- spring-boot-starter-activemq : 操作Activemq
- ……
自动配置虽然非常方便,但是自动配置麻烦的是出现错误时,排查问题的难度上升了。自动配置的逻辑都在Spring Boot Starter中,要想快速定位问题,就必须得了解Spring Boot Starter的内部原理。接下来我们自己手动实现一个Spring Boot Starter。
1. Spring Boot Starter项目创建
创建一个项目springboot-starter-demo,pom.xml配置代码如下:
1 2 3 4 5 6 7 8 9 10 11
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
|
创建一个配置类,用于在属性文件中配置值,代码如下:
1 2 3 4 5 6 7 8 9 10
| package com.self.springbootstarterdemo.properties;
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties;
@Data @ConfigurationProperties(prefix = "spring.user") public class UserProperties { private String name; }
|
再定义一个Client,里面定一个方法,用于获取配置中的值,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.self.springbootstarterdemo.client;
import com.self.springbootstarterdemo.properties.UserProperties;
public class UserClient {
private UserProperties userProperties;
public UserClient(){
}
public UserClient(UserProperties p){ this.userProperties = p; }
public String getName(){ return userProperties.getName(); } }
|
2. 自动创建客户端
以上就是最基本的一个Starter包定义,但目前肯定是不能使用UserClient,因为我们没有自动构建UserClient的实例。接下来开始构建UserClient,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.self.springbootstarterdemo.config;
import com.self.springbootstarterdemo.client.UserClient; import com.self.springbootstarterdemo.properties.UserProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration @EnableConfigurationProperties(UserProperties.class) public class UserAutoConfiguration {
@Bean @ConditionalOnProperty(prefix = "spring.user",value = "enabled",havingValue = "true") public UserClient userClient(UserProperties userProperties){ return new UserClient(userProperties); } }
|
在resources下创建一个META-INF文件夹,然后在META-INF文件夹中创建spring.factories文件,文件中指定自动配置的类:
1 2
| org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.self.springbootstarterdemo.config.UserAutoConfiguration
|
【注意:注意spring.factories文件内容的格式】
至此,Spring Boot启动时会去读取spring.factories文件,然后根据配置激活对应的配置类,这样,一个简单的Starter包就实现了。
3. 使用Starter
可以在其他项目中引入这个Starter包进行测试一下,代码如下:
1 2 3 4 5
| <dependency> <groupId>com.self</groupId> <artifactId>springboot-starter-demo</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
|
引入之后就直接可以使用UserClient,UserClient在项目启动的时候已经自动初始化好,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.self.springboot_item.controller;
import com.self.springbootstarterdemo.client.UserClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class UserController {
@Autowired public UserClient userClient;
@GetMapping("/user/name") public String getUsername(){ return userClient.getName(); } }
|
然后在属性文件中配置name的值和开启UserClient :
1 2
| spring.user.name=xiaozeng spring.user.enabled=true
|
访问/user/home 就可以返回我们配置的xiaozeng。
4. 使用注解开启Starter自动构建
很多时候我们不想在引入Starter包时就执行初始化的逻辑,而是想要由用户来指定是否要开启Starter包的自动配置功能,比如常用的@EnableAsync这个注解就是用于开启调用方法执行异步执行的功能。
同样地,我们也可以通过注解的方式来开启是否自动配置,如果用注解的方式,那么spring.factories就不需要编写了,下面就来看一下怎么定义启用自动配置的注解,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package com.self.springbootstarterdemo.config;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import({UserAutoConfigure.class}) public @interface EnableUserClient {
}
|
这段代码的核心就是@Import({UserAutoConfigure.class}),通过导入的方式实现把UserAutoConfigure实例加入SpringIOC容器中,这样就能开启自动配置了。
使用方式就是在启动类上加上该注解,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.self.springboot_item;
import com.self.springboot_item.config.StartCommand; import com.self.springbootstarterdemo.config.EnableUserClient; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication @EnableUserClient public class SpringbootItemApplication {
public static void main(String[] args) {
new StartCommand(args); SpringApplication.run(SpringbootItemApplication.class, args); } }
|
5. 使用配置开启Starter自动构建
在某些场景下,UserAutoConfigure中会配置多个对象,对于这些对象,如果不想全部配置,或是想让用户指定需要开启配置时候再去构建对象,这时候我们可以通过@ConditionalOnProperty 来指定是否开启配置的功能,代码如下:
1 2 3 4 5
| @Bean @ConditionalOnProperty(prefix = "spring.user",value = "enabled",havingValue = "true") public UserClient userClient(UserPorperties userPorperties) { return new UserClient(userPorperties); }
|
通过上面的配置,只有当启动类加了@EnableUserClient并且配置文件中spring.user.enabled = true 的时候才会自动配置UserClient。
6. 配置Starter内容提示
在自定义Starter包的过程中,还有一点比较重要,就是对配置的内容项进行提示。
定义提示内容需要在META-INF 中创建一个spring-configuration-metadata.json文件,代码如下:
1 2 3 4 5 6 7 8 9 10
| { "properties": [ { "name": "spring.user.name", "defaultValue": "cxytinadi" }, { "name": "spring.user.enabled", "type": "java.lang.Boolean", "defaultValue": false } ] }
|
- name : 配置名
- type :配置的数据类型
- defaultValue :默认值