springboot相较于springMVC的一大优点就是基于约定优于配置的理念简化配置,并且提供给我们开箱即用的一些组件,例如spring-starter依赖。它内部引入相关组件的jar包,并且封装了一些默认配置。让我们在开发时能够专注于业务而不必过多的关注框架的配置。
spring boot starter 加载jar配置类有两种方式。
1、 通过SPI机制自动加载配置类
2、 通过@EnableXxx注解的方式加载
一、通过SPI机制自动加载配置类
原理
我们使用spring组件的时候,例如redis,只需将对应的spring-boot-starter-redis通过pom引入即可,不用自己去查找、引入一系列所需的依赖类。同时,SpringBoot会自动进行类的自动配置,并将相关的Bean注入Spring Conxtex中。
第一步,SpringBoot 在启动时会去依赖的starter包中寻找 resources/META-INF/spring.factories文件,然后根据文件中配置去扫描项目所依赖的Jar包,这类似于 Java 的 SPI 机制。
第二步,根据 spring.factories配置加载AutoConfigure类。
最后,根据@Conditional注解的条件,进行自动配置并将Bean注入Spring Context 上下文当中。
我们也可以使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class})指定自动配置哪些类。
实现
第一步创建一个SpringBoot项目,添加两个依赖项目中
<dependencies>
<!--负责从properties文件中读取配置信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
<!--实现自动化配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
</dependencies>
其中spring-boot-configuration-processor 的作用是编译时生成spring-configuration-metadata.json ,此文件主要给IDE使用。如当配置此jar相关配置属性在 application.yml,你可以用ctlr+鼠标左键点击属性名,IDE会跳转到你配置此属性的类中。
我们日常使用的Spring官方的Starter一般采取spring-boot-starter-{name}的命名方式,如 spring-boot-starter-web 。
而非官方的Starter,官方建议 artifactId 命名应遵循{name}-spring-boot-starter的格式。 例如:ysc-spring-boot-starter 。
<groupId>com.zuji</groupId>
<artifactId>zcl-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
编写三个重要的类
Service类
该类实现具体的功能,包含一个能够将配置文件中配置的字符串根据传入的字符进行分割的方法String[] split(String separatorChar)。
public class StarterService {
private String config;
public StarterService(String config) {
this.config = config;
}
public String] split(String splitChar) {
return config.split(splitChar);
}
}
配置文件读取类
@ConfigurationProperties("example.service")
public class StarterServiceProperties {
private String config;
public void setConfig(String config) {
this.config = config;
}
public String getConfig() {
return config;
}
}
AutoConfigure类
@Configuration
@ConditionalOnClass(StarterService.class)
@EnableConfigurationProperties(StarterServiceProperties.class)
public class StarterAutoConfigure {
@Autowired
private StarterServiceProperties properties;
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "example.service", value = "enabled", havingValue = "true")
StarterService starterService (){
return new StarterService(properties.getConfig());
}
}
@ConditionalOnClass,当classpath下发现该类的情况下进行自动配置StarterService。
@ConditionalOnMissingBean,当Spring Context中不存在该Bean时创建,防止重复执行。
@ConditionalOnProperty(prefix = “example.service”,value = “enabled”,havingValue = “true”),当配置文件中example.service.enabled=true时,执行该方法。
在resources/META-INF/下创建spring.factories文件
这里注意改为你自己的包名
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zcl.StarterAutoConfigure
发布
使用mvn install打包安装
使用
引入starter
<dependency>
<groupId>com.dy</groupId>
<artifactId>zcl-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
在application.yml中编写配置文件
example:
service:
enabled: true
config: gsdf, fd, fasd
配置junit4进行测试
@RunWith(value = SpringJUnit4ClassRunner.class)
//这里App类为springboot的启动类
@SpringBootTest(classes = {App.class})
public class MyTest {
@Autowired
private StarterService starterService;
@Test
public void starterTest() {
String] splitArray = starterService.split(",");
System.out.println(Arrays.toString(splitArray));
}
}
结果
[gsdf, fd, fasd]
二、通过@EnableXxx注解的方式加载
原理
@EnableXxx注解通过使用@Import注解导入一个Configuration的类。最关键的是,该类实现了接口ImportSelector。
public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
该方法返回的Bean 会自动的被注入,被Spring所管理。
在@Import中,有个链接指向了 ImportBeanDefinitionRegistrar。这同样是一个接口,作用跟 ImportSelector 一样。
实现
- 创建2个测试Config类
public class RoleProcessor {
}
public class UserProcessor {
}
- 自定义Enable注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MyEnableImportSelector.class)
public @interface EnableBean {
}
- 实现自己的MyEnableImportSelector 类
public class MyEnableImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{RoleProcessor.class.getName(),UserProcessor.class.getName()};
}
}
- 编写启动类
@EnableBean
public class Main {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Main.class, args);
System.out.println(context.getBean(UserProcessor.class));
System.out.println(context.getBean(RoleProcessor.class));
}
}
- 运行结果
com.zuji.UserProcessor@496bc455
com.zuji.RoleProcessor@59402b8f