Spring Boot自动化配置浅析

Spring Boot的自动化配置为开发者简化了大部分模板化配置,开发者能将更多的精力放到业务逻辑的实现上。如果能了解其内部的实现机制,使用能更加得心应手,同时本身对于这种功能的实现方式也很好奇,于是就有了这篇文档。


Spring Boot的自动化配置的实现需要关注两个注解

@EnableAutoConfiguration

在应用启动的时候,扫描解析所有的自动配置类。

@Conditional

在解析配置的时候,根据不同的环境参数决定是否加载配置。


在分析自动配置内部实现之前,先简单了解一下Spring Boot的启动流程

1、初始化SpringApplication

判断应用是否为Web应用,获取应用的Listeners, Initializers。

2、实例化ApplicationContext容器

根据是否是Web应用,分别实例化AnnotationConfigEmbeddedWebApplicationContext和AnnotationConfigApplicationContext。

3、Spring Boot根据配置参数初始化ApplicationContext

---- 开始AbstractApplicationContext.refresh()

4、初始化ApplicationContext内部的BeanFactory

5、处理ApplicationContext的BeanFactoryPostProcessor

解析Spring配置,包括xml配置和代码配置,注册BeanDefinition到BeanDefinitionRegistry中

6、为ApplicationContext注册BeanPostProcessor

其中有3个需要关注的BeanPostProcessor:

(1)AutowiredAnnotationBeanPostProcessor处理@Autowired和@Value

(2)RequiredAnnotationBeanPostProcessor处理@Required

(3)CommonAnnotationBeanPostProcessor 处理@PostConstruct,@PreDestroy等注解

7、初始化ApplicationContext的MessageSource

8、初始化ApplicationContext的事件处理机制EventMulticaster

9、初始化并启动Web容器

Spring Boot提供了三个容器的实现:Tomcat,Jetty,Undertow,默认使用的是Tomcat。

10、注册ApplicationListener到ApplicationContext

11、初始化所有的非lazyInit的Singleton bean


自动化配置实现逻辑分析

1、注册ConfigurationClassPostProcessor

启动流程的第2 步实例化ApplicationContext,框架会主动往BeanDefinitionRegistry中注册一个ConfigurationClassPostProcessor类型的Bean

2、ConfigurationClassPostProcessor处理配置类

2.1 什么时候处理

在启动流程的第5步BeanFactoryPostProcessor的处理过程中,会开始对ConfigurationClassPostProcessor的处理

2.2 ConfigurationClassPostProcessor的作用

从其命名就能大概了解其是用于处理配置类的。其实现了接口BeanDefinitionRegistryPostProcessor。

BeanDefinitionRegistryPostProcessor接口:在普通的BeanFactoryPostProcessor处理之前,注册bean definition到BeanDefinitionRegistry中。简单说来,它就是用来注册spring bean的定义的。

ConfigurationClassPostProcessor解析处理配置类的注解@Configuration,@Component,@ComponentScan,@Import,@ImportResource,@Bean等。主要是添加配置参数到Environment,注册Bean definition到BeanDefinitionRegistry等。 @EnableAutoConfiguration的处理就是在这个阶段。

3、@EnableAutoConfiguration注解的处理

3.1 @EnableAutoConfiguration定义

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@AutoConfigurationPackage

@Import(EnableAutoConfigurationImportSelector.class)

public @interface EnableAutoConfiguration {

        Class<?>[] exclude() default {};

        String[] excludeName() default {};

}

3.2 @EnableAutoConfiguration的EnableAutoConfigurationImportSelector

在@EnableAutoConfiguration的实现中,会引入EnableAutoConfigurationImportSelector,EnableAutoConfigurationImportSelector实现了接口DeferredImportSelector。

DeferredImportSelector接口: DeferredImportSelector是ImportSelector接口的一个标识接口,继承于ImportSelector,但是没有定义其他方法.DeferredImportSelector与ImportSelector的作用类似,根据配置类上@Configuration注解的配置信息来导入配置,只不过DeferredImportSelector的处理是在@Configuration配置类处理完之后。

EnableAutoConfigurationImportSelector获取META-INF/spring.factories(classpath下所有jar包)配置中key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的配置,再根据exclude和excludeName配置排除之后,遍历解析配置。EnableAutoConfigurationImportSelector将spring.factories的配置引入到ConfigurationClassPostProcessor进行解析处理,从而达到了自动配置的目的。

3.3 Spring Boot提供的META-INF/spring.factories文件内容:

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.MessageSourceAutoConfiguration,\

org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\

org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\

org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

…...

org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration

4、@Conditional使配置灵活适应各种环境

在解析配置的时候,如果发现配置有注解@Conditional,则会根据注解配置的Condition来决定是否加载。

@Conditional在配置的时候需要提供一组org.springframework.context.annotation.Condition接口的实现类,只有所有的Condition返回true才会处理。

public interface Condition {

        boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

4.1 在代码中使用@Conditional注解 

@Component

@Conditional({ConditionImplA.class})

public class SSExceptionTranslationFilterConfigure {

        @Conditional({ConditionImplB.class})

        @Bean

