二、starter
的实现starter
实现起来各有差异,但是他们基本上都会使用到两个相同的内容:ConfigurationProperties
和AutoConfiguration
。因为Spring Boot
坚信“约定大于配置”这一理念,所以我们使用ConfigurationProperties来保存我们的配置,并且这些配置都可以有一个默认值,即在我们没有主动覆写原始配置的情况下,默认值就会生效,这在很多情况下是非常有用的。除此之外,starter
的ConfigurationProperties
还使得所有的配置属性被聚集到一个文件中(一般在resources目录下的application.properties),这样我们就告别了Spring项目中XML地狱。
maven
中,groupId
代表着姓氏,artifactId
代表着名字。Spring Boot
也是有一个命名的建议的。所以名字是不能够随随便便取得,可以按照官方的建议来取。
What’s in a name All official starters follow a similar naming pattern; spring-boot-starter- , where is a particular type of application. This naming structure is intended to help when you need to find a starter. The Maven integration in many IDEs lets you search dependencies by name. For example, with the appropriate Eclipse or STS plugin installed, you can press ctrl-space in the POM editor and type “spring-boot-starter” for a complete list. As explained in the “Creating Your Own Starter” section, third party starters should not start with spring-boot, as it is reserved for official Spring Boot artifacts. Rather, a third-party starter typically starts with the name of the project. For example, a third-party starter project called thirdpartyproject would typically be named thirdpartyproject-spring-boot-starter.
大概意思:官方的 starter
的命名格式为 spring-boot-starter-{xxxx}
比如spring-boot-starter-activemq,
第三方我们自己的命名格式为 {xxxx}-spring-boot-starter
。比如mybatis-spring-boot-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>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>xxx-spring-boot-starter</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
com.person.age=23
com.person.name=Lynch
com.person.sex=F
package com.et.config;
import com.et.service.PersonService;
import com.et.starter.PersonProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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(PersonProperties.class)
@ConditionalOnClass(PersonService.class)
@ConditionalOnProperty(prefix = "com.person", value = "enabled", matchIfMissing = true)
public class PersonServiceAutoConfiguration {
@Autowired
private PersonProperties properties;
// if spring container do not config bean,auto config PersonService
@Bean
@ConditionalOnMissingBean(PersonService.class)
public PersonService personService(){
PersonService personService = new PersonService(properties);
return personService;
}
}
package com.et.service;
import com.et.starter.PersonProperties;
public class PersonService {
private PersonProperties properties;
public PersonService() {
}
public PersonService(PersonProperties properties) {
this.properties = properties;
}
public void sayHello() {
String message = String.format("hi,my name: %s, today,I'am %s , gender: %s",
properties.getName(), properties.getAge(), properties.getSex());
System.out.println(message);
}
}
package com.et.starter;
import java.io.Serializable;
import org.springframework.boot.context.properties.ConfigurationProperties;
@SuppressWarnings("serial")
@ConfigurationProperties(prefix = "com.person")
public class PersonProperties implements Serializable {
private String name;
private int age;
private String sex = "M";
public PersonProperties() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.et.config.PersonServiceAutoConfiguration
mvn clean install
在另外一个项目中添加starter的依赖
<dependency>
<groupId>com.et</groupId>
<artifactId>xxx-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
单元测试类
package com.et.starter;
import com.et.service.PersonService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {
@Autowired
private PersonService personService;
@Test
public void testHelloWorld() {
personService.sayHello();
}
}
运行测试类
2024-03-11 10:35:18.374 INFO 10960 --- [ main] com.et.starter.PersonServiceTest : Starting PersonServiceTest on BJDPLHHUAPC with PID 10960 (started by Dell in D:\IdeaProjects\ETFramework\xxx-spring-boot-starter-test) 2024-03-11 10:35:18.376 INFO 10960 --- [ main] com.et.starter.PersonServiceTest : No active profile set, falling back to default profiles: default 2024-03-11 10:35:19.387 INFO 10960 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2024-03-11 10:35:19.657 INFO 10960 --- [ main] com.et.starter.PersonServiceTest : Started PersonServiceTest in 1.507 seconds (JVM running for 2.188) hi,my name: Lynch, today,I'am 23 , gender: F 2024-03-11 10:35:19.827 INFO 10960 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'