文章目录
问一个问题:SpringBoot为啥不需要像SSM那样需要繁琐的配置?
默认大于配置 是不是在你脑海中出现了这六个字,那么你知道为啥么?没事,如果不知道,那就继续看下去被。
@SpringBootApplication
@SpringBootApplication
public class Springboot02ConfigAutoconfigApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot02ConfigAutoconfigApplication.class, args);
}
}
@SpringBootApplication 这个注解肯定是大家耳熟能详的,但是你点进去看过吗?
@EnableAutoConfiguration
点进去,发现@SpringBootApplication这个注解里面有一大堆的东西,没事,咱们直接切入正题,看@EnableAutoConfiguration:从字面解释:启动自动配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
//代码部分省略
}
点进去,发现@EnableAutoConfiguration这个注解里面有两个重要的注解:@AutoConfigurationPackage:含义是扫描表明该注解的类所有的包以及子包和@Import(AutoConfigurationImportSelector.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//代码部分省略
}
我们主要说明一下这个注解:@Import(AutoConfigurationImportSelector.class)
@Import(AutoConfigurationImportSelector.class)
科普:@Import是Spring的底层注解,将括号中的类导入到IOC容器中,默认的id是首字母小写。
然后我们点开这个类,找到selectImports()方法。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
{
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()
);
}
我们看到有:getAutoConfigurationEntry(annotationMetadata) 方法:方法名的含义:获取自动配置的实体。我们继续点进去。
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
发现其中有一个getCandidateConfigurations(annotationMetadata, attributes);方法。方法名的含义:获取候选加入的配置,并且返回一个字符串类型的集合。,我们点进去继续看。
如果读者没心情看,可以直接点到这里。
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;
}
//这里,我们需要关心一下getSpringFactoriesLoaderFactoryClass()这个方法,
//他返回的是EnableAutoConfiguration.class。
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
在点进去,发现里面如下的方法,其中有一个方法是 loadSpringFactories()方法,方法的含义:加载Spring的工厂类
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
在点进去,没错这里就到了核心的地方,我们来看看。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
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 = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
代码大意:
- 首先通过getResou rces(FACTORIES_RESOURCE_LOCATION)得到一个资源,我们来看看这个静态变量。这个静态变量就是"META-INF/spring.factories"
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
- 然后扫描这个路径下的所有jar包,得到一个url。
- 然后遍历每一个URL,最后把这些URL扫描到的内容,包装成一个Properties对象。
- 然后把Properties对象的一些值,加载到返回结果中。
相当于把如下的这些组件加载到IOC容器中。
总结一下:就是将SpringBoot下的"META-INF/spring.factories"中的这些组件加载到IOC容器中。
以HttpEncodingAutoConfiguration为例,解析自动配置原理。
博主正在努力更新,期待您的阅读。。。