9.springboot自动配置源码

springboot自动加载的源码解析:

1.springboot的启动类代码如下:
    //使用@SpringBootApplication标签标明当前类是springboot的启动类
    @SpringBootApplication
    public class MainApplication {
        public static void main(String[] args) {
            System.out.println("启动springboot项目...");
            ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
            System.out.println("启动成功!");
        }
    }
    
2.仔细研究@SpringBootApplication注解:
    ...
    1.@SpringBootConfiguration
    2.@EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {
        ...
        //该属性可以设置扫描哪个包:例如@SpringBootApplication(scanBasePackages = "cn.com")
        String[] scanBasePackages() default {};
    }
    
3.详解1.@SpringBootConfiguration注解:发现其底层也是@Configuration注解,该注解的作用:告诉springboot当前是配置类,可以配合@bean往容器中加载组件
    ...
    @Configuration
    public @interface SpringBootConfiguration {
        ...
    }
    例如直接在启动类中使用@bean注册组件:
        @SpringBootApplication
        public class MainApplication {
            public static void main(String[] args) {
                System.out.println("启动springboot项目...");
                ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
                System.out.println("启动成功!");
            }
            //此方法是可行的,但是一般是使用专门的配置类进行注册
            @Bean
            public Car getCar() {
                System.out.println("car的创建方法!");
                return new Car("宝马", "14万");
            }
        }
        
4.详解2注解:@EnableAutoConfiguration自动配置,这是springboot的核心:
    4.1.@EnableAutoConfiguration源码
        ....
        @AutoConfigurationPackage
        @Import({AutoConfigurationImportSelector.class})
        public @interface EnableAutoConfiguration {
            ...
        }
        4.1.2研究其下面注解:@AutoConfigurationPackage的内容
            //往容器中导入Registrar类
            @Import({Registrar.class})
            public @interface AutoConfigurationPackage {
                ...
            }
            Registrar类的详情如下:
            static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
                //注册组件
                public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                    AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
                }
            }

4.1.3 @Import({AutoConfigurationImportSelector.class})代码研究(自动导入各种选择器)
        AutoConfigurationImportSelector类代码:
            public String[] selectImports(AnnotationMetadata annotationMetadata) {
                ...
                AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
                 ...
             }
        下续代码:
            protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
                   ...
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                  ...
            }
        下续代码:
            最终发现:所有的选择器导入的bean都在spring-boot-autoconfigure.jar/META-INF/spring.factories中配置:约有127项
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");

按需开启自动配置项:
虽然127个场景所有的自动配置会在springboot启动时全部加载,
最终会进行按需配置,容器中不会存在127个所有的组件,会按需配置:
例子:
   例如aop类中:如果项目中有Advice.class这个类,会进行aop bean的注册,但是项目并没有导入Advice的包,自然不会有该类,也不会配置该场景的启动器
       //条件选择器:当容器中有这个类时执行下述方法
        @ConditionalOnClass({Advice.class})
        static class AspectJAutoProxyingConfiguration {
        }
    例如:amqp类中:
        //条件选择器:当容器中存在RabbitTemplate这个类时,会执行下述方法
        @ConditionalOnClass({RabbitTemplate.class, Channel.class})
        @EnableConfigurationProperties({RabbitProperties.class})
        @Import({RabbitAnnotationDrivenConfiguration.class})
        public class RabbitAutoConfiguration {
        }
注意,注意,注意.....
自动配置的类后面都是XXXAutoConfiguration
每个场景的自动配置类都会将配置关联到一个配置类上(使用@EnableConfigurationProperties(类名)标签去关联springboot的配置文件和这个类)
示例如下:
    1.HttpEncodingAutoConfiguration自动配置类
            //1.标明当前类是springboot配置类
            @Configuration(
                proxyBeanMethods = false
            )
            //2.当前自动配置类和ServerProperties类进行绑定
            @EnableConfigurationProperties({ServerProperties.class})
            //3.当前应用是传统的servlet的web工程
            @ConditionalOnWebApplication(
                type = Type.SERVLET
            )
            //4.项目是否存在CharacterEncodingFilter类
            @ConditionalOnClass({CharacterEncodingFilter.class})
            //5.条件判断注解:springboot的配置文件中是否存在erver.servlet.encoding为前缀的标签,matchIfMissing=true,即使不存都生效
            @ConditionalOnProperty(
                prefix = "server.servlet.encoding",
                value = {"enabled"},
                matchIfMissing = true
            )
            public class HttpEncodingAutoConfiguration {
                ...
            }
    
    
    2.绑定的ServerProperties类代码如下:
            @ConfigurationProperties(
                //指定配置文件的前缀为:server
                prefix = "server",
                ignoreUnknownFields = true
            )
            public class ServerProperties {
                ...
                private Integer port;
                private InetAddress address;
                ...
            }
    
    3.在springboot的配置文件中:application.properties中的绑定配置为:
        server.port=80080

原理总结:
        在绑定的配置类中,各属性有自己默认的值,但当需要更改时,在application.properties中进行配置,@ConfigurationProperties标签
会将其绑定到配置类上的具体属性,达到修改默认值的效果!

 

posted @ 2022-05-11 21:34  努力的达子  阅读(99)  评论(0编辑  收藏  举报