zoukankan      html  css  js  c++  java
  • 006 自动配置

    一 .概述

      在springboot之中最令我们喜欢的特性大概就是自动配置了.springboot根据自动配置帮助我们实现整个开发环境的配置,这可以让我们不需要每次都完成那些重复的配置工作了.

      本此,我们就分析一下自动配置的原理.


     二 .程序的启动类  

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

    在我们springboot的程序启动类之中,我们使用了一个注解@SpringBootApplicatiion注解.

    我们下面看看这个注解帮助我们完成了什么.  

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

    我们发现在这个注解上面带有三个子注解,我们下面来看看这写注解是干什么用的.  

    @Configuration
    public @interface SpringBootConfiguration {
    
    }

    第一个注解,我们发现就是一个配置类,只不过是springvoot特意定义的一个注解而已.  

    @Repeatable(ComponentScans.class)
    public @interface ComponentScan

    第三个注解,我们非常的熟悉了,那就是扫包注解,在springboot之中,默认的扫包策略就是启动包的子包进行扫描.

      现在,我们所有的注意都放在了第二个注解上面.

    @AutoConfigurationPackage
    @Import(EnableAutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {

    我们发现在这个注解上面有两个子注解.我们看看他们是干什么用的.

    /**
     * Indicates that the package containing the annotated class should be registered with
     * {@link AutoConfigurationPackages}.
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(AutoConfigurationPackages.Registrar.class)
    public @interface AutoConfigurationPackage {
    
    }

    从上面的注视之中,我们可以看到,它帮助我们注册该包的所有的bean.

      我们再来看看帮助我们引入的那个类是干什么用的.

    public class EnableAutoConfigurationImportSelector
            extends AutoConfigurationImportSelector {
    
        @Override
        protected boolean isEnabled(AnnotationMetadata metadata) {
            if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
                return getEnvironment().getProperty(
                        EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
                        true);
            }
            return true;
        }
    
    }

    我们看看它的父类:  

    @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            try {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                        .loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = getAttributes(annotationMetadata);
                List<String> configurations = getCandidateConfigurations(annotationMetadata,
                        attributes);
                configurations = removeDuplicates(configurations);
                configurations = sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = getExclusions(annotationMetadata, attributes);
                checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = filter(configurations, autoConfigurationMetadata);
                fireAutoConfigurationImportEvents(configurations, exclusions);
                return configurations.toArray(new String[configurations.size()]);
            }
            catch (IOException ex) {
                throw new IllegalStateException(ex);
            }
        }

    我们发现,这个就是一个导入bean的组件的@Import而已,我们现在需要关注的就是到底为我们导入了什么?

        protected static final String PATH = "META-INF/"
                + "spring-autoconfigure-metadata.properties";

    我们发现从该配置之中加入了很多了bean.这些bean很多都是一些配置类,基本上都存在springboot的自动配置包下.也就是说,最终这个路径下的配置文件之中的bean大多都会注册到我们的bean之中.  

    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    另外我们发现也从这个配置文件之中引入了很多的组件.

      也就是说,springboot会从上面的一些配置文件之中加载很多的bean.  


     三 .自动配置类

      我们从上面看到了springboot几乎为我们加载了自动配置包下面的所有的自动配置类.

      我们看看我们这个包下面到底有些什么东西.

      里面所有的类都是自动配置类,我们看一个简单的就好了. 

    @Configuration
    @ConditionalOnClass(Gson.class)
    public class GsonAutoConfiguration {
    
        @Bean
        @ConditionalOnMissingBean
        public Gson gson() {
            return new Gson();
        }
    
    }

    我们发现上面就是一个简单的配置类,如果类路径下面由一个Gson类,而且容器之中没有gson对象,springboot就会帮助我们向容器之中注入一个bean.


    四 .@Conditional注解

      在springboot之中(实际上在spring4之中引入),为我们引入了一些条件注解,这些注解会帮助我们在运行环境下确定是否向容器之中注册bean.

      实际上就是一个boolean的判断,如果条件满足,就会注入,否则就不会注入.

      我们看看几个常见的注解:

      [1]ConditionalOnBean : 当容器之中含有这个bean的时候,就会注入

      [2]ConditionalOnClass : 当类路径下有这个class的时候就会注册

      [3]ConditionalOnProperty : 当环境变量之中有这个属性的时候就会注册

      等等等,springboot就是通过这些注解完成的自动的装配.


     五 .自动装配的分析

      我们使用一个比较简单的配置类进行自动配置的一般流程的分析:

    @Configuration
    @EnableConfigurationProperties(HttpEncodingProperties.class)
    @ConditionalOnWebApplication
    @ConditionalOnClass(CharacterEncodingFilter.class)
    @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
    public class HttpEncodingAutoConfiguration {
    
        private final HttpEncodingProperties properties;
    
        public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
            this.properties = properties;
        }
    
        @Bean
        @ConditionalOnMissingBean(CharacterEncodingFilter.class)
        public CharacterEncodingFilter characterEncodingFilter() {
            CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
            return filter;
        }
    
        @Bean
        public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
            return new LocaleCharsetMappingsCustomizer(this.properties);
        }
    
        private static class LocaleCharsetMappingsCustomizer
                implements EmbeddedServletContainerCustomizer, Ordered {
    
            private final HttpEncodingProperties properties;
    
            LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) {
                this.properties = properties;
            }
    
            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                if (this.properties.getMapping() != null) {
                    container.setLocaleCharsetMappings(this.properties.getMapping());
                }
            }
    
            @Override
            public int getOrder() {
                return 0;
            }
    
        }
    
    }

    [1]首先,我们发现这个是一个配置类.

    [2]@ConditionalOnWebApplication 表示需要在web环境下才注册

    [3]@ConditionalOnClass(CharacterEncodingFilter.class) 表示需要在类路径下有一个类才进行注册

    [4]@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) 这个表示在配置文件下有这个属性才可以,但是在这里给出了默认值,也就是说一定成立的,除非用户主动取消掉这个配置.

    [5]@EnableConfigurationProperties(HttpEncodingProperties.class)  这个注解表示完成对HttpEncodingProperties这个属性配置类进行属性的填充.

    下面,我们来分析一下源代码了:

        private final HttpEncodingProperties properties;
    
        public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
            this.properties = properties;
        }

    我们从上面的注解之中看到了会向容器之中注册一个HttpEncodingProperties类,然后本自动配置类就实例化了.也就是说,现在的HttpEncodingProperties 的属性被填充好了.

    @Bean
        @ConditionalOnMissingBean(CharacterEncodingFilter.class)
        public CharacterEncodingFilter characterEncodingFilter() {
            CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
            filter.setEncoding(this.properties.getCharset().name());
            filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
            filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
            return filter;
        }

    表示向容器之中添加一个字符编码过滤器.

    @Bean
        public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
            return new LocaleCharsetMappingsCustomizer(this.properties);
        }

    表示向容器之中添加一个自定义配置器.


     六 .自动配置的基本流程

    [1]一般会有一个属性配置类与属性文件进行绑定

    [2]然后通过这个属性的对自动配置i类进行属性的填充

    [3]然后就是创建不同bean到容器之之中,直到完成整个springboot的自动配置.


     七 .自动配置报告

      我们现在知道了springvoot的自动配置原理了,但是我们看到自动配置非常的复杂,我们如何好能确定一个bean是否已经被初始化好了呢?

      springboot为我们提供了自动配置报告.  

    debug=true

    我们在主配置文件之中,可以添加如上的配置,然后再启动springboot 的时候就会开启自动配置报告的生成.

      在我们的控制台上就显示了不同的配置报告的信息.

    从这里显示的就是springboot帮助我们添加的bean,也就是自动配置成功的类.

    下面显示的就是没有自动成功的类.

  • 相关阅读:
    课堂练习
    软件工程课堂练习二维数组子数组和最大值,只要连续就好
    结对项目电梯调度
    第二次课堂练习
    软件工程课堂练习二维数组子数组和的最大值
    敏捷开发方法
    软件工程个人作业
    电梯调度
    创意
    02合并frame
  • 原文地址:https://www.cnblogs.com/trekxu/p/9739548.html
Copyright © 2011-2022 走看看