• springboot的@EnableAutoConfiguration起作用的原理


    通常我们启动一个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.
         * <p>
         * 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 {
                Enumeration<URL> urls = (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<String> getSet(String className, String key) {
                return getSet(className, key, null);
            }
    
            @Override
            public Set<String> getSet(String className, String key,
                    Set<String> 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自动加载配置的原理也分析完毕。

  • 相关阅读:
    知识加油站
    markdown 使用
    今日计划
    继续继续
    我回来了
    2020/5/8
    2020/5/10
    明天一定要学习了
    入驻博客园
    this is a test
  • 原文地址:https://www.cnblogs.com/swave/p/10966697.html
Copyright © 2020-2023  润新知