前言
本文主要从源码角度分析springboot如何进行自动配置的。
springboot中的自动装配
springboot最核心的一点
特性:零xml、自动装配、内嵌tomcat。spring可以利用servlet3.0实现零xml和内嵌tomcat,但是自动装配实现不了
首先我们需要弄清楚自动装配做了什么,我先举个例子:
比如:
- 在单纯的spring项目中,我们开启AOP的方式是这样:
先在pom.xml中导入AOP的坐标(引入相关依赖)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
然后,在配置来中开启AOP,然后编写相关类来实现逻辑
@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy // 这里开启AOP
public class AppConfig {
}
- 如果在springboot中,启用AOP
我们只需要在pom.xml加入相关依赖就行,然后直接使用就可以
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1. 自定义自动装配
在下面目录创建 resources/META-INF/spring.factories文件
# 里面添加如下内容,key固定,value自己写类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.demo.config.MyAutoConfiguration
编写
MyAutoConfiguration
配置类@Configuration // 可以自定义加载的条件,可以说必须吧,因为一般也是满足什么条件才加载。 // 比如:@ConditionalOnClass(DispatcherServlet.class),要存在这个类才加载 @Conditional(MyCondition.class) // 会先加载这个,配置类里面加载相关设置。当然不是必须 @EnableConfigurationProperties(MyProperties.class) public class MyAutoConfiguration { // 赋值后,后面可以来使用 private final MyProperties myProperties; public MyAutoConfiguration(MyProperties properties) { this.myProperties = properties; } // ...todo }
- 编写配置类和条件类
MyProperties类
@ConfigurationProperties(prefix = "my", ignoreUnknownFields = true)
public class MyProperties {
private String name;
private String password;
}
MyCondition类
public class MyCondition implements Condition {
// 返回true才会加载。
// 这个方法里面写相关的逻辑,来返回true 或 false
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// ...todo
return false;
}
}
自定义一个自动装配已经完成,下面进入源码分析了。
2. 源码解析
从springboot项目的启动类开始,
App类
@SpringBootApplication public class App { public static void main(String[] args) { // SpringApplication.run(App.class); SpringApplication application = new SpringApplication(App.class); application.run(args); } }
- 点击
@SpringBootApplication注解
- 点击@EnableAutoConfiguration注解
这里面用了 @Import注解,这里是利用spring中的扩展方式之一,后面可能会将spring中主要的扩展方式
@AutoConfigurationPackage
// 这里面又是从 spring.factories配置文件中拿 key为 EnableAutoConfiguration.class 的所有value值
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- 进入
AutoConfigurationImportSelector类
这里利用了@Import扩展方式的特点,通过全限定类名,加载出类,然后放入到spring容器中
里面重要的方法:
selectImports()方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 这里面又是从 spring.factories配置文件中拿 key为 EnableAutoConfiguration.class 的所有value值
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
// 最后在这个方法的返回值使用,然后spring内部,会通过全限定类名的字符串加载出类,然后放入到spring容器中
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
getAutoConfigurationEntry()方法
只要用的是 AutoConfigurationEntry类中的configurations属性,里面保存这需要被加载的类的全限定类名的集合
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 这里面又是从 spring.factories配置文件中拿 key为 EnableAutoConfiguration.class 的所有value值
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
// 剔除一些配置,比如:因为项目中并没有加入相关的依赖(rabbitMQ),所以也不能创建对象
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations()方法
返回所有的key为 EnableAutoConfiguration 的所有类的全限定类名
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 这里面又是从 spring.factories配置文件中拿 key为 EnableAutoConfiguration.class 的所有value值
// getSpringFactoriesLoaderFactoryClass()方法返回的是 EnableAutoConfiguration.class
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类
里面的 loadSpringFactories()方法返回,key为EnableAutoConfiguration的所有在spring.factories文件中的value值
public final class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
// 这里就是要写在相关目录下的原因,这里会加载类路径下的META-INF目录下的 spring.factories文件
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// ......
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 这里首先尝试从cache中拿取,如果不为空,直接就返回
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// FACTORIES_RESOURCE_LOCATION 就是 "META-INF/spring.factories"
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// 变成资源文件
UrlResource resource = new UrlResource(url);
// 会把里面的键值对都存在properties里面
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
// 添加到result map中
result.add(factoryClassName, factoryName.trim());
}
}
}
// 放入到cache中
cache.put(classLoader, result);
return result;
}
// ......
}
// ......
}
到这里,本篇博客也写的基本完成了,后面如果有需要可能会进行补充,如有错误,欢迎指出。又水了一篇,快乐。
版权属于:未央花
本文链接:https://www.pslanys.com/archives/251.html
转载时须注明出处及本声明
提供拼多多代发 京东快递 淘宝代发,无需签收,单号网www.kuaidzj.com