SpringBoot 基础篇:自动配置
SpringBoot 基础篇:自动配置
自动配置的使用
自动配好 Tomcat
引入 Tomcat 依赖。Web Starter 启动器引入
在
spring-boot-starter-tomcat
中引入 Tomcat,而spring-boot-starter-tomcat
在spring-boot-starter-web
中已经包含了。
配置 Tomcat,看本文后面的原理部分
自动配好 SpringMVC
引入 SpringMVC 全套组件,Web Starter 启动器引入
主要是
spring-web
、spring-webmvc
,这两个依赖在spring-boot-starter-tomcat
中都有引入
自动配好 Web 常见功能
SpringBoot 帮我们配置好了所有 web 开发的常见场景,主要通过帮我们注册处理特定问题的组件来实现这一点,那都帮我们注册了哪些组件呢?
-
dispatcherServlet
,SpringMVC 入口 servlet,由DispatcherServletAutoConfiguration
自动配置类注册 -
characterEncodingFilter
,字符编码,保证中文不乱码,由HttpEncodingAutoConfiguration
自动配置类注册 -
viewResolver
,视图解析器,假设为ThymeleafViewResolver
,由ThymeleafAutoConfiguration
自动配置类注册 -
multipartResolver
,文件上传解析器,由DispatcherServletAutoConfiguration
自动配置类注册
等等等,这些组件我们在学习 SpringMVC 的时候都已经接触过了,很清楚其作用。
如何查看 IOC 容器内的组件呢?通过下面的代码
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
// 1. 返回IOC容器 ConfigurableApplicationContext 实际上就是IOC容器
// ConfigurableApplicationContext 接口实现了ApplicationContext接口,实际上就是当前应用中Spring上下文的具体实现类
final ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
// 2. 打印容器里的所有组件的名称
// 上面的那些组件都默认包含在输出结果里
// 当然也包括我们自己写的一些业务组件
final String[] names = run.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
输出:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainApplication
org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory
helloController
org.springframework.boot.autoconfigure.AutoConfigurationPackages
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
propertySourcesPlaceholderConfigurer
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration
websocketServletWebServerCustomizer
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
tomcatServletWebServerFactory
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
servletWebServerFactoryCustomizer
tomcatServletWebServerFactoryCustomizer
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor
org.springframework.boot.context.internalConfigurationPropertiesBinderFactory
org.springframework.boot.context.internalConfigurationPropertiesBinder
org.springframework.boot.context.properties.BoundConfigurationProperties
org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata
server-org.springframework.boot.autoconfigure.web.ServerProperties
webServerFactoryCustomizerBeanPostProcessor
errorPageRegistrarBeanPostProcessor
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration
dispatcherServlet
spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration
dispatcherServletRegistration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
taskExecutorBuilder
applicationTaskExecutor
spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration
error
beanNameViewResolver
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration
conventionErrorViewResolver
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
errorAttributes
basicErrorController
errorPageCustomizer
preserveErrorControllerTargetClassPostProcessor
spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
requestMappingHandlerAdapter
requestMappingHandlerMapping
welcomePageHandlerMapping
mvcConversionService
mvcValidator
mvcContentNegotiationManager
mvcPathMatcher
mvcUrlPathHelper
viewControllerHandlerMapping
beanNameHandlerMapping
routerFunctionMapping
resourceHandlerMapping
mvcResourceUrlProvider
defaultServletHandlerMapping
handlerFunctionAdapter
mvcUriComponentsContributor
httpRequestHandlerAdapter
simpleControllerHandlerAdapter
handlerExceptionResolver
mvcViewResolver
mvcHandlerMappingIntrospector
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter
defaultViewResolver
viewResolver
requestContextFilter
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
formContentFilter
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration
mbeanExporter
objectNamingStrategy
mbeanServer
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
springApplicationAdminRegistrar
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
applicationAvailability
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
lifecycleProcessor
spring.lifecycle-org.springframework.boot.autoconfigure.context.LifecycleProperties
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration
standardJacksonObjectMapperBuilderCustomizer
spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration
jacksonObjectMapperBuilder
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration
parameterNamesModule
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration
jacksonObjectMapper
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
jsonComponentModule
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration
stringHttpMessageConverter
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration
mappingJackson2HttpMessageConverter
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
messageConverters
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
taskSchedulerBuilder
spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
restTemplateBuilder
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration
tomcatWebServerFactoryCustomizer
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
characterEncodingFilter
localeCharsetMappingsCustomizer
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
multipartConfigElement
multipartResolver
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
org.springframework.aop.config.internalAutoProxyCreator
注意,如果报错
javax.management.InstanceAlreadyExistsException: org.springframework.boot:type=Admin,name=SpringApplication
则需要在 Run/Debug Configuration
中,取消 launch optimization
和 Enable JMX agent
,具体位置为

