- 首先看主配置类中的SpringBootApplication注解,进点去
@SpringBootApplication public class RestfulcrudApplication { public static void main(String[] args) { SpringApplication.run(RestfulcrudApplication.class, args); } }
可以看到一堆注解,其中这个EnableAutoConfiguration就是用来开启自动配置的,点进去
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { }
可以看到这个注解注解导入了 AutoConfigurationImportSelector.class,这个类就是用来自动配置的,点进去
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
AutoConfigurationImportSelector.class中的 selectImports(AnnotationMetadata annotationMetadata)方法调用了getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata)方法,而getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata)方法调用了getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 方法,
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.filter(configurations, autoConfigurationMetadata); this.fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions); } }
getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 方法,调用了loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); 要想知道为什么可以自动配置,得先从loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())方法看起
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
-
把断点打在loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())方法上,开启debug模式
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { //loadFactoryNames 首先获取了一个工厂bean,传给了loadSpringFactories String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
在进入loadSpringFactories 方法之前要先看一下 jar 包下的META-INF 下的 spring.factories,它在这里, 里边的内容是
# PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener # Error Reporters org.springframework.boot.SpringBootExceptionReporter=\ org.springframework.boot.diagnostics.FailureAnalyzers # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor # Failure Analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer # FailureAnalysisReporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
进入loadSpringFactories 方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //实例化 一个一对多的键值对map MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //从这里开始就是重点了, urls 中保存的是各个 jar 包下的 META-INF/spring.factories 中 “每一段” 的第一行 Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); //创建一个一对多的map,就是一个键可以对应多个值 LinkedMultiValueMap result = new LinkedMultiValueMap(); //这个 while 是遍历I spring.factories 中的每一个“部分”,因为urls 存的是每一个“部分”的第一行 while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); //把url 封装成一个 Properties 对象,这个Properties 对象对象是很多个 spring.factories“部分” Properties properties = PropertiesLoaderUtils.loadProperties(resource); //获得Properties 对象的迭代器,这里迭代的就是一个一个“部分” Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { //创建一个Entry 对象, 这个对象的键将存者每一个部分的第一行,值存的是每一个“部分”除了第一行之外的部分 Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); //将值的部分转换成一个数组 String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; //就是将Entry 对象进行一次转化,使得值部分从一个字符串变成一个数组 result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); //返回result return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
然后,所有的META-INF/spring.factories下边对应的类,也就是xxxAutoConfiguration就会通过反射的方式实例化并且添加进容器了
-
然后,看看这些xxxAutoConfiguration是怎么是怎么工作的,看一个HttpEncodingAutoConfiguration
@Configuration @EnableConfigurationProperties({HttpProperties.class}) @ConditionalOnWebApplication( type = Type.SERVLET ) // CharacterEncodingFilter.class位于类路径上,才会实例化本类 @ConditionalOnClass({CharacterEncodingFilter.class}) //这个注解就是说和配置文件中如果 spring.http.encoding 那一项绑定,配置文件中不写的话 默认值为true @ConditionalOnProperty( prefix = "spring.http.encoding", value = {"enabled"}, matchIfMissing = true ) public class HttpEncodingAutoConfiguration { private final Encoding properties; //这个类之后一个有参构造器,也就是说实例化对象的时候会从容器中拿到 HttpProperties 的一个实例,进去 public HttpEncodingAutoConfiguration(HttpProperties properties) { this.properties = properties.getEncoding(); } /** 下边就是这个类的具体功能了 **/ @Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE)); return filter; } @Bean public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() { return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties); } private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered { private final Encoding properties; LocaleCharsetMappingsCustomizer(Encoding properties) { this.properties = properties; } public void customize(ConfigurableServletWebServerFactory factory) { if (this.properties.getMapping() != null) { factory.setLocaleCharsetMappings(this.properties.getMapping()); } } public int getOrder() { return 0; } } }
那么就不得不看看 HttpProperties.class
//终于找到这个了!!!!!!!!! //这个类会和配置文件中的 spring.http.xxx = xxx 的属性绑定,也就是说在配置文件中配置的 spring.http.xxx = xxx 会被作为实例化本类的属性 @ConfigurationProperties( prefix = "spring.http" ) public class HttpProperties { private boolean logRequestDetails; private final HttpProperties.Encoding encoding = new HttpProperties.Encoding(); public HttpProperties() { } public boolean isLogRequestDetails() { return this.logRequestDetails; } public void setLogRequestDetails(boolean logRequestDetails) { this.logRequestDetails = logRequestDetails; } public HttpProperties.Encoding getEncoding() { return this.encoding; } public static class Encoding { public static final Charset DEFAULT_CHARSET; private Charset charset; private Boolean force; private Boolean forceRequest; private Boolean forceResponse; private Map<Locale, Charset> mapping; public Encoding() { this.charset = DEFAULT_CHARSET; } public Charset getCharset() { return this.charset; } public void setCharset(Charset charset) { this.charset = charset; } public boolean isForce() { return Boolean.TRUE.equals(this.force); } public void setForce(boolean force) { this.force = force; } public boolean isForceRequest() { return Boolean.TRUE.equals(this.forceRequest); } public void setForceRequest(boolean forceRequest) { this.forceRequest = forceRequest; } public boolean isForceResponse() { return Boolean.TRUE.equals(this.forceResponse); } public void setForceResponse(boolean forceResponse) { this.forceResponse = forceResponse; } public Map<Locale, Charset> getMapping() { return this.mapping; } public void setMapping(Map<Locale, Charset> mapping) { this.mapping = mapping; } public boolean shouldForce(HttpProperties.Encoding.Type type) { Boolean force = type != HttpProperties.Encoding.Type.REQUEST ? this.forceResponse : this.forceRequest; if (force == null) { force = this.force; } if (force == null) { force = type == HttpProperties.Encoding.Type.REQUEST; } return force; } static { DEFAULT_CHARSET = StandardCharsets.UTF_8; } public static enum Type { REQUEST, RESPONSE; private Type() { } } } }
-
到了这里,就可以总结了:
-
首先EnableAutoConfiguration注解使得 spring.factories 中的类会被加载容器,说白了就是会实例化一个对象并把这个对象添加到Spring的某个map中,
-
要实例化这些对象的时候它们要要求从容器中拿到另外要给xxxProperties类队形作为参数传进去他们的构造函数,为这个类的变量赋值
-
而这些xxxProperties是和 配置文件中某个特定的配置绑定的,例如上边的HttpProperties就和配置文件中的spring.http为前缀的配置绑定,这样子在实例化的时候就会把这些配置作为属性赋值。
-