美文网首页Spring-Boot
SpringBoot自动装配原理分析

SpringBoot自动装配原理分析

作者: 张振伟 | 来源:发表于2019-07-01 10:43 被阅读0次

    SpringBoot能流行起来我觉得有个重要的原因就是它的自动装配,想想使用SpringBoot与Spring开发项目的区别,第一反应就是减少了很多配置。

    其实SpringBoot是基于Spring基础之上的,通过MAVEN引入SpringBoot也会发现,它传递依赖了Spring。所以配置并没有减少,只是它帮我们自动配置了,这些自动配置的类都在spring-boot-autoconfigure-版本.jar包中。

    以一个最简单的SpringBoot应用为例,通常需要写这样一个启动类:

    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
        
        public static void main(String[] args) {
            SpringApplication springApplication = new SpringApplication(Application.class);
            springApplication.setBannerMode(Banner.Mode.LOG);
            springApplication.run(args);
        }
        
    }
    

    进入@SpringBootApplication注解内部,可以看到与自动装配有关的@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 {
        // ...
    }
    

    @EnableAutoConfiguration通过@import方式引入了EnableAutoConfigurationImportSelector

    @SuppressWarnings("deprecation")
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(EnableAutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
        // ...
    }
    

    EnableAutoConfigurationImportSelector是一个ImportSelector,我们查看其selectImports方法,其内部调用getCandidateConfigurations方法,读取META-INF/spring.factories里配置的Configuration类。

    @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); // META-INF/spring.factories
            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);
        }
    }
    

    在SpringBoot的spring-boot-autoconfigure-版本.jar包中,spring.factories文件里预定义了很多Configuration类:

    # Initializers
    org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
    
    # Application Listeners
    org.springframework.context.ApplicationListener=\
    org.springframework.boot.autoconfigure.BackgroundPreinitializer
    
    # Auto Configuration Import Listeners
    org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
    org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
    
    # Auto Configuration Import Filters
    org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
    org.springframework.boot.autoconfigure.condition.OnClassCondition
    
    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
    org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
    org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
    org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
    org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
    org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
    org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
    ......
    

    到这里自动装配的主流程就结束了,然后具体的装配逻辑就在各自的Configuration类中。

    以常见的HttpEncodingAutoConfiguration为例,这些Configuration类都添加了@Configuration注解表名都是配置类,然后通过例如:@ConditionalXXX等判断通过代码方式创建需要用到的Bean。

    @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;
            }
    
        }
        
    }
    

    相关文章

      网友评论

        本文标题:SpringBoot自动装配原理分析

        本文链接:https://www.haomeiwen.com/subject/vfowcctx.html