经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
Spring Boot 自动配置原理
来源:cnblogs  作者:雪漫士兵  时间:2019/2/18 9:36:30  对本文有异议

注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别。

前言

关于配置文件可以配置的内容,在 Spring Boot 官方网站已经提供了完整了配置示例和解释。

可以这么说,Spring Boot 的一大精髓就是自动配置,为开发省去了大量的配置时间,可以更快的融入业务逻辑的开发,那么自动配置是怎么实现的呢?

1. @SpringBootApplication

跟着 Spring Boot 的启动类的注解 @SpringBootApplication 进行源码跟踪,寻找自动配置的原理。

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @SpringBootConfiguration
  6. @EnableAutoConfiguration
  7. @ComponentScan(
  8.     excludeFilters = {@Filter(
  9.     type = FilterType.CUSTOM,
  10.     classes = {TypeExcludeFilter.class}
  11. ), @Filter(
  12.     type = FilterType.CUSTOM,
  13.     classes = {AutoConfigurationExcludeFilter.class}
  14. )}
  15. )
  16. public @interface SpringBootApplication {

@EnableAutoConfiguration 开启自动配置。

@ComponentScan 开启注解扫描

SpringBootApplication 我们可以发现,这是一个简便的注解配置,它包含了自动配置,配置类,包扫描等一系列功能。

2. @EnableAutoConfiguration

继续跟踪,查看@EnableAutoConfiguration 源码,里面比较重要的是 @Import ,导入了一个翻译名为自动配置的选择器的类。这个类其实就是自动配置的加载选择器。

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. @Import({AutoConfigurationImportSelector.class})
  7. public @interface EnableAutoConfiguration {
  8.     String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  9.  
  10.     Class<?>[] exclude() default {};
  11.  
  12.     String[] excludeName() default {};
  13. }

继续跟踪 AutoConfigurationImportSelector.class .在这个类有一个重要的方法 getCandidateConfigurations.用于加载 Spring Boot 配置的自动配置类。

getAutoConfigurationEntry 会筛选出有效的自动配置类。

  1. protected AutoConfigurationEntry getAutoConfigurationEntry(
  2.             AutoConfigurationMetadata autoConfigurationMetadata,
  3.             AnnotationMetadata annotationMetadata) {
  4.         if (!isEnabled(annotationMetadata)) {
  5.             return EMPTY_ENTRY;
  6.         }
  7.         AnnotationAttributes attributes = getAttributes(annotationMetadata);
  8.         List<String> configurations = getCandidateConfigurations(annotationMetadata,
  9.                 attributes);
  10.         configurations = removeDuplicates(configurations);
  11.         Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  12.         checkExcludedClasses(configurations, exclusions);
  13.         configurations.removeAll(exclusions);
  14.         configurations = filter(configurations, autoConfigurationMetadata);
  15.         fireAutoConfigurationImportEvents(configurations, exclusions);
  16.         return new AutoConfigurationEntry(configurations, exclusions);
  17.     }   
  18.  
  19. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
  20.             AnnotationAttributes attributes) {
  21.         List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
  22.                 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
  23.         Assert.notEmpty(configurations,
  24.                 "No auto configuration classes found in META-INF/spring.factories. If you "
  25.                         + "are using a custom packaging, make sure that file is correct.");
  26.         return configurations;
  27.     }

下图是 DEBUG 模式下筛选之后的结果,因为我只添加了 web 模块,所以只有 web 相关的自动配置。

筛选过后的自动配置

3. xxxAutoConfiguration 与 xxxProperties

在上面的 debug 里,我们看到了成功加载的自动配置,目前只看到了配置类,却还没有发现自动配置值,随便选择一个 AutoConfiguration 查看源码。

