通常我们启动一个springboot项目会在启动方法中增加@SpringBootApplicatoin注解,该注解中包含了@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 { /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ @AliasFor(annotation = EnableAutoConfiguration.class) Class>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; /** * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses} * for a type-safe alternative to String-based package names. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to * scan for annotated components. The package of each class specified will be scanned. ** Consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. *
@return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class>[] scanBasePackageClasses() default {}; }
那么@EnableAutoConfiguration是如何自动起作用的呢。我们先来看看@EnableAutoConfiguration的定义
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ Class>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ String[] excludeName() default {}; }
我们看看EnableAutoConfiguration的注释定义:
Enable auto-configuration of the Spring Application Context, attempting to guess and * configure beans that you are likely to need. Auto-configuration classes are usually * applied based on your classpath and what beans you have defined.
大概的意思是:容器尝试猜测你想配置的bean,然后自动加载。
很欣喜的发现了@EnableAutoConfiguration里面引用了@Import(AutoConfigurationImportSelector.class),直觉看起来像这个AutoConfigurationImportSelector里面引入了我们那些自动配置(数据源、拦截器)的类。
我们来看看AutoConfigurationImportSelector的定义:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
原来AutoConfigurationImportSelector类实现了org.springframework.context.annotation.DeferredImportSelector
那么实现了该类的public String[] selectImports(AnnotationMetadata annotationMetadata)后,spring在解析bean的时候,就会自动调用该方法来加载拓展类,也就是selectImports方法返回的名称列表来加载类。
很容易起想到selectImports里面就返回了AutoEnableConfiguration的那些数据源、拦截器配置的类。!
接下来看看selectImports的具体实现:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; }
//从org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader的loadMetadata加载要自动配置的资源数据
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry( autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
看下AutoConfigurationMetadataLoader的源码看,可以看到默认加载的自动配置的路径为:/META-INF/spring-autoconfigure-metadata.properteis。
final class AutoConfigurationMetadataLoader { //默认加载自动配置的文件位置 protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties"; private AutoConfigurationMetadataLoader() { } public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); } static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { try { Enumerationurls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path); Properties properties = new Properties(); while (urls.hasMoreElements()) { properties.putAll(PropertiesLoaderUtils .loadProperties(new UrlResource(urls.nextElement()))); } return loadMetadata(properties); } catch (IOException ex) { throw new IllegalArgumentException( "Unable to load @ConditionalOnClass location [" + path + "]", ex); } } static AutoConfigurationMetadata loadMetadata(Properties properties) { return new PropertiesAutoConfigurationMetadata(properties); } /** * {@link AutoConfigurationMetadata} implementation backed by a properties file. */ private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata { private final Properties properties; PropertiesAutoConfigurationMetadata(Properties properties) { this.properties = properties; } @Override public boolean wasProcessed(String className) { return this.properties.containsKey(className); } @Override public Integer getInteger(String className, String key) { return getInteger(className, key, null); } @Override public Integer getInteger(String className, String key, Integer defaultValue) { String value = get(className, key); return (value != null) ? Integer.valueOf(value) : defaultValue; } @Override public Set getSet(String className, String key) { return getSet(className, key, null); } @Override public Set getSet(String className, String key, Set defaultValue) { String value = get(className, key); return (value != null) ? StringUtils.commaDelimitedListToSet(value) : defaultValue; } @Override public String get(String className, String key) { return get(className, key, null); } @Override public String get(String className, String key, String defaultValue) { String value = this.properties.getProperty(className + "." + key); return (value != null) ? value : defaultValue; } } }
可以看到配置的默认加载的配载类有如下,这些类都被@Confiration注解,并且都自动创建了一些@Bean来使用组件的特性,通过这些自动创建的bean来代替我们系统中手动声明bean,达到加快开发效率的效果。
[org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration, org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration, org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration, org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration, org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration, org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration, org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration, org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration, org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration, org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration, 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.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.servlet.WebSocketServletAutoConfiguration]
至此,springboot自动加载配置的原理也分析完毕。