我们使用SpringBoot,基本上都是沉醉在它Starter的方便之中。Starter为我们带来了众多的自动化配置,有了这些自动化配置,我们可以不费吹灰之力就能搭建一个生产级开发环境,有的小伙伴会觉得这个Starter 好神奇呀!其实Starter也都是基于Spring + SpringMVC中的基础知识点实现的,今天小编就通过自定义一个Starter来带大家了解Starter,慢慢揭开Starter的神秘面纱!
其实Starter的核心就是条件注解@Conditional。当classpath下存在某一个Class时,某个配置才会生效,大伙可能也发现了源码解读时总是会出现条件注解,其实这就是Starter配置的核心之一。
1.1Starter定义
所谓的Starter,其实就是一个普通的Maven项目,因此我们自定义Starter,需要首先创建一个普通的Maven项目,然后引入对应的依赖版本,这里我们使用的springboot版本是2.2.1.RELEASE。
# 引入依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.swk</groupId>
<artifactId>MyStarter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>MyStarter</name>
<description>MyStarter</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
依赖引入完成后,我们首先创建一个HelloProperties类,用来接收application.properties中注入的值。
@ConfigurationProperties(prefix = "hello")
public class HelloProperties {
private static final String DEFAULT_NAME = "隔壁老王";
private static final String DEFAULT_MSG = "隔壁小王";
private String name = DEFAULT_NAME;
private String msg = DEFAULT_MSG;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
这个配置类很好理解,将application.properties中配置的属性值直接注入到这个实例中。
@ConfigurationProperties类型安全的属性注入,即将application.properties文件中前缀为hello的属性注入到这个类对应的属性,最后使用的时候,application.properties中的配置文件,大概如下:
hello.name=zhangsan
hello.msg=java
配置完成HelloProperties后,接下来我们来定义一个HelloService,然后定义一个简单的say方法,HelloService 的定义如下:
public class HelloService {
private String msg;
private String name;
public String sayHello() {
return name + " say " + msg + " !";
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接下来是重轴戏 - 定义自动配置类
@Configuration
@EnableConfigurationProperties(HelloProperties.class)
@ConditionalOnClass(HelloService.class)
public class HelloServiceAutoConfiguration {
@Autowired
private HelloProperties helloProperties;
@Bean
public HelloService helloService() {
HelloService helloService = new HelloService();
helloService.setName(helloProperties.getName());
helloService.setMsg(helloProperties.getMsg());
return helloService;
}
}
注解说明
@Configuration 注解表明这是一个配置类。
@EnableConfigurationProperties 注 解 是 使 我 们 之 前 配 置 的 @ConfigurationProperties注解生效,让配置的属性成功进入到Bean中。@ConditionalOnClass 表示当项目当前classpath下存在HelloService 时,后面的配置才生效。
自动配置类中首先注入HelloProperties,这个实例中含有我们在application.properties中配置的相关数据。
创建一个HelloService实例对象,并将HelloProperties中的值注入进去。
到这一步,我们的自动配置类就算是完成了。接下来我们还需要一个spring.factories文件。那么这个文件是干嘛的呢?大家知道我们的SpringBoot项目的启动类都有一个@SpringBootApplication注解,这个注解是一个复合注解,定义如下:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication { }
我们可以看到@SpringBootApplication这是一个组合注解,其中的一个组合项就是@EnableAutoConfiguration,这个注解是干嘛的呢?
@EnableAutoConfiguration表示启用Spring应用程序上下文的自动配置,该注解会自动导入一个名为AutoConfigurationImportSelector的类,而这个类会去读取一个spring.factories的文件, spring.factories 中则定义需要加载的自动化配置类。
我们打开任意一个框架的Starter,都能看到它有一个spring.factories文件,例如 MyBatis的Starter如下:
那么我们自定义的Starter也需要这样一个文件,我们首先在Maven项目的resources目录下创建一个名为META-INF的文件夹,然后在文件夹中创建一个名为spring.factories的文件,文件内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.swk.mystarter.config.HelloServiceAutoConfiguration
在这里指定我们的自动化配置类的路径即可,如此之后我们的自动化配置类就算完成了。
1.2使用自定义Starter
我们将这个自动化配置类安装到本地仓库,然后在其它项目中使用。安装到本地方式是在IntelliJ IDEA中,点击右边的Maven Project,然后选择 Lifecycle 中的 install,双击即可。
创建SpringBoot工程,在其pom.xml引入自定义Starter工程依赖。
<dependency>
<groupId>com.swk</groupId>
<artifactId>MyStarter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
引入了自定义的Starter后,我们项目中就有了一个默认的 HelloService 实例可以使用,这里我们直接在单元测试类中注入该实例完成测试。
单元测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class UsemystarterApplicationTests {
@Autowired
HelloService helloService;
@Test
public void contextLoads() {
System.out.println(helloService.sayHello());
}
}
总结
通过上述自定义Starter,相信加深了大家对Starter的理解。我们也可以看到SpringBoot所提供的这种方式主要是基于自动配置来完成的,这种方式在很大程度上减弱了原先Spring框架的配置,体现了SpringBoot框架“约定大于配置”的思想,让我们开发人员更加专注于业务实现。