        public SSExceptionTranslationFilter exceptionTranslationFilter{

                    ...

        }

        ...

}

在Spring Boot中一般不直接使用@Conditional,而是使用基于@Conditional经过进一步的封装,功能更强大的实现。比如:

@ConditionalOnClass                

        必须满足所有指定的class都存在才解析

@ConditionalOnProperty             

        指定的配置信息必须满足才解析

@ConditionalOnExpression           

        通过Spring表达式返回结果来决定是否解析

@ConditionalOnWebApplication       

        是Web应用才解析

@ConditionalOnMissingBean          

        指定的bean不存在才解析

…...

4.2 分析一个条件注解的实现,比如@ConditionalOnMissingBean

@Target({ ElementType.TYPE, ElementType.METHOD })

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Conditional(OnBeanCondition.class)

public @interface ConditionalOnMissingBean {

        Class<?>[] value()         

                 //指定需要检查的bean的class类型, 比如{String.clas, Filter.class}

        String[] type()                

                 //指定需要检查的bean的类型, 比如{“java.lang.String”, “javax.servlet.Filter”}

        Class<?>[] ignored()      

                 //在检查的时候需要忽略的class类型. 比如 {String.class}

        String[] ignoredType()    

                 //作用同上, 比如{“java.lang.String”, “javax.servlet.Filter”}

        Class<? extends Annotation>[] annotation()    

                 //在检查的时候,需要所有的bean都不能有指定的注解

        String[] name()                                                   

                 //根据bean的name来进行检查

        SearchStrategy search() default SearchStrategy.ALL  

                 //在检查的时候,扫描BeanFactory的范围,默认为ALL.

}

SearchStrategy枚举:

CURRENT          只扫描当前的BeanFactory

PARENTS          只扫描父BeanFactory

ALL             作用等于CURRENT+PARENTS


从配置参数可以看出所有的参数都一个目的:限定Bean的范围。比如根据类型,根据名称,扫描BeanFactory的范围。 

其功能实现主要是依赖于其的注解@Conditional(OnBeanCondition.class)。OnBeanCondition的实现逻辑为:根据配置的范围参数依次从BeanFactory获取bean的names(此处只是获取name,并没有去获取bean),所有范围内都找不到指定的bean,则返回true,否则返回false。


填坑小提示:

Condition的实现中是可以获取到BeanFactory的,进一步当然可以从BeanFactory中获取到bean。但是Condition是作用于启动流程中的第5步。此时如果将bean从BeanFactory中取出,会缺少第6步中BeanPostProcessor对@Autowired,@PostConstruct等注解的处理,导致bean的初始化不完全。


5、@EnableAutoConfiguration与@Conditional

@EnableAutoConfiguration自动加载配置,@Conditional根据环境决定是否解析处理配置。这两个注解的配合完成了自动化配置功能。

5.1 WebMvcAutoConfiguration代码示例

根据代码来看一下是怎么配合完成自动化配置的。比如Spring Boot为SpringMVC提供了一个自动化配置类:org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration。源代码如下:

@Configuration

@ConditionalOnWebApplication

@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,

                    WebMvcConfigurerAdapter.class })

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)

@AutoConfigureAfter(DispatcherServletAutoConfiguration.class)

public class WebMvcAutoConfiguration {

        …...

        @Bean

        @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)

        public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {

                    return new OrderedHiddenHttpMethodFilter();

        }

        @Bean

        @ConditionalOnMissingBean(HttpPutFormContentFilter.class)

        public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {

                    return new OrderedHttpPutFormContentFilter();

        }

        …...

}


可以看到这是一个Configuration类,配置实现了 OrderedHiddenHttpMethodFilter,OrderedHttpPutFormContentFilter等bean。同时在Class和Method级别上都有配置@Conditional开头的注解。 


5.2 流程解析:

(1)EnableAutoConfigurationImportSelector加载配置 WebMvcAutoConfiguration。

(2)判断ClassPath中是否同时存在 Servlet.class,DispatcherServlet.class,WebMvcConfigurerAdapter.class,并且BeanFactory能找到 WebMvcConfigurationSupport.class类型的bean。如果满足继续解析EnableAutoConfigurationImportSelector,否则跳过解析该配置。

(3)判断BeanFactory中是否已经存在 HiddenHttpMethodFilter.class类型的bean,如果不存在则创建OrderedHiddenHttpMethodFilter。

(4)判断BeanFactory中是否已经存在 HiddenHttpMethodFilter.class类型的bean,如果不存在则创建OrderedHttpPutFormContentFilter。


在Spring Boot中怎么自定义自动配置?

1、需要提供jar包,在jar包中需要包含META-INF/spring.factories文件。

2、在spring.factories中添加配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration =

com.dianrong.xxxx.xxxConfiguration

3、Configuration的实现需要添加注解@Configuration。

4、可以选择添加@Conditional来适应不同的环境。

有了SpringBoot的自动化配置,我们可以灵活的自定义我们自己的自动配置。当应用需要该功能时,只需要简单的依赖该jar包即可。同时Spring Boot为我们提供的条件注解,同样的代码可以灵活适应各种环境。





相关文章
相关标签/搜索