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,自定义已有组件的默认行为,这个以后深入再学
-