zoukankan      html  css  js  c++  java
  • SpringBoot的自动装配原理

    在使用 springboot的时候,我们体会到了它的舒服之处,容易上手,开箱即用。撇去了繁杂的xml配置,使用注解和 yml的方式统一配置和管理。使我们的开发变得非常的优雅。

    用了这么久的springboot  天天在那看@SpringBootApplication 那么 springboot的 自动装配原理是什么呢?

     

    接下来认认真真来看一次 springboot 装配原理。

    自动配置原理

    @SpringBootApplication
    public class DemoTestApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoTestApplication.class, args);
        }
    
    }

    点开 @SpringBootApplication 可以看到

    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {

    删去一些不重要的注解,其中最为重要的两个注解为

    @SpringBootConfiguration 和 @EnableAutoConfiguration

     

    @SpringBootConfiguration

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Configuration
    public @interface SpringBootConfiguration {

    经由分析,可以简单看到 @SpringBootConfiguration 里边值得我们注意的一个 注解是 @Configuration,这表明这是一个 spring的配置类,我们将它交给 IOC容器来管理。

    @EnableAutoConfiguration

    见名知意,开启自动自动配置。看来这就是我们要找的主要类了,那我们来看看它到底做了什么

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage // 自动配置包
    @Import({AutoConfigurationImportSelector.class}) //自动配置导入选择器
    public @interface EnableAutoConfiguration {

    我们可以看到,这个类最最重要的一条配置就是 @Import 导入了一个 自动配置导入选择器,那么这个类到底干嘛的呢?

    AutoConfigurationImportSelector.class

        // 我们来看看它的 selectImports 放
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            } else {
                AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry 
                    //这个方法就是我们要着重分析的方法了,getAutoConfigurationEntry 获取自动配置条目
                    = this.getAutoConfigurationEntry(annotationMetadata);
                return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
            }
        }
    
        protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
            if (!this.isEnabled(annotationMetadata)) {
                return EMPTY_ENTRY;
            } else {
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                /*
                  所有的配置都存放在configurations中,
                  而这些配置都从getCandidateConfiguration中获取,
                  这个方法是用来获取候选的配置。
                */
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                ...
            }
        }

    点进 getCandidateConfigurations(annotationMetadata, attributes)方法,我们知道了这个方法可以用来获取所有候选的配置,那么这些候选的配置又是从哪来的呢?

        protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
            List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
        }
    
        protected Class<?> getSpringFactoriesLoaderFactoryClass() {
            return EnableAutoConfiguration.class;
        }

    可以看到源码中有一个 loadFactoryNames 中传入了一个 getSpringFactoriesLoaderFactoryClass,通过这个方法我看到了熟悉的 EnableAutoConfiguratioin 注解,标注了这个类的包不就是 @SpringBootApplication 吗?

    所以我们可以得出结论:它兜兜转转绕了这么多地方,就是为了将启动类所需的所有资源导入。

    然后看到 springboot 给我们的提示  Assert.notEmpty("No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");

    这个断言的意思是,configurations必须非空,否则就打印一段话,No auto configuration classes found in META-INF/spring.factories,我们把这个逻辑反过来想想。如果这个集合不为空,是不是就代表找到了这个spring.factories并且会去加载这个文件中的内容呢?

    我们点进去这个 文件会发现里边包含了很多自动配置属性

    大致浏览一下spring.factories 会发现,大部分的文件名都是 xxxAutoConfiguration结尾

     

    那么 loadFactoryNames 这个方法干嘛的?经过几次跳转我们发现代码中最重要的一行代码

    Properties properties = PropertiesLoaderUtils.loadProperties(resource);

    它将我们传入的文件内容包封装成了 Properties 对象

    也就是 这个 EnableAutoConfiguration值,然后把他们添加到容器中。

    (图截不下...一直到最后)

    然后每一个自动配置类会进行自动配置功能

     

    以 HttpAutoConfiguration 为例

    我们点进去HttpEncodingAutoConfiguration 看看,然后分析一下他是怎么从 yml里获取我们的配置的。

    @Configuration// 表明当前是一个配置类
    @EnableConfigurationProperties({ServerProperties.class})// 启用指定类的@ConfigurationProperties功能
    //这里是启动 ServerProperties 的@ConfigurationProperties
    @ConditionalOnWebApplication // 如果当前是 Web 项目则生效
    @ConditionalOnClass({CharacterEncodingFilter.class}) // 项目组存在 CharacterEncodingFilter 这个类则生效
    @ConditionalOnProperty(
        prefix = "server.servlet.encoding",
        value = {"enabled"},
        matchIfMissing = true
    )// 默认这个字段 server.servlet.encoding = true
    public class HttpEncodingAutoConfiguration {
        
        private final Encoding properties;
    
        //当类中 只有一个 有参构造器的时候,这个时候的参数 会从 IOC容器里去找
        //所以这里,我们就将与 yml 对应的 Properties 文件与 AutoConfigutation这个类 绑定了
        public HttpEncodingAutoConfiguration(ServerProperties properties) {
            this.properties = properties.getServlet().getEncoding();
        }
    
        @Bean
        @ConditionalOnMissingBean//如果当前类不存在,就执行这个方法。
        public CharacterEncodingFilter characterEncodingFilter() {
            CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            //从 properties/yml获取值 
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
            return filter;
        }
    }

    关于 ServerProperties

    @ConfigurationProperties(
        prefix = "server",
        ignoreUnknownFields = true
    ) //看到这里我们非常的熟悉,我们不也常用这个 @ConfigurationProperties 来写自己的配置类吗
    public class ServerProperties {

    小总结:

    springboot 启动会加载大量的配置类,如果我们需要的功能有默认写好的配置类,那么我们直接用就行了,还可以看看这些自动配置类装载了哪些组件啊,对应的 Properties 文件里有哪些可以配置的属性啊。

    如果没有的话,那我们可就要自己来配置了,相信看完本文后大家也都知道,对应的基本套路了。

    XxxxAutoConfig

    XxxxProperties

    然后在自己项目类路径下 也就是  src/resources/META-INF/spring.factories(自己创建的) 里注册配置一下就行了

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    xxx.xxx.xxx包.XxxxAutoConfig,

    转载:https://www.yuque.com/zzuli-tree/my6a8r/lkk3a0

     

     

     

  • 相关阅读:
    秒杀系统性能测试和优化
    性能测试分析过程(三)linux下查看最消耗CPU/内存的进程
    [改善Java代码]注意方法中传递的参数要求(replaceAll和replace的区别)
    [改善Java代码]由点及面,一叶知秋----集合大家族
    [改善Java代码]非稳定排序推荐使用List
    [改善Java代码]多线程使用Vector或HashTable
    [改善Java代码]减少HashMap中元素的数量
    [改善Java代码]使用shuffle打乱列表
    [改善Java代码]集合运算时使用更优雅的方式
    [改善Java代码]集合中的元素必须做到compareTo和equals同步
  • 原文地址:https://www.cnblogs.com/shadoll/p/14422633.html
Copyright © 2011-2022 走看看