前言
之前的几篇文章,我们介绍到了spring 4 带来的改变–> java 零配置,那么spring boot 是怎么在它的基础上实现自动配置的呢?由于这点设计的范围过大,因此这里我们就只关注mvc方面的自动化配置,涉及如下几个类:
- EmbeddedServletContainerAutoConfiguration(这个已经在spring boot 源码解析12-servlet容器的建立中分析过了,这里就不分析了)
- DispatcherServletAutoConfiguration
- ErrorMvcAutoConfiguration(在spring boot 源码解析14-ImportSelector及默认错误页面中已经有解释过了,这里就不解释了)
- WebMvcAutoConfiguration(这里的内容比较多,我们下一篇文章进行分析)
- HttpMessageConvertersAutoConfiguration
- HttpEncodingAutoConfiguration
- MultipartAutoConfiguration
- ServerPropertiesAutoConfiguration
- WebClientAutoConfiguration
DispatcherServletAutoConfiguration
DispatcherServletAutoConfiguration 在类上声明了如下注解:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)
- @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) 说明了它在自动化配置有高优先级
- @Configuration 是一个配置类,会交由ConfigurationClassPostProcessor进行加载
- @ConditionalOnWebApplication 必须在web环境下生效
- @ConditionalOnClass(DispatcherServlet.class) 必须在类路径下有DispatcherServlet存在
- @AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class) 在EmbeddedServletContainerAutoConfiguration之后进行加载
由于DispatcherServletAutoConfiguration有2个内部类,DispatcherServletConfiguration,DispatcherServletRegistrationConfiguration.根据加载规则,会优先加载内部类再加载DispatcherServletAutoConfiguration中的定义.接下来我们分别看下其实现:
DispatcherServletConfiguration有如下注解:
@Configuration @Conditional(DefaultDispatcherServletCondition.class) @ConditionalOnClass(ServletRegistration.class) @EnableConfigurationProperties(WebMvcProperties.class)
- @Configuration 表明其是一个配置类
@Conditional(DefaultDispatcherServletCondition.class) 引入了一个条件类,其判断逻辑如下:
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage.Builder message = ConditionMessage .forCondition("Default DispatcherServlet"); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); List<String> dispatchServletBeans = Arrays.asList(beanFactory .getBeanNamesForType(DispatcherServlet.class, false, false)); if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome.noMatch(message.found("dispatcher servlet bean") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome .noMatch(message.found("non dispatcher servlet bean") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } if (dispatchServletBeans.isEmpty()) { return ConditionOutcome .match(message.didNotFind("dispatcher servlet beans").atAll()); } return ConditionOutcome.match(message .found("dispatcher servlet bean", "dispatcher servlet beans") .items(Style.QUOTE, dispatchServletBeans) .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); }
- 如果beanFactory 中存在id 为 dispatcherServlet,类型为DispatcherServlet的bean ,返回不匹配.否则进入第2步
- 如果beanFactory中包含id为dispatcherServlet的bean,返回不匹配
- 如果beanFactory中不包含类型为DispatcherServlet的bean,返回匹配
- 其他情况下,返回匹配
- @ConditionalOnClass(ServletRegistration.class)–> 当前的类路径下存在ServletRegistration.class.
@EnableConfigurationProperties(WebMvcProperties.class)–> 导入了一个属性配置类,其WebMvcProperties.class有如下注解:
@ConfigurationProperties(prefix = "spring.mvc")
这也就意味着我们在application.properties 中配置spring.mvc前缀的属性,就会自动配置到WebMvcProperties中.
DispatcherServletConfiguration中有如下被@Bean注解的方法
dispatcherServlet,注册dispatcherServlet,id为dispatcherServlet.代码如下:
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet() { // 实例化DispatcherServlet DispatcherServlet dispatcherServlet = new DispatcherServlet(); // 设置是否允许分发Option请求,默认为true dispatcherServlet.setDispatchOptionsRequest( this.webMvcProperties.isDispatchOptionsRequest()); // 设置是否允许分发Trace请求,默认为false dispatcherServlet.setDispatchTraceRequest( this.webMvcProperties.isDispatchTraceRequest()); // 设置是否没有handeler处理请求时抛出异常,默认为false dispatcherServlet.setThrowExceptionIfNoHandlerFound( this.webMvcProperties.isThrowExceptionIfNoHandlerFound()); return dispatcherServlet; }
- 实例化DispatcherServlet
- 设置是否允许分发Option请求,默认为true
- 设置是否允许分发Trace请求,默认为false
- 设置是否没有handeler处理请求时抛出异常,默认为false
multipartResolver,代码如下:
@Bean @ConditionalOnBean(MultipartResolver.class) @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) public MultipartResolver multipartResolver(MultipartResolver resolver) { return resolver; }
在beanFactory中存在MultipartResolver类型的bean时,但是其id不是multipartResolver,那么此时就会重新注册一个id为multipartResolver的bean。
DispatcherServletRegistrationConfiguration,有如下注解:
@Configuration @Conditional(DispatcherServletRegistrationCondition.class)// 当beanFactory中有DispatcherServlet,并且id为dispatcherServlet @ConditionalOnClass(ServletRegistration.class) // ServletRegistration在类路径存在的话 @EnableConfigurationProperties(WebMvcProperties.class) @Import(DispatcherServletConfiguration.class)
- @Configuration表明其是一个配置类
@Conditional(DispatcherServletRegistrationCondition.class) 引入了DispatcherServletRegistrationCondition条件类,其判断逻辑如下:
@Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory); if (!outcome.isMatch()) { return outcome; } return checkServletRegistration(beanFactory); }
- 获得beanFactory
调用checkDefaultDispatcherName,看是否beanFactory中是否包含id 为dispatcherServlet,类型为DispatcherServlet的bean.如果不包含,则直接返回不匹配,否则进行第3步.代码如下:
private ConditionOutcome checkDefaultDispatcherName( ConfigurableListableBeanFactory beanFactory) { List<String> servlets = Arrays.asList(beanFactory .getBeanNamesForType(DispatcherServlet.class, false, false)); boolean containsDispatcherBean = beanFactory .containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); if (containsDispatcherBean && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { return ConditionOutcome .noMatch(startMessage().found("non dispatcher servlet") .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)); } return ConditionOutcome.match(); }
- 获得DispatcherServlet类型的bean
- containsDispatcherBean =beanFactory 中是否包含id为 dispatcherServlet的bean
- 如果beanFactory 中包含id 为dispatcherServlet的bean,但是其类型不是DispatcherServlet,则返回不匹配,否则返回匹配
checkServletRegistration 代码如下:
private ConditionOutcome checkServletRegistration( ConfigurableListableBeanFactory beanFactory) { ConditionMessage.Builder message = startMessage(); List<String> registrations = Arrays.asList(beanFactory .getBeanNamesForType(ServletRegistrationBean.class, false, false)); boolean containsDispatcherRegistrationBean = beanFactory .containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); if (registrations.isEmpty()) { if (containsDispatcherRegistrationBean) { return ConditionOutcome .noMatch(message.found("non servlet registration bean").items( DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } return ConditionOutcome .match(message.didNotFind("servlet registration bean").atAll()); } if (registrations .contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) { return ConditionOutcome.noMatch(message.found("servlet registration bean") .items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } if (containsDispatcherRegistrationBean) { return ConditionOutcome .noMatch(message.found("non servlet registration bean").items( DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); } return ConditionOutcome.match(message.found("servlet registration beans") .items(Style.QUOTE, registrations).append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)); }
- 获得beanFactory中类型为ServletRegistrationBean的bean name
- containsDispatcherRegistrationBean = beanFactory中是否包含id 为dispatcherServletRegistration的bean
如果beanFactory中不包含ServletRegistrationBean的bean
- 包含id为dispatcherServletRegistration的bean,则返回不匹配
- 如果不包含id为dispatcherServletRegistration的bean,则返回匹配
- 如果在beanFactory中类型为ServletRegistrationBean的bean name 中包含dispatcherServletRegistration则返回不匹配
- 如果在beanFactory中类型为ServletRegistrationBean的bean name 中不包含dispatcherServletRegistration,且 beanFactory中包含id 为dispatcherServletRegistration的bean,则返回不匹配
- 其他情况(beanFactory中类型为ServletRegistrationBean的bean name 中不包含dispatcherServletRegistration,其不包含id为dispatcherServletRegistration的bean),返回匹配.
- @EnableConfigurationProperties(WebMvcProperties.class) 可通过spring.mvc 进行配置
- @Import(DispatcherServletConfiguration.class) 导入了DispatcherServletConfiguration 的配置.
DispatcherServletRegistrationConfiguration 中没有内部类,只有一个被@bean注解的方法,代码如下:
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME) @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public ServletRegistrationBean dispatcherServletRegistration( DispatcherServlet dispatcherServlet) { ServletRegistrationBean registration = new ServletRegistrationBean( dispatcherServlet, this.serverProperties.getServletMapping()); registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); registration.setLoadOnStartup( this.webMvcProperties.getServlet().getLoadOnStartup()); if (this.multipartConfig != null) { registration.setMultipartConfig(this.multipartConfig); } return registration; }
- 当beanfactory中存在id 为dispatcherServlet,类型为DispatcherServlet则会进行注册(id 为 dispatcherServletRegistration,类型为ServletRegistrationBean),否则不进行注册。
- 直接使用DispatcherServlet和server配置中的servletPath路径构造ServletRegistrationBean, ServletRegistrationBean实现了ServletContextInitializer接口,在onStartup方法中对应的Servlet注册到Servlet容器中,所以这里DispatcherServlet会被注册到Servlet容器中,对应的urlMapping为server.servletPath配置.默认为/,加载顺序为-1.
HttpMessageConvertersAutoConfiguration
HttpMessageConvertersAutoConfiguration在类上有如下注解:
@Configuration @ConditionalOnClass(HttpMessageConverter.class) @AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class }) @Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class })
- @Configuration–>配置类
- @ConditionalOnClass(HttpMessageConverter.class)–>当前类路径下存在HttpMessageConverter
- @AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class }) –> 在GsonAutoConfiguration,JacksonAutoConfiguration之后进行加载
- @Import({ JacksonHttpMessageConvertersConfiguration.class,GsonHttpMessageConvertersConfiguration.class })–>导入了JacksonHttpMessageConvertersConfiguration,GsonHttpMessageConvertersConfiguration的配置
HttpMessageConvertersAutoConfiguration中有StringHttpMessageConverterConfiguration内部类,因此为因此对其进行加载.
StringHttpMessageConverterConfiguration有如下注解:
@Configuration @ConditionalOnClass(StringHttpMessageConverter.class) @EnableConfigurationProperties(HttpEncodingProperties.class)
- 当前类路径下存在StringHttpMessageConverter
- 可以在application.properties中通过spring.http.encoding进行配置
StringHttpMessageConverterConfiguration中声明的bean只有一个,当beanFactory中不存在StringHttpMessageConverter时进行注册,如下:
@Bean @ConditionalOnMissingBean public StringHttpMessageConverter stringHttpMessageConverter() { StringHttpMessageConverter converter = new StringHttpMessageConverter( this.encodingProperties.getCharset()); converter.setWriteAcceptCharset(false); return converter; }
StringHttpMessageConverter 默认编码为:UTF-8
HttpMessageConvertersAutoConfiguration 只注册了一个HttpMessageConverters,当beanFactory中不存在HttpMessageConverters的bean时.代码如下:
@Bean @ConditionalOnMissingBean public HttpMessageConverters messageConverters() { return new HttpMessageConverters(this.converters == null ? Collections.<HttpMessageConverter<?>>emptyList() : this.converters); }
HttpEncodingAutoConfiguration
HttpEncodingAutoConfiguration类上有如下注解:
@Configuration @EnableConfigurationProperties(HttpEncodingProperties.class) @ConditionalOnWebApplication @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
- @Configuration –> 配置类
- @EnableConfigurationProperties(HttpEncodingProperties.class) –> 可通过spring.http.encoding.xxx 进行配置
- @ConditionalOnWebApplication–>web 环境
- @ConditionalOnClass(CharacterEncodingFilter.class) –> 当前类路径下存在CharacterEncodingFilter
- @ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true) –> spring.http.encoding的值为true时,方可进行配置,默认为true
HttpEncodingAutoConfiguration中不存在内部类,其注册了2个bean,分别如下:
注册CharacterEncodingFilter,当beanFactory中不存在CharacterEncodingFilter类型的bean时.代码如下;
@Bean @ConditionalOnMissingBean(CharacterEncodingFilter.class) public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; }
声明了CharacterEncodingFilter,默认编码为UTF-8,对请求进行强制编码(默认),对响应编码为utf-8为false(默认,可通过spring.http.encoding.forceResponse = true 进行配置)
注册LocaleCharsetMappingsCustomizer.无条件.代码如下:
@Bean public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() { return new LocaleCharsetMappingsCustomizer(this.properties); }
MultipartAutoConfiguration
MultipartAutoConfiguration类上有如下注解:
@Configuration @ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class }) @ConditionalOnProperty(prefix = "spring.http.multipart", name = "enabled", matchIfMissing = true) @EnableConfigurationProperties(MultipartProperties.class)
- @Configuration –> 配置类
- @ConditionalOnClass({ Servlet.class,StandardServletMultipartResolver.class,MultipartConfigElement.class }) –> 当前类路径下存在Servlet,StandardServletMultipartResolver,MultipartConfigElement
- @ConditionalOnProperty(prefix = “spring.http.multipart”, name = “enabled”, matchIfMissing = true)–> spring.http.multipart.enabled 等于true,进行配置,默认为true
- @EnableConfigurationProperties(MultipartProperties.class) 可通过spring.http.multipart.xxx 进行配置
MultipartAutoConfiguration中没有配置类,其注册了2个bean,代码如下:
注册MultipartConfigElement,当beanFactory中不存在MultipartConfigElement时注册.代码如下:
@Bean @ConditionalOnMissingBean public MultipartConfigElement multipartConfigElement() { return this.multipartProperties.createMultipartConfig(); }
调用:
public MultipartConfigElement createMultipartConfig() { MultipartConfigFactory factory = new MultipartConfigFactory(); if (StringUtils.hasText(this.fileSizeThreshold)) { factory.setFileSizeThreshold(this.fileSizeThreshold); } if (StringUtils.hasText(this.location)) { factory.setLocation(this.location); } if (StringUtils.hasText(this.maxRequestSize)) { factory.setMaxRequestSize(this.maxRequestSize); } if (StringUtils.hasText(this.maxFileSize)) { factory.setMaxFileSize(this.maxFileSize); } return factory.createMultipartConfig(); }
- 文件大小默认为0,也就是不限制
- 默认请求大小为 10MB
- 最大文件大小为 1MB
注册 StandardServletMultipartResolver.当beanFactory不存在MultipartResolver时.代码如下:
@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) @ConditionalOnMissingBean(MultipartResolver.class) public StandardServletMultipartResolver multipartResolver() { StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver(); multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily()); return multipartResolver; }
ServerPropertiesAutoConfiguration
ServerPropertiesAutoConfiguration 有如下注解:
@Configuration @EnableConfigurationProperties @ConditionalOnWebApplication
- @Configuration–> 注解类
@EnableConfigurationProperties –> 当beanFactory中不存在org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor时,注册如下2个bean:
- id 为org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor,class为ConfigurationPropertiesBindingPostProcessor
- id为org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store,class为ConfigurationPropertiesBindingPostProcessor
- @ConditionalOnWebApplication–>web环境
ServerPropertiesAutoConfiguration中没有配置内部类,只有2个被@bean注解的方法,如下:
注册ServerProperties,当beanFactory中不存在ServerProperties时.代码如下:
@Bean @ConditionalOnMissingBean(search = SearchStrategy.CURRENT) public ServerProperties serverProperties() { return new ServerProperties(); }
注册DuplicateServerPropertiesDetector,代码如下:
@Bean public DuplicateServerPropertiesDetector duplicateServerPropertiesDetector() { return new DuplicateServerPropertiesDetector(); }
在内置容器配置时会用到,该bean的作用是为了确保只存在一个ServerProperties bean的定义。代码如下:
@Override public void customize(ConfigurableEmbeddedServletContainer container) { // ServerProperties handles customization, this just checks we only have // a single bean String[] serverPropertiesBeans = this.applicationContext .getBeanNamesForType(ServerProperties.class); Assert.state(serverPropertiesBeans.length == 1, "Multiple ServerProperties beans registered " + StringUtils .arrayToCommaDelimitedString(serverPropertiesBeans)); }
WebClientAutoConfiguration
WebClientAutoConfiguration 有如下注解:
@Configuration @AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
- @Configuration–> 配置类
- @AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class) –> HttpMessageConvertersAutoConfiguration 之后进行加载
WebClientAutoConfiguration中只有一个内部类,RestTemplateConfiguration
RestTemplateConfiguration 注解如下:
@Configuration @ConditionalOnClass(RestTemplate.class)
- @Configuration –> 配置类
- @ConditionalOnClass(RestTemplate.class)–> 当前类路径下存在RestTemplate
RestTemplateConfiguration只注册了一个RestTemplateBuilder.当beanFactory中不存在RestTemplateBuilder时.代码如下:
@Bean @ConditionalOnMissingBean public RestTemplateBuilder restTemplateBuilder() { RestTemplateBuilder builder = new RestTemplateBuilder(); HttpMessageConverters converters = this.messageConverters.getIfUnique(); if (converters != null) { builder = builder.messageConverters(converters.getConverters()); } List<RestTemplateCustomizer> customizers = this.restTemplateCustomizers .getIfAvailable(); if (!CollectionUtils.isEmpty(customizers)) { customizers = new ArrayList<RestTemplateCustomizer>(customizers); AnnotationAwareOrderComparator.sort(customizers); builder = builder.customizers(customizers); } return builder; }
- 实例化RestTemplateBuilder
- 如果存在HttpMessageConverters,则进行配置
- 如果存在RestTemplateCustomizer,则排序后,进行配置
自动化配置加载流程解析
spring boot 声明了这么多的自动化配置类,那么它的加载流程是怎么的,我们就拿DispatcherServletConfiguration来分析,其它的配置类,加载流程都是一样的.
DispatcherServletAutoConfiguration加载顺序:
@SpringBootApplication有@EnableAutoConfiguration注解,又因为@Import(EnableAutoConfigurationImportSelector.class),因此在ConfigurationClassParser#doProcessConfigurationClass中会调用processImports 进行处理,最终调用EnableAutoConfigurationImportSelector#selectImports进行处理获得org.springframework.boot.autoconfigure.EnableAutoConfiguration 的配置,因为EnableAutoConfigurationImportSelector是DeferredImportSelector的子类,因此会添加到deferredImportSelectors中,最终在ConfigurationClassParser#processDeferredImportSelectors进行处理.最终调用processImports 进行对org.springframework.boot.autoconfigure.EnableAutoConfiguration配置的类依次进行加载,此时就会对DispatcherServletAutoConfiguration进行加载。
DispatcherServletAutoConfiguration 不是ImportSelector,ImportBeanDefinitionRegistrar 的子类,因此会当做一个配置类调用processConfigurationClass进行加载.(注意此时DispatcherServletAutoConfiguration是由该应用所导入的) 由于其有内部类,因此会首先加载DispatcherServletConfiguration和DispatcherServletRegistrationConfiguration.
DispatcherServletConfiguration因为有@EnableConfigurationProperties 注解,在@EnableConfigurationProperties注解上有@Import,因此会调用ConfigurationClassParser#processImports.
在该方法中会调用EnableConfigurationPropertiesImportSelector#selectImports方法,最终返回的是org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar,org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar.代码如下:
return new String[] { ConfigurationPropertiesBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
ConfigurationPropertiesBeanRegistrar,ConfigurationPropertiesBindingPostProcessorRegistrar 是ImportBeanDefinitionRegistrar的子类,因此会实例化加入到DispatcherServletConfiguration中的ImportBeanDefinitionRegistrar.代码如下:
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
处理完@Import后,依次处理被@bean注解的方法(dispatcherServlet,multipartResolver),此时向configClass加入了BeanMethod.代码如下:
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); }
DispatcherServletConfiguration因为有@Import(DispatcherServletConfiguration.class)注解,因此也就间接导入了DispatcherServletConfiguration,EnableConfigurationPropertiesImportSelector.
因此加载时会先调用ConfigurationClassParser#processImports.在该方法中会依次遍历DispatcherServletConfiguration,EnableConfigurationPropertiesImportSelector,对于EnableConfigurationPropertiesImportSelector的处理,和DispatcherServletConfiguration一样,这里就不在赘述了.
对于DispatcherServletConfiguration的处理,会调用processConfigurationClass(candidate.asConfigClass(configClass)),注意此时candidate指的是DispatcherServletConfiguration, configClass指的是DispatcherServletRegistrationConfiguration。
asConfigClass代码如下:
public ConfigurationClass asConfigClass(ConfigurationClass importedBy) throws IOException { if (this.source instanceof Class) { return new ConfigurationClass((Class<?>) this.source, importedBy); } return new ConfigurationClass((MetadataReader) this.source, importedBy); }
调用
public ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) { this.metadata = metadataReader.getAnnotationMetadata(); this.resource = metadataReader.getResource(); this.importedBy.add(importedBy); }
经过这步处理,意味着DispatcherServletConfiguration所对应的ConfigurationClass是被DispatcherServletRegistrationConfiguration导入的.
在ConfigurationClassParser#processConfigurationClass中,首先从configurationClasses中查看DispatcherServletConfiguration是否解析过,这里由于DispatcherServletConfiguration已经解析过了,同时DispatcherServletConfiguration是被DispatcherServletRegistrationConfiguration导入的,因此会调用ConfigurationClass#mergeImportedBy.这里的代码如下:
ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) { if (configClass.equals(it.next())) { it.remove(); } } } }
ConfigurationClass#mergeImportedBy 代码如下:
public void mergeImportedBy(ConfigurationClass otherConfigClass) { this.importedBy.addAll(otherConfigClass.importedBy); }
注意,这里加入的是DispatcherServletRegistrationConfiguration
处理完@Import后,处理被@bean注解的方法–>dispatcherServletRegistration。和DispatcherServletConfiguration一样.
由于DispatcherServletAutoConfiguration没有被@bean注解的方法,因此DispatcherServletAutoConfiguration的解析到此结束
因此DispatcherServletAutoConfiguration共解析了3个配置类:
- DispatcherServletConfiguration
- DispatcherServletRegistrationConfiguration
- DispatcherServletAutoConfiguration
接下来会调用ConfigurationClassBeanDefinitionReader#loadBeanDefinitions进行处理
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions.在该方法中会依次遍历之前解析的ConfigurationClass,调用loadBeanDefinitionsForConfigurationClass.代码如下:
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { if (trackedConditionEvaluator.shouldSkip(configClass)) { String beanName = configClass.getBeanName(); if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { this.registry.removeBeanDefinition(beanName); } this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); return; } if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
该方法的逻辑如下:
- 判断是否需要跳过.
如果该配置是被导入的,则调用registerBeanDefinitionForImportedConfigurationClass.判断逻辑如下:
public boolean isImported() { return !this.importedBy.isEmpty(); }
registerBeanDefinitionForImportedConfigurationClass代码如下:
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) { AnnotationMetadata metadata = configClass.getMetadata(); AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef); configBeanDef.setScope(scopeMetadata.getScopeName()); String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry); AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata); BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition()); configClass.setBeanName(configBeanName); if (logger.isDebugEnabled()) { logger.debug("Registered bean definition for imported class '" + configBeanName + "'"); } }
- 根据configClass中配置的AnnotationMetadata 实例化AnnotatedGenericBeanDefinition
进行属性的设置
- 解析该configClass的Scope,一般是singleton
- 生成bean的id
- 设置bean的一些属性,如LazyInit,Primary等
- 生成BeanDefinitionHolder,并对其尝试进行代理,最后向registry进行注册
遍历该配置类声明的BeanMethods,调用loadBeanDefinitionsForBeanMethod.
- 调用loadBeanDefinitionsFromImportedResources 处理该类导入的配置文件
- 调用loadBeanDefinitionsFromRegistrars处理该类配置的importBeanDefinitionRegistrars
关于这部分的详解,可以看spring boot 源码解析11-ConfigurationClassPostProcessor类加载解析
对于DispatcherServletConfiguration来说,由于该类是被DispatcherServletAutoConfiguration和DispatcherServletRegistrationConfiguration到入的,因此会调用registerBeanDefinitionForImportedConfigurationClass.因为DispatcherServletConfiguration有ConfigurationPropertiesBeanRegistrar,ConfigurationPropertiesBindingPostProcessorRegistrar,因此会调用loadBeanDefinitionsFromRegistrars.代码如下:
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { for (Map.Entry<ImportBeanDefinitionRegistrar, AnnotationMetadata> entry : registrars.entrySet()) { entry.getKey().registerBeanDefinitions(entry.getValue(), this.registry); } }
因此会依次调用ConfigurationPropertiesBeanRegistrar,ConfigurationPropertiesBindingPostProcessorRegistrar的registerBeanDefinitions.
ConfigurationPropertiesBeanRegistrar#registerBeanDefinitions,代码如下:
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { MultiValueMap<String, Object> attributes = metadata .getAllAnnotationAttributes( EnableConfigurationProperties.class.getName(), false); List<Class<?>> types = collectClasses(attributes.get("value")); for (Class<?> type : types) { String prefix = extractPrefix(type); String name = (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName()); if (!registry.containsBeanDefinition(name)) { registerBeanDefinition(registry, type, name); } } }
最终注册了一个id为spring.mvc.org.springframework.boot.autoconfigure.web.WebMvcProperties,类型为org.springframework.boot.autoconfigure.web.WebMvcProperties的bean.
ConfigurationPropertiesBindingPostProcessorRegistrar#registerBeanDefinitions,代码如下:
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(BINDER_BEAN_NAME)) { BeanDefinitionBuilder meta = BeanDefinitionBuilder .genericBeanDefinition(ConfigurationBeanFactoryMetaData.class); BeanDefinitionBuilder bean = BeanDefinitionBuilder.genericBeanDefinition( ConfigurationPropertiesBindingPostProcessor.class); bean.addPropertyReference("beanMetaDataStore", METADATA_BEAN_NAME); registry.registerBeanDefinition(BINDER_BEAN_NAME, bean.getBeanDefinition()); registry.registerBeanDefinition(METADATA_BEAN_NAME, meta.getBeanDefinition()); } }
当beanFactory中不存在org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor时,注册如下2个bean:
- id 为org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor,class为ConfigurationPropertiesBindingPostProcessor
- id为org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store,class为ConfigurationPropertiesBindingPostProcessor
对于DispatcherServletRegistrationConfiguration来说,由于该类是被DispatcherServletAutoConfiguration导入的,因此会调用registerBeanDefinitionForImportedConfigurationClass.同样,也会调用loadBeanDefinitionsFromRegistrars.在 ConfigurationPropertiesBeanRegistrar#registerBeanDefinitions中,由于之前已经注册了id为spring.mvc.org.springframework.boot.autoconfigure.web.WebMvcProperties,类型为org.springframework.boot.autoconfigure.web.WebMvcProperties的bean.因此可以任何loadBeanDefinitionsFromRegistrars没有产生任何副作用.
对于 DispatcherServletAutoConfiguration来说,由于是被spring boot的启动类导入的,因此会调用registerBeanDefinitionForImportedConfigurationClass.又因为该类没有被@bean 注解的方法,没有导入其他的配置,因此后续不再处理.
自动配置总结
配置的bean | 声明的类 | 条件 |
---|---|---|
DispatcherServlet(id为DispatcherServlet) | DispatcherServletConfiguration | 1.web环境 2.beanFactory不存在dispatcherServlet |
MultipartResolver(id为multipartResolver) | DispatcherServletConfiguration | 1.web环境,2.beanFactory不存在dispatcherServlet,3 不存在id为multipartResolver,类型为MultipartResolver的bean |
dispatcherServletRegistration | DispatcherServletRegistrationConfiguration | 1.web环境,2.beanFactory中有DispatcherServlet类型,并且id为dispatcherServlet的bean |
StringHttpMessageConverter(id为stringHttpMessageConverter) | StringHttpMessageConverterConfiguration | 1.当前类路径下存在StringHttpMessageConverter,2.beanFactory中不存在StringHttpMessageConverter |
HttpMessageConverters(id为messageConverters) | HttpMessageConvertersAutoConfiguration | 1.当前类路径下存在StringHttpMessageConverter,2.beanFactory中不存在HttpMessageConverters |
CharacterEncodingFilter(id为characterEncodingFilter) | HttpEncodingAutoConfiguration | 1.web 环境,2.当前类路径下存在CharacterEncodingFilter, 3. beanFactory中不存在CharacterEncodingFilter类型的bean |
LocaleCharsetMappingsCustomizer(id为localeCharsetMappingsCustomizer) | HttpEncodingAutoConfiguration | 无 |
MultipartConfigElement(id为multipartConfigElement) | MultipartAutoConfiguration | 1.当前类路径下存在Servlet,StandardServletMultipartResolver,MultipartConfigElement,2.spring.http.multipart.enabled 等于true,3.beanFactory中不存在MultipartConfigElement |
StandardServletMultipartResolver(id为multipartResolver) | MultipartAutoConfiguration | 1.当前类路径下存在Servlet,StandardServletMultipartResolver,MultipartConfigElement,2.spring.http.multipart.enabled 等于true,3.beanFactory中不存在MultipartResolver |
ServerProperties(id为serverProperties) | ServerPropertiesAutoConfiguration | 1.web环境,2.beanFactory中不存在ServerProperties |
DuplicateServerPropertiesDetector(id为duplicateServerPropertiesDetector) | ServerPropertiesAutoConfiguration | 1.web环境 |
RestTemplateBuilder(id为restTemplateBuilder) | RestTemplateConfiguration | 1.当前类路径下存在RestTemplate,2.beanFactory中不存在RestTemplateBuilder |