这里选择了 ServletWebServerFactoryAutoConfiguration.

  1. @Configuration
  2. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  3. //判断当前项目有没有这个类
  4. //CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
  5. @ConditionalOnClass(ServletRequest.class)
  6. //Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果
  7. //满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
  8. @ConditionalOnWebApplication(type = Type.SERVLET)
  9. @EnableConfigurationProperties(ServerProperties.class)
  10. @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
  11.         ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
  12.         ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
  13.         ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
  14. public class ServletWebServerFactoryAutoConfiguration {

需要注意的是 @EnableConfigurationProperties(ServerProperties.class).他的意思是启动指定类的
ConfigurationProperties功能;将配置文件中对应的值和 ServerProperties 绑定起来;并把
ServerProperties 加入到 IOC 容器中。

再来看一下 ServerProperties .

  1. @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
  2. public class ServerProperties {
  3.  
  4.     /**
  5.      * Server HTTP port.
  6.      */
  7.     private Integer port;

显而易见了,这里使用 ConfigurationProperties 绑定属性映射文件中的 server 开头的属性。结合默认配置

  1. # 路径spring-boot-autoconfigure-2.1.1.RELEASE.jar
  2. # /META-INF/spring-configuration-metadata.json
  3.  
  4.     {
  5.       "name": "server.port",
  6.       "type": "java.lang.Integer",
  7.       "description": "Server HTTP port.",
  8.       "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties",
  9.       "defaultValue": 8080
  10.     }

达到了自动配置的目的。

4. 自动配置总结

  1. SpringBoot 启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration 。

  2. @EnableAutoConfiguration 给容器导入META-INF/spring.factories 里定义的自动配置类。

  3. 筛选有效的自动配置类。

  4. 每一个自动配置类结合对应的 xxxProperties.java 读取配置文件进行自动配置功能 。

5. 配置类

通过自动配置,我们发现已经帮我们省去了大量的配置文件的编写,那么在自定义配置的时候,我们是不是需要编写XML呢?Spring boot 尽管可以使用 SpringApplicationXML 文件进行配置,但是我们通常会使用 @Configuration 类进行代替,这也是官方推荐的方式。

5.1 XML配置

定义 helloService Bean.

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5.  
  6.     <bean id="helloService" class="net.codingme.boot.service.HelloService"></bean>
  7.  
  8. </beans>

引入配置。

  1. @ImportResource(value = "classpath:spring-service.xml")
  2. @SpringBootApplication
  3. public class BootApplication {
  4.  
  5.     public static void main(String[] args) {
  6.         SpringApplication.run(BootApplication.class, args);
  7.     }
  8. }

5.2 注解配置

此种方式和上面的XML配置是等效的,也是官方推荐的方式。@Configuration 注解的类(要在扫描的包路径中)会被扫描到。

  1. /**
  2.  * <p>
  3.  * 配置类,相当于传统Spring 开发中的 xml-> bean的配置
  4.  *
  5.  * @Author niujinpeng
  6.  * @Date 2018/12/7 0:04
  7.  */
  8. @Configuration
  9. public class ServiceConfig {
  10.  
  11.     /**
  12.      * 默认添加到容器中的 ID 为方法名(helloService)
  13.      *
  14.      * @return
  15.      */
  16.     @Bean
  17.     public HelloService helloService() {
  18.         return new HelloService();
  19.     }
  20. }

6. 附录

@Conditional扩展注解作用(判断是否满足当前指定条件)
@ConditionalOnJava系统的java版本是否符合要求
@ConditionalOnBean容器中存在指定Bean;
@ConditionalOnMissingBean容器中不存在指定Bean;
@ConditionalOnExpression满足SpEL表达式指定
@ConditionalOnClass系统中有指定的类
@ConditionalOnMissingClass系统中没有指定的类
@ConditionalOnSingleCandidate容器中只有一个指定的Bean,或者这个Bean是首选Bean
@ConditionalOnProperty系统中指定的属性是否有指定的值
@ConditionalOnResource类路径下是否存在指定资源文件
@ConditionalOnWebApplication当前是web环境
@ConditionalOnNotWebApplication当前不是web环境
@ConditionalOnJndiJNDI存在指定项

文章代码已经上传到 GitHub Spring Boot 自动配置

<完>
本文原发于个人博客:https://www.codingme.net 转载请注明出处
原文链接:http://www.cnblogs.com/niumoo/p/10393547.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号