你还可以在这里开启 debug 级别的日志输出,Enable debug output
。
默认的包扫描配置
这是 Spring 基础,Spring 需要扫描组件,然后注册到容器中,而我们需要给 Spring 指定扫描路径。
Spring Boot 中,默认包结构也不需要我们配,主程序(带有注解 @SpringBootApplication
)所在包及其下面的所有子包里面的组件都会被默认扫描进来,想要改变扫描路径,使用 @SpringBootApplication(scanBasePackages="com.liangkang")
指定扫描路径即可。
查看 @SpringBootApplication
的源码我们知道,@SpringBootApplication
= @SpringBootConfiguration
+ @EnableAutoConfiguration
(继承 @Configuration
) + @ComponentScan
,想要改变包扫描路径,
因为
@SpringBootApplication
继承了@Configuration
注解,所以,我们可以在@SpringBootApplication
注解修饰的类中编写@Bean
注解修饰的方法来向 IOC 容器中注册 Bean,也可以在@SpringBootApplication
注解修饰的类上添加@Import
等注解,但是一般我们不这么干,为了层次清晰,我们一般只在@SpringBootApplication
注解修饰的类中保留一个 man 方法,另外创建一个类用@Configuration
修饰,并在其中注册 Bean。
我们也可以不写 @SpringBootApplication
,而是写这三个注解,然后通过 @ComponentScan
指定扫描路径。
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("xyz.xiashuo")
其实基本上可以直接在 @SpringBootApplication
上设置其继承的注解的配置,当然最常用的还是 scanBasePackages
和 proxyBeanMethods
。
@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
// 通过此属性直接设置 @ComponentScan 注解的 basePackages 字段
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
// 通过此属性直接设置 @ComponentScan 注解的 basePackageClasses 字段
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
// 通过此属性直接设置 @ComponentScan 注解的 nameGenerator 字段
@AliasFor(
annotation = ComponentScan.class,
attribute = "nameGenerator"
)
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
// 通过此属性直接设置 @Configuration 注解的 proxyBeanMethods 字段
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
各种配置拥有默认值
这些默认值,实际上也可以看作是配置项,在 application.properties
文件中都可以设置,
IDEA 非常智能,只写配置的部分代码,IDEA 会提示你全称,非常方便,这里有个小技巧,我们可以在这里通过 IDEA 的智能提示来查看默认值
比如最基础的就是 Tomcat 的默认端口,我们之前修改过,还可以修改文件上传的单个文件最大的大小的限制,配置项为 spring.servlet.multipart.max-file-size=1MB
,默认大小为 1MB。
每一类配置实际上都对应着一个 XXXProperties
类,比如 ServerProperties
、MultipartProperties
,按着 Ctrl 点击配置即可查看,我们在 application.properties
中配置的值都会映射到对应的属性类的实例上,然后这些属性类实例会注册到在 IOC 容器中,变成 IOC 组件,在 Spring 中使用。
这是通过
@ConfigurationProperties
注解实现的
带有 web 启动器的 SpringBoot 默认启动的时候,注册的 XXXProperties
组件,有这些:
-
ServerProperties
-
WebMvcProperties
-
TaskExecutionProperties
-
ResourceProperties
-
LifecycleProperties
-
JacksonProperties
-
ProjectInfoProperties
-
TaskSchedulingProperties
-
MultipartProperties
按需加载所有自动配置项
我们有很多 starter,但是只有引入了的场景对应的自动配置才会开启,比如我只引入了 spring-boot-starter-web
,那么就只有此场景对应的属性的自动配置会开启,那 spring-boot-starter-data-jpa
对应的自动配置就不会开启,为什么?
我们之前说过,所有的 starter 都有一个依赖,就是 spring-boot-start
,而 spring-boot-start
也依赖了一个包,就是 spring-boot-autoconfigure
,这个包里有包含几乎所有场景的自动配置类,也就是各种 XXXAutoConfiguration
这样的类,比如:
-
WebMvcAutoConfiguration
-
BatchAutoConfiguration
-
ActiveMQAutoConfiguration
但是这些配置类都会有 @ConditionalOnClass({XXX.class})
、@ConditionalOnBean({XXX.class})
或 @Conditional({XXX.class})
,意思是只有 XXX类
加载了,这个类才会加载,而这些关键的决定自动配置类是否加载的类,都是由相应的 starter 引入的。以 WebMvcAutoConfiguration
为例,只有 Servlet
和 DispatcherServlet
和 WebMvcConfigurer
都在类路径中存在,WebMvcAutoConfiguration
才会生效。
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {}
@ConditionalOnClass
和@ConditionalOnBean
都是 SpringBoot 引入的注解,都是基于 Spring 框架的@Conditional
注解关于
@Conditional
注解,请看《Spring5-IOC 容器.docx》《SpringBoot 基础篇 -3- 容器功能.md》中的
@Conditional
小节
自动配置原理
主要看 @SpringBootApplication
注解的元注解 @EnableAutoConfiguration
@EnableAutoConfiguration
@EnableAutoConfiguration
的作用是启用 Spring 应用程序上下文的自动配置(Auto-configuration),主要是 bean 组件的自动配置,自动配置时,会尝试猜测和配置您可能需要的 bean。自动配置类通常以类路径中的类和已定义的 bean 应用来进行判断是否注册 bean。例如,如果您的类路径上有 tomcat-embedded.jar
,那么您可能需要一个 TomcatServletWebServerFactory
类型的 bean,除非你自己已经定义了一个 ServletWebServerFactory
类型的 bean。
auto-configuration 机制自动判断注册组件,就能省下我们手动写代码配置组件的工作。
Auto-configuration
试图尽可能地智能,当您定义更多自己的配置时,它会让路给你自己的配置。您总是可以手动 exclude()
任何您永远不想应用的配置 (如果您无法访问它们的类,请使用 excludeName()
)。你也可以通过 spring.autoconfiguration.exclude
属性排除它们。自动配置总是在注册用户定义 bean 之后应用。
使用 @EnableAutoConfiguration
注解(通常通过 @SpringBootApplication
注解)的类所在的包,具有特定的意义,通常用作默认扫描的包。通常建议您将 @EnableAutoConfiguration
(如果您不使用 @SpringBootApplication
) 放在包的根目录包中,以便可以搜索所有子包和类。
自动配置的类是常规的 Spring@Configuration
类型的 bean。它们是使用 SpringFactoriesLoader
机制来定位的。通常自动配置的 bean 被 @Conditional
注解修饰,最经常使用 @ConditionalOnClass
和 @ConditionalOnMissingBean
。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 自动配置的时候排除特定的配置类,传入被排除的类的class实例
Class<?>[] exclude() default {};
// 自动配置的时候排除特定的配置类,无法传入被排除的类的class实例时,传入被排除类的类名
String[] excludeName() default {};
}
主要分析 @AutoConfigurationPackage
注解和 AutoConfigurationImportSelector
选择器中引入的组件配置
@AutoConfigurationPackage
@AutoConfigurationPackage
的作用是通过 AutoConfigurationPackages
类注册包下的所有组件。当没有指定基包 (basePackages) 或基包类 (basePackageClasses) 时,将注册当前注解所在的包下的所有组件。
实际情况中:
@EnableAutoConfiguration
注解使用了@AutoConfigurationPackage
注解为元注解
@SpringBootApplication
注解又使用了@EnableAutoConfiguration
注解为元注解所以最终,当没有指定基包 (basePackages) 或基包类 (basePackageClasses) 时,将注册
@SpringBootApplication
修饰的类所在的包下的所有组件。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
// 应该扫描的包
String[] basePackages() default {};
// 用类所在的包指定应该扫描的包
Class<?>[] basePackageClasses() default {};
}
这个注解通过引入 AutoConfigurationPackages.Registrar
注册器来处理组件注册
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 通过 metadata 获取当前注解修饰的类所在的包名,然后调用 AutoConfigurationPackages.register 方法,注册这个包下的所有组件
// 这个当前注解,指的是使用了这个 ImportBeanDefinitionRegistrar 的 @Import 注解
// @AutoConfigurationPackage 注解使用了 @Import 注解为元注解,所以实际上这个当前注解是 @AutoConfigurationPackage
// @EnableAutoConfiguration 注解使用了 @AutoConfigurationPackage 注解为元注解
// @SpringBootApplication 注解又使用了 @EnableAutoConfiguration 注解为元注解
// 所以最终 决定的是 @SpringBootApplication 修饰的类所在的包名
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
AutoConfigurationPackages.Registrar
具体做了什么?很简单,向容器中注册一个 BasePackages
类型的 bean,BasePackages
的构造函数是将多个基础包名作为字符串数组传入来构造实例。
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
//
// 如果已经注册了,则往改构造方法中继续添加参数,
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
}
else {
// 向容器中注册一个 BasePackages 类型的bean,BasePackages的构造函数是将多个基础包名作为字符串数组传入来构造实例
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
至于之后,这个 BasePackages
类型的 bean 怎么起作用,我们就不继续深入探索了。
我们可以通过写点测试代码实践一下
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
final ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
// 原本想直接通过getBean方法获取 AutoConfigurationPackages.BasePackages 类型的ban,但是
// 根据权限设计和包设计,无法直接获取 AutoConfigurationPackages.BasePackages 类,
// 算了,直接使用 AutoConfigurationPackages.get 算了
List<String> strings = AutoConfigurationPackages.get(context);
System.out.println(strings);
}
}
输出:
[xyz.xiashuo]
就是 @SpringBootApplication
修饰的 MainApplication
所在的包
AutoConfigurationImportSelector
AutoConfigurationImportSelector
的作用是选择需要引入的自动配置类,然后将其注入到 IOC 容器中,AutoConfigurationImportSelector
是 DeferredImportSelector
的实现类。如果需要 @EnableAutoConfiguration
的自定义变体,也可以子类化这个类。
主要看对 ImportSelector
接口的实现的 AutoConfigurationImportSelector#selectImports
方法,作用是获取最终符合当前场景的名称后缀为 AutoConfiguration
的类。我们可以称之为 XXXAutoConfiguration
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// annotationMetadata 是注解相关的信息
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取最终符合当前场景的 XXXAutoConfiguration
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
首先看 AutoConfigurationImportSelector#isEnabled
,只判断 application.properties
中, spring.boot.enableautoconfiguration
如果为 true 或则默认不配置,就会自动注入那 127 个 XXXAutoConfiguration
// 其实有没有资格跟注解元数据屁关系没有
protected boolean isEnabled(AnnotationMetadata metadata) {
// 如果当前类是 AutoConfigurationImportSelector 类
if (getClass() == AutoConfigurationImportSelector.class) {
// 则判断 环境变量(Environment) 中 EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY是否配置为true,如果为空,则默认为true
// @EnableAutoConfiguration 注解的 ENABLED_OVERRIDE_PROPERTY 字段 实际上是 spring.boot.enableautoconfiguration
// 也就是说,在application.properties中, spring.boot.enableautoconfiguration 如果为true 或则默认不配置,就会自动注入那127个AutoConfiguration类
return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
return true;
}
再看 AutoConfigurationImportSelector#getAutoConfigurationEntry
,核心
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解配置
// 注解配置因为注解继承,可能有很多,这里只看 @EnableAutoConfiguration 注解的配置,即 exclude 和 excludeName
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取所有待注入的 XXXAutoConfiguration
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重,注意保留元素的顺序
configurations = removeDuplicates(configurations);
// 排除注解中配置的排除类,即需要排除的 XXXAutoConfiguration
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 经过筛选之后,符合当前项目需要的的 XXXAutoConfiguration
configurations = getConfigurationClassFilter().filter(configurations);
// 触发监听事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
首先重点看 getCandidateConfigurations
方法。获取所有的待注入的 XXXAutoConfiguration
,主要就是 spring-boot-autoconfigure-2.3.4.RELEASE.jar
的 META-INF/spring.factories
中的配置,有 127 个 XXXAutoConfiguration
会被注入
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 使用给定的类加载器。从“META-INF/spring.factories”中加载给定类型的工厂实现类的完全限定类名, SpringFactoriesLoader 是与 META-INF/spring.factories 文件打交道的主要API
// 后面再 getConfigurationClassFilter 方法中也有用到
// 获取名称为 EnableAutoConfiguration注解类对应的配置
// 主要就是 spring-boot-autoconfigure-2.3.4.RELEASE.jar 的 META-INF/spring.factories 中的配置,有127个 XXXAutoConfiguration会被注入
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;
}
具体的 127 个
XXXAutoConfiguration
自动配置类看本小节的最后一个小节:具体的127个XXXAutoConfiguration自动配置类
实际上,我们并不会把这个 127 个 XXXAutoConfiguration
全部注册到 IOC 容器中。实际上我们会经过筛选,筛选的过程就是 getConfigurationClassFilter().filter(configurations);
先看 getConfigurationClassFilter
方法,先获取过滤方法,主要就是 OnBeanCondition
和 OnClassCondition
和 OnWebApplicationCondition
private ConfigurationClassFilter getConfigurationClassFilter() {
if (this.configurationClassFilter == null) {
// 调用 SpringFactoriesLoader.loadFactories 获取 AutoConfigurationImportFilter 类型的过滤类
// SpringFactoriesLoader.loadFactories 的作用是 使用给定的类加载器。从“META-INF/spring.factories”中加载给定类型的工厂实现类的完全限定类名,
// SpringFactoriesLoader 是与 META-INF/spring.factories 文件打交道的主要API
// 在 spring-boot-autoconfigure-2.3.4.RELEASE.jar 的 META-INF/spring.factories 中默认有三个 AutoConfigurationImportFilter 对应的类:OnBeanCondition和OnClassCondition和OnWebApplicationCondition
// # Auto Configuration Import Filters
// org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
// org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
// org.springframework.boot.autoconfigure.condition.OnClassCondition,\
// org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
// 将 OnBeanCondition和OnClassCondition和OnWebApplicationCondition 组合成 ConfigurationClassFilter
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
然后进行过滤,即,调用 ConfigurationClassFilter#filter
,实际上还是通过判断 XXXAutoConfiguration
自动配置类上的 @Conditional
子注解
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
// 一般就三个 AutoConfigurationImportFilter实现类 :OnBeanCondition和OnClassCondition和OnWebApplicationCondition
// OnClassCondition : 检查 XXXAutoConfiguration 上的 @ConditionalOnClass 注解的的判断条件是否成立,即指定的类是否存在,
// OnBeanCondition : 检查 XXXAutoConfiguration 上的 @ConditionalOnBean 注解的的判断条件是否成立,即指定名称的bean是否存在
// OnWebApplicationCondition : 检查 XXXAutoConfiguration 上的 @ConditionalOnWebApplication 注解的的判断条件是否成立,主要是判断web应用是基于Servlet的还是基于Reactive
for (AutoConfigurationImportFilter filter : this.filters) {
// 仍然还是委托给 AutoConfigurationImportFilter 的 match 方法来进行匹配
// 返回的结果是true和false,按照被判断元素的顺序排列
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
// 如果有元素不匹配,将那个元素设置为空,表示删除
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
// 还需不需要剔除空元素,如果所有元素都匹配,那就没有必要再遍历一次了,直接返回即可
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
// 剔除空元素,表示删除空元素
for (String candidate : candidates) {
if (candidate != null) {
result.add(candidate);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return result;
}
最终 AutoConfigurationImportSelector#getAutoConfigurationEntry
返回的自动配置类,可能就只有几十个了,以 SpringBoot 的默认场景为例(POM 中只有 spring-boot-starter-parent
,<dependencies>
中不引入任何 starter),
0 = "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration"
1 = "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration"
2 = "org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration"
3 = "org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration"
4 = "org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration"
5 = "org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration"
6 = "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration"
7 = "org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration"
8 = "org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration"
9 = "org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration"
10 = "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration"
11 = "org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration"
AutoConfigurationImportFilter
这里多分析一下 OnBeanCondition
和 OnClassCondition
和 OnWebApplicationCondition
,AutoConfigurationImportFilter
只有一个方法就是 match
,抽象类 FilteringSpringBootCondition
实现 AutoConfigurationImportFilter
,并将具体逻辑委托为 FilteringSpringBootCondition#getOutcomes
,然后 OnBeanCondition
和 OnClassCondition
和 OnWebApplicationCondition
分别重写 FilteringSpringBootCondition#getOutcomes
。
SpringFactoriesLoader - 工厂加载器
通用工厂加载机制,供框架内部使用。
工厂的定义是,调用工厂方法返回实例
通用工厂的定义是,调用工厂方法,传入想要的实例的类型,返回相应类型的实例
SpringFactoriesLoader#loadFactories
就是这个通用工厂的获取实例的方法
SpringFactoriesLoader 从 META-INF/spring.factories
中加载并实例化给定类型的工厂。META-INF/spring.factories
文件可能出现在类路径中的多个 JAR 文件中。META-INF/spring.factories
必须采用 Properties
格式,其中键是接口或抽象类的完全限定类名,值是用逗号分隔的实现类名列表。
实际上在
META-INF/spring.factories
中,value 并不一定是 key 的实现类或者子类,可能完全没有关系,这使得SpringFactoriesLoader
更加灵活,比如org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的 value,此时是不能直接SpringFactoriesLoader#loadFactories
的,会直接报错,只能使用SpringFactoriesLoader#loadFactoryNames
获取生产出的实例的权限定性域名,然后再直接通过Class.forName
获取 Class 实例,然后调用newInstance
方法实例化即可
举个例子
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
example.MyService
是接口的名称,MyServiceImpl1
和 MyServiceImpl2
是两个实现。
简单实践如下:
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
final ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
List<AutoConfigurationImportFilter> autoConfigurationImportFilter = SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, null);
System.out.println(autoConfigurationImportFilter.size());
List<String> enableAutoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, null);
System.out.println(enableAutoConfigurations.size());
try {
Class<?> aClass = Class.forName(enableAutoConfigurations.get(0));
Object o = aClass.newInstance();
System.out.println(o.toString());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
输出
3
127
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration@2a62b5bc
具体的 127 个 XXXAutoConfiguration 自动配置类
这 127 个自动配置组件的主要功能是配置其他的组件,其本身并无其他作用。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
常见的 XXXAutoConfiguration 自动配置类
DispatcherServletAutoConfiguration
内部类 DispatcherServletConfiguration
的 multipartResolver
方法的写法很有意思,功能是将未正确命名的 MultipartResolver
类型的组件复制一遍,然后改个正确的名称。防止有些用户配置的文件上传解析器不符合规范。
所以开发者在自己注册 MultipartResolver
类型的组件的时候,名字可以随便写。SpringBoot 可以自动适配。
@Bean
// 容器中有 MultipartResolver 类型的bean
@ConditionalOnBean(MultipartResolver.class)
// 但是容器中没有 DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME 名字的bean,
// DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME 实际上就是 multipartResolver
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// resolver 就是容器自动注入 容器中已经存在的,但是名字不为 multipartResolver 的 MultipartResolver 类型的 bean,
// Detect if the user has created a MultipartResolver but named it incorrectly
// 直接返回,因为 此方法是一个@Bean犯法,且的名字就是 multipartResolver,因此最终会向容器注册一个 multipartResolver名字的bean
// 相当于把bean 复制一遍,更改名称
return resolver;
}
HttpEncodingAutoConfiguration
依赖 ServerProperties
,配置 CharacterEncodingFilter
,解决中文乱码问题。
@Bean
@ConditionalOnMissingBean
// 这个注解很妙,表示,如果容器中如果没有配置 CharacterEncodingFilter 类型的bean,我们就注入这个Bean,否则就不注入
// 这样用户如果已经注册了CharacterEncodingFilter类型的bean,SpringBoot自动配置就不再配置,否则,SpringBoot自动配置就帮我们注册
// 这也说明了,SpringBoot自动配置类注册bean的操作,应该放到最后,放到注册用户配置的bean之后,即放到 @ComponentScan 的扫描之后
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
SpringBoot 默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先,而这一点主要通过 @ConditionalOnMissingBean
注解实现
这也说明了一个问题:SpringBoot 自动配置类注册 bean 的操作,应该放到最后,放到注册用户配置的 bean 之后,即放到 @ComponentScan 的扫描之后
WebMvcAutoConfiguration
之后在学习SpringMVC 的时候,我们默认通过 WebMvcConfigurationSupport
配置 Web,同时自己通过实现 WebMvcConfigurer
自定义某些配置,来对 WebMvcConfigurationSupport
中的默认配置进行更改和补充,现在,默认依然是通过 WebMvcConfigurationSupport
配置 Web,不过有了 WebMvcAutoConfiguration
,我们直接可以通过在 application.properties
中添加特定前缀的 key 并设置 value,即可自定义 Web 的配置,不需要再手动写代码实现 WebMvcConfigurer
,编写方法,可谓相当的牛逼!
实际上配置的内容没有变,还是要注册这些组件,只不过在 SpringMVC 中,是我们手动写类去注册,现在,只需要配一下
Properties
文件即可,SpringBoot 的自动配置会帮我们来注册相应的组件,甚至Properties
文件中没有配置特定属性的时候,也可以默认注册相应的组件。
其中,主要就是配置 spring.mvc
开头的配置(映射为 WebMvcProperties
),spring.web
开头的配置(映射为 WebProperties
,2.7.5 版本)或 spring.resources
开头的配置(对应 ResourceProperties
,2.3.4 版本),这两个配置类最终会在 WebMvcAutoConfiguration
中使用。
具体配置了什么,怎么配置的,我们以后有时间再细细研究。
请参考《SpringBoot-Web 相关自动配置类源码解析 -WebMvcAutoConfiguration.md》
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
说明
WebMvcAutoConfiguration
需要在 DispatcherServletAutoConfiguration
后面装配,
这个跟 SpringMVC 的时候的装配顺序是一样的。
这里用到了之前学习 conditional 注解的时候的 configuration 类的的生效顺序
修改默认配置
SpringBoot 自动配置的基本原理
通过 @ConfigurationProperties
注解,application.properties
中的特定前缀的属性对应到特定 XXXProperties
类型的类,并注册为 IOC 中的组件,然后特定类型自动配置类(XXXAutoConfiguration
)根据需要获取特定的 XXXProperties
组件,通过 XXXProperties
组件读取 application.properties
中的配置,根据这些配置,来进行自动的判断和组件的装配。
定制化配置 SpringBoot 的方式:
-
用户直接自己手动注册组件,这种用的少,而且还不一定有用。得需要自动配置组件的时候配置了类似
@ConditionalOnMissingBean
的注解。 -
用户去看这个组件是获取的配置文件什么值,然后根据自己的需要定制这个配置。一般都是这种方式。
- 手动查找配置文件的目录:Application Properties,注意 SpringBoot 的版本。
- 直接查底层源码,看控制这个组件导入的配置的名称是什么。
简单实践
我们也可以在自定义的组件中通过注入相应的 XXXProperties
类型的组件来获取在 application.properties
中的配置,用起来非常方便。
在 POM 中引入 spring-boot-starter-web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
然后在 application.properties
中
# 应用的上下文路径
server.servlet.context-path=/SpringBoot-IOC
# 当前应用所在的Web服务器监听的本地端口
server.port=8889
编写测试
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
final ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
ServerProperties serverProperties = context.getBean(ServerProperties.class);
System.out.println(serverProperties.getPort()+serverProperties.getServlet().getContextPath());
}
}
输出
8889/SpringBoot-IOC
爽!
使用 SpringBoot 的最佳实践
-
根据实际需要引入场景启动器,
-
查看哪些自动配置类生效,自动配置了哪些组件(选做)
-
自己分析,引入场景对应的自动配置一般都生效了
-
配置文件
application.properties
中添加配置debug=true
开启自动配置报告。Positive matches 表示生效,Negative matches 表示不生效,每一个组件都有单独的日志。
-
-
是否需要修改
-
参照文档修改配置项,Application Properties
-
手动修改一下 banner 图,在
application.properties
中添加配置。spring.banner.image.location = classpath:banner.jpg
-
-
看生效的自动配置类使用了哪些
XXXProperties
,然后再看这些XXXProperties
中有哪些配置,各自影响哪些组件的自动注册 -
自定义注册组件
@Bean
、@Component
-
自定义器
XXXXXCustomizer
,自定义已有组件的默认行为,这个以后深入再学
-