关联博文:
本文我们开始分析SpringBoot的自动配置原理。当我们想使用某个中间件时比如Redis,我们直接引入其starter然后在application.properties做一些简单配置即可使用。这就是SpringBoot的自动配置在起作用。
从本质来讲,自动配置无非就是有个处理器在启动流程的某个环节根据某个条件进行了自动扫描并注册BeanDefinition到BeanFactory中。那么这里我们要搞清楚这样几个问题:
表现是什么?
处理器是什么?
哪个环节?
如何扫描注册?
什么时候实例化?
我们先从@SpringBootApplication这个注解看起。
@SpringBootApplication
如下所示,该注解是一个复合注解其上面标注了@SpringBootConfiguration
和@EnableAutoConfiguration
。这里也标注了@ComponentScan
注解表明以当前类所在包为基础进行扫描、注册并实例化。
@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 { //... }
① @SpringBootConfiguration
SpringBootConfiguration
注解如下所示,其使用了@Configuration
注解进行标注表明其是一个配置类。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { // 标明@Configuration标注的类中那些@Bean标注的方法是否应该被代理,默认为true @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true; }
@Configuration
注解也是一个复合注解,其又被@Component
标注,表明配置类也是一个普通组件可以被扫描、注入到容器中。我们回过头继续看另外一个注解@EnableAutoConfiguration
。
② @EnableAutoConfiguration
这个注解是用来干嘛的呢?简单来讲就是启用Spring应用程序上下文的自动配置,尝试猜测和配置您可能需要的bean。自动配置类通常基于类路径和定义的bean来应用。例如,如果在类路径上有tomcat-embedded.jar,则可能需要一个TomcatServletWebServerFactory(除非定义了自己的ServletWebServerFactory bean)。
通常通过@SpringBootApplication 标注@EnableAutoConfiguration注解的包具有特定的意义,通常用作“默认值”。例如,它将在扫描@Entity类时使用。通常建议将@EnableAutoConfiguration(如果没有使用@SpringBootApplication)放在根包中,以便可以搜索所有子包和类。
Auto-configuration(自动配置)类也是常规的Spring Configuration bean,它们被spring的SpringFactoriesLoader机制进行加载(通过配置文件spring.factories配置的EnableAutoConfiguration属性值)。
通常自动配置类被标注了@Conditional
注解,大多数情况还会存在@ConditionalOnClass
和@ConditionalOnMissingBean
注解。用来判断当前自动配置类生效的时机。
@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 {}; }
也就是说只要我们应用中有了这样一个标注了@EnableAutoConfiguration 的主启动类,SpringBoot就可以启用自动配置流程完成扫描并加载、注册相关的BeanDefinition到容器中。那么SpringBoot从哪里扫描得到自动配置类信息?
我们回过头继续看注解。@Import注解是一个导入注解,允许导入标注了@Configuration的类、ImportSelector、ImportBeanDefinitionRegistrar以及其他常规组件(类似于AnnotationConfigApplicationContext#register)。这里导入了AutoConfigurationImportSelector这个组件,这个组件很重要。
@AutoConfigurationPackage注解呢,是一个复合注解被@Import(AutoConfigurationPackages.Registrar.class)标注导入了AutoConfigurationPackages.Registrar
。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
如下图所示,在AutoConfigurationImportSelector
的getCandidateConfigurations
方法中会从META-INF/spring.factories
配置文件中扫描那些EnableAutoConfiguration配置类。
在spring-boot-autoconfigure-2.2.4.RELEASE.jar!\META-INF\spring.factories
中,我们能够看到如下配置。
其中就有RedisAutoConfiguration
,其自动配置类如下所示,这也是我们为什么直接简洁配置就可以使用Redis的原因。
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }
而AutoConfigurationPackages.Registrar是用来做什么呢?
其本身实现了ImportBeanDefinitionRegistrar,重写了registerBeanDefinitions。也就说这个类主要用来注册BeanDefinition到BeanFactory中。注册了什么BeanDefinition?如下图所示以编程方式注册自动配置包名称,其中包名是从@EnableAutoConfiguration配置类设置的。
到了这里,我们就从表现上看到了自动配置注解、配置类扫描、bean定义的注解。
那么扫描、注册BeanDefinition发生在哪个环节呢?哪个处理器来执行的呢?bean实例化又发生在什么时候?