SpringBoot自动装配机制
SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的META-INF/spring.factories
文件,将文件中配置的类型信息加载到 Spring 容器。
通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能。
具体内容就是
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
fun.tans.mystarter.ThreadPoolAutoConfiguration
具体如何实现?
@EnableAutoConfiguration注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //主要是此注解
public @interface EnableAutoConfiguration {
}
@AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
private static final String[] NO_IMPORTS = {};
private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
private ConfigurationClassFilter configurationClassFilter;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//1. 这里通过metadata获取注解实体,调用代码片段2
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//2. 调用代码片段3
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//3. 调用代码片段4
List<String> configurations =
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "......");
return configurations;
}
}
SpringFactoriesLoader
public final class SpringFactoriesLoader {
//代码片段4
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
//调用代码片段5
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
//代码片段5
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
//Important:开始处理配置文件.....
//FACTORIES_RESOURCE_LOCATION="META-INF/spring.factories"
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
}
// Replace all lists with unmodifiable lists containing unique elements
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
cache.put(classLoader, result);
}
return result;
}
}
筛选加载
在AutoConfiguration插入指定的@Conditional
@Configuration
// 检查相关的类:RabbitTemplate 和 Channel是否存在
// 存在才会加载
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {
}
实现自己的Stater
1. 新建工程mystarter
, 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupI d>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
注意,如果pom.xml
存在如下spring-boot-maven-plugin
模块,请删除,因为如果存在此插件,在打包的时候会生成BOOT-INF
目录,导致我们的其他模块引入此模块出现错误!问题指南
2. 编写AutoConfiguration配置类和Properties配置文件类
package fun.tans.mystarter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Describe: 类描述
* @Author: tyf
* @CreateTime: 2022/4/9
**/
@ConfigurationProperties(prefix = "my")
public class MyProperties {
private String Name;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
// private String
}
package fun.tans;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Describe: 类描述
* @Author: tyf
* @CreateTime: 2022/4/9
**/
@Configuration
@EnableConfigurationProperties(MyProperties.class)
public class ThreadPoolAutoConfiguration {
@Bean
//注意此判定条件一定存在
@ConditionalOnClass(ThreadPoolExecutor.class)
public ThreadPoolExecutor myThreadPool(){
return new ThreadPoolExecutor(10,10,10, TimeUnit.MICROSECONDS, new ArrayBlockingQueue<>(100));
}
}
3.编写Spring.factories
文件
在resource目录下新建META-INF/spring.factories
文件,写入我们的需要被扫描配置的类.注意不能出现多余空格等,格式要正确。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
fun.tans.ThreadPoolAutoConfiguration
4.使用Maven工具打包并打包在本地仓库
一般是打成jar包然后发布到远程仓库,我们测试的话可以使用maven install
命令,来打包后直接自动放在本地仓库中。
而package
命令仅仅是打包。
5.查看打包好的模块
如果打包成功,在maven仓库目录/repository
会找到你的jar包:
6.开启新的项目进行测试
新建模块或者项目来进行测试,这里我们新建项目test
- 首先引入依赖(也就是我们刚刚打好的jar包)
<dependency>
<groupId>fun.tans</groupId>
<artifactId>mystarter</artifactId>
<version>1.0.0</version>
</dependency>
- 编写测试类,使用junit