文章目录
- 一、解读@SpringBootApplication
- @EnableAutoConfiguration
- @AutoConfigurationImportSelector
- 二、解读run方法
- 1、判断该工程的类型
- 2、初始化获取工厂实例
- 2.1、SpringFactoriesLoader类的loadFactoryNames
大概聊聊SpringBoot的自动配置,这里是基于SpringBoot 2.0.6.RELEASE,Spring 5.0.10.RELEASE。
一、解读@SpringBootApplication
@SpringBootApplication
public class BaseApplication {
public static void main(String[] args) {
SpringApplication.run(BaseApplication.class, args);
}
}
查看启动类上的注解@SpringBootApplication
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // SpringBoot特有的配置注解
@EnableAutoConfiguration // 开启自动配置(本质是根据依赖自动配置)
@ComponentScan(
excludeFilters = {
@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
@Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})}
)
public @interface SpringBootApplication {
@ComponentScan
相当于xml配置中的<context: component-scan>
配置包扫描。
如果没有定义basePackages、basePackageClasses,则默认扫描声明这个注解所在的包。所以一般启动类都放在包层次较浅的包下,这样就都可以扫描到了。
@EnableAutoConfiguration
查看@EnableAutoConfiguration
:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationImportSelector
Import了AutoConfigurationImportSelector
类:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
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 StringUtils.toStringArray(configurations);
}
↓
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;
}
重写了selectImports()方法,其中调用了getCandidateConfigurations()方法,通过注解元数据和注解属性获得候选配置。loadFactoryNames()
方法通过loadSpringFactories()
加载META-INF/spring.factories文件。(下文2.1会具体解读)其中就有EnableAutoConfiguration
,提供了许多自动配置类。
autoconfigure下有各种自动配置:
META-INF/spring.factories
看到有许多自动装配的Bean
(key-value形式,key是类名或者接口名,而value是实现名)
而当这些配置满足一定条件,就会自动配置。
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class})
public class DataSourceAutoConfiguration {
@ConditionalOnClass
当有这些依赖时会自动配置。@EnableConfigurationProperties
启用配置属性。举例,这里启动了DataSourceProperties.class。可以在application.yml中指定属性,满足prefix和字段名即可。
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
...
...
二、解读run方法
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args);
}
↓
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
↓
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1、判断工程的类型(REACTIVE、NONE、SERVLET)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2、初始化,获取Spring工厂实例
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
1、判断该工程的类型
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
如果是响应式的类或者依赖存在,并且不含有前端控制器和JERSEY框架相关的,则是REACTIVE响应式工程。
遍历SERVLET_INDICATOR_CLASSES
,如果Servlet和ConfigurableWebApplicationContext都不存在,则不是个Web工程。
否则,是个Servlet工程。
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
2、初始化获取工厂实例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
// 获取当前线程的类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
// 2.1、获得工厂类名的Set集合
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 利用反射得到工厂实例的集合
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
2.1、SpringFactoriesLoader类的loadFactoryNames
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) {
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()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
类加载器到FACTORIES_RESOURCE_LOCATION
路径下获取文件资源。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
可以看到,最后也是要读取META-INF下的spring.factories文件。
总结:
1、我们在使用SpringBoot的时候,只要在pom.xml中引用启动器(即提供了相关依赖,启动器会统一依赖版本),它会为我们提供默认配置,自动配置类都存在于spring.factories下。
2、自定义属性,大多情况只要在application.yml中配置即可,也可以写一个Bean覆盖配置。