阅读 29

SpringBoot原理之自动配置机制详解

Springboot遵循“约定优于配置”的原则,使用注解对一些常规的配置项做默认配置,减少或不使用xml配置,让你的项目快速运行起来,下面这篇文章主要给大家介绍了关于SpringBoot原理之自动配置机制的相关资料,需要的朋友可以参考下

目录
  • 前言

  • Spring配置类

  • SpringBoot自动配置

    • 自动配置的概念

    • 自动配置的运行机制

      • 加载方式

    • SpringFactoriesLoader机制

      • SpringFactoriesLoader如何应用在自动配置中

      • 小结

        前言

        在当下的java生态里,SpringBoot已经成为事实上的开发标准,绝大多数人现在都是面向SpringBoot编程。SpringBoot是对Spring的进一步封装,整合了分布式系统上下游所需的各种类库和组件,并且实现了开箱即用,而这一切的底层基础就是SpringBoot的自动配置机制。

        Spring配置类

        Spring引入配置类是为了:1)替换冗长繁琐的配置文件,2)提供更灵活的bean定义方式。使用@Configuration注解去标记一个配置类,通过其中含有@Bean注解的方法去创建一个bean,如下代码

        1
        2
        3
        4
        5
        6
        7
        8
        9
        @Configuration
        public class HelloAutoConfiguration {
         
            @Bean
            HelloService helloService() {
                return new HelloService;
            }
         
        }

        即为一个简单的配置类,并且定义了一个HelloService的bean。在此之上,Spring还提供了一套条件加载机制,可以去动态控制一个配置类是否被加载。通过实现org.springframework.context.annotation.Condition接口,开发者就可以自己控制配置类的加载条件,满足很多复杂的场景

        SpringBoot自动配置

        介绍完了Spring的配置类,我们来看看SpringBoot是怎么利用这套机制去实现自动配置的。

        自动配置的概念

        首先,什么是自动配置?我们看一下SpringBoot对于自动配置类的定义:

        Auto-configuration classes are regular Spring @Configuration beans. They are located using the SpringFactoriesLoader mechanism (keyed against this class). Generally auto-configuration beans are @Conditional beans (most often using @ConditionalOnClass and @ConditionalOnMissingBeanannotations).

        自动配置类就是一个普通的@Configuration配置类,通常会带有一些@Conditional条件注解,并且使用SpringFactoriesLoader机制去定位加载它们(并非都是如此,还有其他一些Spring固有的加载方式,比如通过@ComponentScan包扫描或者显式@Import方式都可以让它们被发现)。

        自动配置的运行机制

        加载方式

        自动配置机制的启用是通过@EnableAutoConfiguration注解去控制的,因此需要在SpringBoot工程的入口类上启用该注解,但是通常,我们一般使用@SpringBootApplication来代替,后者是一个注解的合集,包含了一些必要的默认配置,其中就有@EnableAutoConfiguration注解,其类的注释上是这么描述的:

        Indicates a Configuration class that declares one or more @Bean methods and also triggers auto-configuration and component scanning. This is a convenience annotation that is equivalent to declaring @Configuration, @EnableAutoConfiguration and @ComponentScan.

        它本身既标识一个配置类,同时也开启了自动配置和组件扫描。

        回到@EnableAutoConfiguration注解上,我们看一下该注解的定义

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        @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 {};
        }

        其中@Import(AutoConfigurationImportSelector.class)是功能生效的关键,该注解导入了AutoConfigurationImportSelector组件到Spring环境中,开启自动配置类的扫描加载工作,该类实现了接口org.springframework.context.annotation.ImportSelector


        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        public interface ImportSelector {
         
            /**
             * Select and return the names of which class(es) should be imported based on
             * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
             * @return the class names, or an empty array if none
             */
            String[] selectImports(AnnotationMetadata importingClassMetadata);
         
           ....其他省略
        }

        其中selectImports方法会在Spring启动时被调用,用于返回所有的自动配置类,调用入口在org.springframework.context.annotation.ConfigurationClassParser类中,该类是Spring专门用来加载处理所有@Configuration配置类的,具体的加载细节,限于篇幅问题,就不在本文中展开说明了,读者们可自行去阅读源码,本人也许会在后续再另开一篇详细说明。接着说selectImports方法,我们来看一下自动配置类的加载过程,AutoConfigurationImportSelector对于该方法的具体实现为

        1
        2
        3
        4
        5
        6
        7
        8
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }

        isEnabled方法是一个开关,用于控制是否启用自动配置,逻辑很简单,略过不提,往下看,关键逻辑在getAutoConfigurationEntry方法中,跟下去

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return EMPTY_ENTRY;
            }
            AnnotationAttributes attributes = getAttributes(annotationMetadata);
            List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
            configurations = removeDuplicates(configurations);
            Set<String> exclusions = getExclusions(annotationMetadata, attributes);
            checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = getConfigurationClassFilter().filter(configurations);
            fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }

        很容易看到加载逻辑在getCandidateConfigurations方法中,后续代码是去重和过滤的过程,再往下看

        1
        2
        3
        4
        5
        6
        7
        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            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;
        }

        这个方法就很简单明显了,直接调用SpringFactoriesLoader去加载对应的内容,接下来我们再聊聊SpringFactoriesLoader机制是怎么回事。

        SpringFactoriesLoader机制

        SpringFactoriesLoader直译过来就是工厂加载机制,是Spring仿照Java的SPI机制实现的一套类加载机制,通过读取模块内的META-INF/spring.factories文件来加载类,该文件为Properties格式,其中key部分是一个Class全限定名称,可以是一个接口、抽象类或者注解等,而value部分是一个支持逗号分割的实现类列表,比如

        而SpringFactoriesLoader就是Spring提供的一个用于读取解析META-INF/spring.factories文件的工具类,通过传入一个Class类型加载其对应的实现类列表。

        SpringFactoriesLoader如何应用在自动配置中

        介绍完了SpringFactoriesLoader,我们来研究一下SpringBoot的自动配置机制中是怎么使用它的,回到上面的getCandidateConfigurations方法中,我们看一下这一行

        1
        2
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
                        getBeanClassLoader());

        其中第一个参数是key对应Class类型,第二个参数是用哪个ClassLoader去加载配置文件,我们看一下getSpringFactoriesLoaderFactoryClass这个方法返回的具体Class是什么

        1
        2
        3
        protected Class<?> getSpringFactoriesLoaderFactoryClass() {
            return EnableAutoConfiguration.class;
        }

        很简单,直接返回@EnableAutoConfiguration注解对应的class类型,那么自动配置类在META-INF/spring.factories文件中的配置方式就显而易见了,上面截图中最前面的部分

        1
        2
        3
        4
        5
        6
        7
        # AutoConfiguration
        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
        org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
        org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
        org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
        org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration

        就是对应的自动配置类了。这些被配置在此处的类都会被作为自动配置类加载到Spring中,然后进行相应的处理,发挥出每个类的功能作用。

        小结

        SpringBoot的自动配置机制就简单介绍到这里了,相信看官们看完了之后也都有了一些了解,当然这篇文章里还有很多相关内容没有涉及到,包括自动配置类的条件加载方式、多个类之间的加载顺序控制、排除和过滤机制,以及如何自定义自动配置类、重写框架默认行为等等,这些内容笔者会在后续的文章中再进行详细探讨。

        到此这篇关于SpringBoot原理之自动配置机制的文章就介绍到这了

        原文链接:https://mp.weixin.qq.com/s/GPgpzpK6L5_aLLbR7W9V9g


        文章分类
        代码人生
        版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 gxwowoo@163.com 举报,一经查实,本站将立刻删除。
        相关推荐