Spring Boot自动配置源码解析(基于Spring Boot 2.0.2.RELEASE)

  在Spring Boot官方介绍中,首一段话是这样的(如下图)。我们可以大概了解到其所表达的含义:我们可以利用Spring Boot写很少的配置来创建一个非常方便的基于Spring整合第三方类库的单体企业级应用。相信使用过Spring Boot的人都知道,她在这方面从前到后的一系列整合。本篇文字将带你进入具体的实现细节。

  首先我们写一段Spring Boot应用启动类的代码如下:

 1 package com.springTest;
 2
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5
 6 @SpringBootApplication //定义Spring Boot应用启动类
 7 public class DemoApplication {
 8
 9     public static void main(String[] args) {
10         SpringApplication.run(DemoApplication.class, args);//启动spring容器等处理
11     }
12 }

 跟人@SpringBootApplication注解实现我们可以看到它的源码:

 1 @Target(ElementType.TYPE)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Documented
 4 @Inherited
 5 @SpringBootConfiguration
 6 @EnableAutoConfiguration //自动配置关键位置
 7 @ComponentScan(excludeFilters = {
 8         @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
 9         @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
10 public @interface SpringBootApplication {

  通过名称我们就可以注意到第6行@EnableAutoConfiguration 自动配置,点进去看它的源码:

1 @Target(ElementType.TYPE)
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 @Inherited
5 @AutoConfigurationPackage //自动扫描包路径配置
6 @Import(AutoConfigurationImportSelector.class) //自动配置导入
7 public @interface EnableAutoConfiguration {

  上面的代码我们可以看到EnableAutoConfiguration利用@Import导入配置组件。 跟踪源码其org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports方法返回的是StringUtils.toStringArray(configurations)。

具体方法代码如下:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); //获取配置类的集合
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }

  上面代码,我们是通过第8行代码获取所需要的配置类的集合,所以我们继续跟代码,找到如下代码信息:

1 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

   上面的代码通过传入getSpringFactoriesLoaderFactoryClass()和 getBeanClassLoader()的返回值获取配置信息集合。通过跟踪代码,我们可以知道getSpringFactoriesLoaderFactoryClass()方法返回的值为固定的EnableAutoConfiguration.class。 getBeanClassLoader()方法则返回的是this.beanClassLoader对象。深入loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader)方法得知,其返回值的代码为:

return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());

  这段代码明显是将loadSpringFactories(classLoader)返回的集合进行过滤获取key为“EnableAutoConfiguration”的List类型的value值。那么我们继续检查loadSpringFactories的源码:

 1 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
 2         MultiValueMap<String, String> result = cache.get(classLoader);
 3         if (result != null) {
 4             return result;
 5         }
 6
 7         try {
 8             Enumeration<URL> urls = (classLoader != null ?
 9                     classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
10                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); // 找到关于FACTORIES_RESOURCE_LOCATION的定义:public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
11             result = new LinkedMultiValueMap<>();
12             while (urls.hasMoreElements()) {
13                 URL url = urls.nextElement();
14                 UrlResource resource = new UrlResource(url);
15                 Properties properties = PropertiesLoaderUtils.loadProperties(resource);
16                 for (Map.Entry<?, ?> entry : properties.entrySet()) {
17                     List<String> factoryClassNames = Arrays.asList(
18                             StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
19                     result.addAll((String) entry.getKey(), factoryClassNames);
20                 }
21             }
22             cache.put(classLoader, result);
23             return result;
24         }
25         catch (IOException ex) {
26             throw new IllegalArgumentException("Unable to load factories from location [" +
27                     FACTORIES_RESOURCE_LOCATION + "]", ex);
28         }
29     }

  上面的代码我们能够很容易的看出它所实现的功能为:加载所有jar包中META-INF/spring.factories文件并解析返回其数据集。到这里,我们不得不对META-INF/spring.factories这个文件产生浓厚的兴趣。通过对Spring Boot所引入的jar包的查找中我们找到了这么一个配置文件如下图(注意箭头指示的内容):

  这不正是我们取到多有META-INF/spring.factories文件中定义的数据之后过滤出来所需要的内容吗?忍着激动,我们继续进行分析。

  我们应该很容易的猜想到这里面定义的全类名对应的内容应该跟我们所要找的自动配置有关系,所以我们随便先找一个Configuration来验证一下我们的猜想。以org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration为例,我们分析一下它的底层实现:

 1 @Configuration //Spring配置文件注解,表明这是一个配置类
 2 @ConditionalOnClass(CacheManager.class) //底层实现为@Condition 代表CacheManager.class这个类必须存在,否则这个配置文件配置的类将不生效
 3 @ConditionalOnBean(CacheAspectSupport.class)
 4 @ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
 5 @EnableConfigurationProperties(CacheProperties.class) //对应配置文件的配置
 6 @AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
 7 @AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
 8         RedisAutoConfiguration.class })
 9 @Import(CacheConfigurationImportSelector.class) //导入种类的缓存配置
