一. springboot的自动装配
1. @SpringBootApplication ---> @EnableAutoConfinguration ---> @Import(AutoConfigurationImportSelector.class)
2. AutoConfigurationImportSelector类实现了DeferredImportSelector接口
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
重写了其selectImports方法得到相关的注解配置类的全名称:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
selectImprts内调用了getAutoConfigurationEntry方法,
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
再进入getCandidateConfiguration方法,
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
最后:SpringFactoriesLoader的loadFactoryNames方法,能得到所有的spring.factories内的配置内容,告知spring相关的配置类,加载对应的bean;
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
二. 根据SPI机制实现自己的自动装配类
类似于redis的使用,项目启动,可以自动注入redisTemplate,开箱即用;
注意: 这个是个普通的maven项目,完成后intall到仓库中,其他项目直接依赖使用
其中pom.xml:
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zsw.example</groupId>
<artifactId>order-api</artifactId>
<version>1.1-SNAPSHOT</version>
<name>order-api</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.1.7.RELEASE</version>
<optional>true</optional><!--optional 为true , 不传递此依赖-->
</dependency>
</dependencies>
</project>
1. 自定义相关参数类
@Data
@ConfigurationProperties(prefix = "zsw")
public class MyRedisProperty {
private String host = "127.0.0.1";
private Integer port = 6666;
private Integer timeOut;
private Boolean ssl;
}
2. 自定义服务端,类似于redisTemplate
@Data
public class MyRedisClient {
private String address;
public MyRedisClient(MyRedisProperty myRedisProperty) {
this.address = myRedisProperty.getHost() + ":" + myRedisProperty.getPort();
}
}
3. 自定义相关配置类,用于加载client,类似于RedisAutoConfiguration
@EnableConfigurationProperties(MyRedisProperty.class) // 属性注入,自动装载MyRedisProperty
@Configuration
@ConditionalOnProperty(prefix = "zsw", value = "enable") // 满足条件才加载此配置类
public class MyRedisAutoConfiguration {
@Bean
public MyRedisClient myRedisClient(MyRedisProperty myRedisProperty) {
System.out.println("myRedisProperty: " + myRedisProperty.getHost() + " " + myRedisProperty.getPort());
return new MyRedisClient(myRedisProperty);
}
}
3.2 测试配置类是否完善,能否装载client,要将@ConditionalOnProperty注释掉,否则不加载的,测试类:
/**
* 测试configuration
*
* @author zhangshiwei
* @since 2020年9月21日 11:15:34
*/
public class ConfigurationTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
MyRedisAutoConfiguration.class);
System.out.println("从spring容器中获取对象 MyRedisClient: " + applicationContext.getBean(MyRedisClient.class));
}
}
3.3 测试ConfigurationTest 结果:
4. 新建resources.MET-INF文件夹,在MET-INF内新建文件: spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zsw.example.spring.factories.MyRedisAutoConfiguration
5. 待使用MyRedisClient的项目,添加依赖
<!-- spring.factories 自动装配测试-->
<dependency>
<groupId>com.zsw.example</groupId>
<artifactId>order-api</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
6. 待使用MyRedisClient的项目,yml配置新增
zsw:
enable: true
host: 127.0.0.1
port: 1234
7. 添加测试类
/**
* SpringFactories 测试控制类
*
* @author zhangshiwei
* @since 2020年9月21日 11:01:26
*/
@RequestMapping("/springFactories")
@RestController
public class SpringFactoriesTestController {
@Autowired
private MyRedisClient myRedisClient;
@RequestMapping("/address")
public String test() {
return myRedisClient.getAddress();
}
}