10 public class CacheAutoConfiguration {
11
12     @Bean
13     @ConditionalOnMissingBean
14     public CacheManagerCustomizers cacheManagerCustomizers(
15             ObjectProvider<List<CacheManagerCustomizer<?>>> customizers) {
16         return new CacheManagerCustomizers(customizers.getIfAvailable());
17     }
18
19     @Bean
20     public CacheManagerValidator cacheAutoConfigurationValidator(
21             CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
22         return new CacheManagerValidator(cacheProperties, cacheManager);
23     }
24
25     @Configuration
26     @ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
27     @ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
28     protected static class CacheManagerJpaDependencyConfiguration
29             extends EntityManagerFactoryDependsOnPostProcessor {
30
31         public CacheManagerJpaDependencyConfiguration() {
32             super("cacheManager");
33         }
34
35     }
36
37     /**
38      * Bean used to validate that a CacheManager exists and provide a more meaningful
39      * exception.
40      */
41     static class CacheManagerValidator implements InitializingBean {
42
43         private final CacheProperties cacheProperties;//自动注入缓存配置
44
45         private final ObjectProvider<CacheManager> cacheManager;
46
47         CacheManagerValidator(CacheProperties cacheProperties,
48                 ObjectProvider<CacheManager> cacheManager) {
49             this.cacheProperties = cacheProperties;
50             this.cacheManager = cacheManager;
51         }
52
53         @Override
54         public void afterPropertiesSet() {
55             Assert.notNull(this.cacheManager.getIfAvailable(),
56                     () -> "No cache manager could "
57                             + "be auto-configured, check your configuration (caching "
58                             + "type is ‘" + this.cacheProperties.getType() + "‘)");
59         }
60
61     }
62
63     /**
64      * {@link ImportSelector} to add {@link CacheType} configuration classes.
65      */
66     static class CacheConfigurationImportSelector implements ImportSelector {
67
68         @Override
69         public String[] selectImports(AnnotationMetadata importingClassMetadata) {
70             CacheType[] types = CacheType.values();
71             String[] imports = new String[types.length];
72             for (int i = 0; i < types.length; i++) {
73                 imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
74             }
75             return imports;
76         }
77
78     }
79
80 }

  再对应CacheProperties.class的的源码实现:

1 @ConfigurationProperties(prefix = "spring.cache") //spring中自动导入配置注解
2 public class CacheProperties {
3
4     /**
5      * Cache type. By default, auto-detected according to the environment.
6      */
7     private CacheType type; //spring.cache.type对应

  其实分析到这里已经差不多通透了,还有一些实现细节由于篇(wo)幅(hao)原(lan)因(le),不便详叙。如果疑问,欢迎留言,我会认真作答。

原文地址:https://www.cnblogs.com/HuaiyinMarquis/p/9078127.html

时间: 2024-08-25 12:50:41

Spring Boot自动配置源码解析(基于Spring Boot 2.0.2.RELEASE)的相关文章

SpringBoot 源码解析 (五)----- Spring Boot的核心能力 - 自动配置源码解析

在上一篇博客中分析了springBoot启动流程,大体的轮廓只是冰山一角.今天就来看一下springBoot的亮点功能:自动化装配功能. 先从@SpringBootApplication开始.在启动流程章节中,我们讲述了SpringBoot2大致的启动步骤,并进行了源码详解.但是在刷新容器这块并未展开,refreshContext(context);简单的一行代码,背后却做了太多事情.所以为了不喧宾夺主,本篇也尽量选取和注解@SpringBootApplication有关的方法讲解. sprin

SPRING源码解析-SPRING 核心-IOC

IoC 和 AOP是Spring的核心, 是Spring系统中其他组件模块和应用开发的基础.透过这两个模块的设计和实现可以了解Spring倡导的对企业应用开发所应秉承的思路: 易用性. POJO开发企业应用, 直接依赖于Java语言,而不是容器和框架. 提升程序的可测试性,提高软件质量. 提供一致性编程模型,面向接口的编程 降低应用的负载和框架的侵入性.IoC和AOP实现. 不作为现有解决方案的替代,而是集成现有. IoC和AOP这两个核心组件,特别是IoC容器,使用户在使用Spring完成PO

【Spring】Spring IOC原理及源码解析之scope=request、session

一.容器 1. 容器 抛出一个议点:BeanFactory是IOC容器,而ApplicationContex则是Spring容器. 什么是容器?Collection和Container这两个单词都有存放什么东西的意思,但是放在程序猿的世界,却注定是千差万别.Collection,集合,存放obj instanceof Class为true的一类对象,重点在于存放:Container,容器,可以存放各种各样的obj,但不仅仅是存放,他被称为容器,更重要的是他能管理存放对象的生命周期和依赖. 容器:

66.源码解析:ButterKnife(7.0.1)

1.使用 2.预备知识: (1)注解 元注解是指注解的注解.包括  @Retention @Target @Document @Inherited四种. 1.1.@Retention: 定义注解的保留策略 @Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含 @Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得, @Rete

spring的WebUtils类源码解析

参考文章:1. http://www.ibm.com/developerworks/cn/java/j-lo-spring-utils1/ 2.spring源码 WebUtils 位于 org.springframework.web.util 包中的 WebUtils 是一个非常好用的工具类,它对很多 Servlet API 提供了易用的代理方法,降低了访问 Servlet API 的复杂度,可以将其看成是常用 Servlet API 方法的门面类. 下面这些方法为访问 HttpServletR

netcore3.0 IConfiguration配置源码解析(三)

前面两篇文章主要讲到netcore的配置以及各种配置源. 本篇主要讲到把配置值转换成C#的实体类,体现在IConfiguration各种扩展方法: public static class ConfigurationBinder { /// <summary> /// Attempts to bind the configuration instance to a new instance of type T. /// If this configuration section has a va

4、Spring Boot 自动配置原理

1.4 Spring Boot 自动配置原理 简介 spring boot自动配置功能可以根据不同情况来决定spring配置应该用哪个,不应该用哪个,举个例子: Spring的JdbcTemplate是不是在Classpath里面?如果是,并且DataSource也存在,就自动配置一个JdbcTemplate的Bean Thymeleaf是不是在Classpath里面?如果是,则自动配置Thymeleaf的模板解析器.视图解析器.模板引擎 那个这个是怎么实现的呢?原因就在于它利用了Spring的

spring与ibatis集成之事务部分源码解析

ibatis是一个非常优秀的半自动ORM框架,相较于许多人认为编写sql和配置字段映射会降低开发效率,我认为在数据库最易成为系统瓶颈的情况下,开发人员必须通过手动编写sql来保证sql执行的高效,并在编写过程中思考表结构的设计是否合理.网上已有许多关于ibatis架构和映射实现原理解析的文章,本文主要讨论ibatis和spring集成后事务管理的实现.在spring和ibatis集成后,有两种方式执行sql:第一种是dao继承spring的SqlMapClientDaoSupport,具体的sq

Feign 系列(05)Spring Cloud OpenFeign 源码解析

Feign 系列(05)Spring Cloud OpenFeign 源码解析 [TOC] Spring Cloud 系列目录(https://www.cnblogs.com/binarylei/p/11563952.html#feign) 在 上一篇 文章中我们分析 Feign 参数解析的整个流程,Feign 原生已经支持 Feign.JAX-RS 1/2 声明式规范,本文着重关注 Spring Cloud 是如果整合 OpenFeign 的,使之支持 Spring MVC? 1. Sprin