SpringBoot源码-自动装配

这篇具有很好参考价值的文章主要介绍了SpringBoot源码-自动装配。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、自动装配原理图

SpringBoot源码-自动装配

 文章来源地址https://www.toymoban.com/news/detail-480976.html

二、入口

springboot的核心注解@SpringBootApplication

SpringBoot源码-自动装配

接着看 @SpringBootApplication 注解

截图:

SpringBoot源码-自动装配

代码:

@Target({ElementType.TYPE}) //注解的适用范围,Type表示注解可以描述在类、接口、注解或者枚举中
@Retention(RetentionPolicy.RUNTIME) // 表示注解的生命周期,Runtime运行时
@Documented // 表示注解可以记录在javadoc中
@Inherited // 标识可以被子类继承该注解
//--------------------------------------------------------
@SpringBootConfiguration // 表示该类为配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan(
        excludeFilters = {@ComponentScan.Filter(
                type = FilterType.CUSTOM,
                classes = {TypeExcludeFilter.class}
        ), @ComponentScan.Filter(
                type = FilterType.CUSTOM,
                classes = {AutoConfigurationExcludeFilter.class}
        )}
)
public @interface SpringBootApplication {
}

 接着看红框的注解 @EnableAutoConfiguration

截图:

SpringBoot源码-自动装配

代码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//--------------------------------------------------------
@AutoConfigurationPackage //自动配置包
@Import({AutoConfigurationImportSelector.class}) //Spring的底层注解@Import,给容器中导入一个组件
public @interface EnableAutoConfiguration {

    //@AutoConfigurationPackage 注解的功能是由@Import 注解实现的,它是Spring框架的底层注解,它的作用就是给容器中导入某个组件类。
    //AutoConfigurationImportSelector可以帮助SpringBoot应用将所有符合条件@Configuration配置都加载到当前SpringBoot创建并使用的
    //IOC容器(ApplicationContext)中,AutoConfigurationImportSelector是通过SelectImports这个方法告诉SpringBoot都需要导入那些组件

    //AutoConfigurationImportSelector 组件是实现了 DeferredImportSelector 类,以及很多的 Aware 接口,这些 Aware 接口来实现一些回调方法,
    // 通过这些回调,把 AutoConfigurationImportSelector 的属性进行赋值。


    // 分析下 DeferredImportSelector 这个类
    // 有个内部接口 Group 接口,这个接口里面有两个方法 process() 和 selectImport()
    // 为什么要强度这两个方法,因为这两个方法在 SpringBoot 启动的时候会被调用。
    //跟自动配置逻辑相关的入口方法在 DeferredImportSelectorGrouping 类的 getImport() 方法处,
    // 所以我们就从 getImport() 方法开始入手。 先保留这个疑问,等到剖析run()方法时就会串起来的!!!
    //然后下面来看一下AutoConfigurationImportSelect组件类中的方法是怎么被调用的?


    //我们现在来看一下 DeferredImportSelectorGrouping 这个类:
    // 调用 DeferredImportSelectGrouping 的 getImport() 方法,在这个方法里面又会去调用 group.process() 和 group.selectImports(),
    // 去找这两个方法的具体实现





}

 接着看红框的 AutoConfigurationImportSelector.class 这个类

截图:

SpringBoot源码-自动装配

接着看接口 DeferredImportSelector 的实现

截图:

SpringBoot源码-自动装配

在这个DeferredImportSelector类中,有一个内部接口Group接口,这个接口里面有两个方法 process()selectImport()

接着看下这两个接口的作用,实现类就是上面的 AutoConfigurationImportSelector

2.1 process() 方法实现

截图:

SpringBoot源码-自动装配

 

代码:

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
        Assert.state(deferredImportSelector instanceof org.springframework.boot.autoconfigure.AutoConfigurationImportSelector, () -> {
            return String.format("Only %s implementations are supported, got %s", org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
        });
        // 【1】调用 getAutoConfigurationEntry 方法得到自动配置类放入 AutoConfigurationEntry 对象,
        // AutoConfigurationEntry 中封装有符合条件的自动配置类已经要排除的类
         AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(this.getAutoConfigurationMetadata(), annotationMetadata);
        // 【2】又将封装了自动配置类的 autoConfigurationEntry 对象装进 autoConfigurationEntries 集合中
         this.autoConfigurationEntries.add(autoConfigurationEntry);
        // 【3】遍历刚获取符合条件的自动配置类
         Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

         while(var4.hasNext()) {
             String importClassName = (String)var4.next();
             //这里符合条件的自动配置类作为key,annotationMetadata 作为值加入 entries 集合中
             this.entries.putIfAbsent(importClassName, annotationMetadata);
         }

    }

 接着看  getAutoConfigurationEntry() 方法实现

截图:

SpringBoot源码-自动装配

 

代码:


/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata} 
* of the importing {@link Configuration @Configuration} class.
* 根据导入 @Configuration 类的 AnnotationMetadata 返回  AutoConfigurationEntry
* @param autoConfigurationMetadata the auto-configuration metadata --自动配置元数据
* @param annotationMetadata the annotation metadata of the configuration class -- 配置类的注释元数据
* @return the auto-configurations that should be imported --导入的自动配置
*/ 
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
             //【1】得到 spring.factories 文件配置的所有自动配置类, EnablesAutoConfiguration 的类
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            //注意:虽然能拿到所有 spring.factories 下所有 EnableAutoConfiguration 后缀的配置类,
            //但是每个配置类里面也会有很多的 @Bean 注解的类。同样会进行实例化。
            //那么如果没有用到的话,这些Bean没有必要实例化,所以要进行条件帅选以及剔除

            // 利用 LinkedHashSet 移除重复的配置类
            configurations = this.removeDuplicates(configurations);

            //得到要移除的自动配置类,比如注解属性exclude 的配置类
            //比如:@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
            // 将会获取到 exclude = DataSourceAutoConfiguration.class 的注解数据
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            //检查要被排除的配置类,因为有些不是自动配置类的,故要抛出异常
            this.checkExcludedClasses(configurations, exclusions);
            //【2】将要移除的配置类异常
            configurations.removeAll(exclusions);
            //【3】因为spring.factories 文件获取的自动配置类太多,如果有些不必要的自动配置类都加载进内存,会造成内存浪费,因此这里需要进行过滤
            //这个filter就是关键过滤
            configurations = this.filter(configurations, autoConfigurationMetadata);
            //【4】获取了符合条件的自动配置类后,此时触发 AutoConfigurationImportEvent 事件
            //目的是告知 ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            //【5】将符合条件和要排除的自动配置类封装进 AutoConfiguration 对象并返回
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

 2.1.1 getCandidateConfigurations()  方法实现

接着看红框1方法 getCandidateConfigurations() 

截图:

SpringBoot源码-自动装配

代码:

/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* 返回应该考虑的自动配置类名。默认情况下,此方法将使用带有getSpringFactoriesLoaderFactoryClass()的SpringFactoriesLoad加载候选者
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        //这个方法需要传入两个参数 getSpringFactoriesLoaderFactoryClass
        //getSpringFactoriesLoaderFactoryClass() 这个方法返回的就是 EnableAutoConfiguration.class
        //getBeanClassLoader() 这个方法返回的是 BeanClassLoader(类加载器)
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

 接着看红框 loadFactoryNames() 方法实现

截图:

SpringBoot源码-自动装配

代码:

/**
	 * Load the fully qualified class names of factory implementations of the
	 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
	 * class loader.
     * 使用给定的类加载器从“META-INF/spring.factors”加载给定类型的工厂实现的完全限定类名
	 * @param factoryType the interface or abstract class representing the factory -- 表示工厂的接口或抽象类
	 * @param classLoader the ClassLoader to use for loading resources; can be
	 * {@code null} to use the default -- 用于加载资源的 ClassLoader;可以是null以使用默认值
	 * @throws IllegalArgumentException if an error occurs while loading factory names--如果是加载工厂名称时发生异常
	 * @see #loadFactories
	 */
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

    // SpringFactoriesLoader 会加载所有jar包下的 META-INF/spring.factories
    //这样子的话就会把所有spring.factories中的自动配置类的全限定路径给拿到了,现在回到 getCandidateConfigurations()
    //这个方法,然后对这个List进行帅选以及剔除,接着看filter()的过滤方法
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

 2.1.2  filter() 方法实现

接着看上面红框2 filter() 方法实现

截图:

SpringBoot源码-自动装配

代码:

 private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
        long startTime = System.nanoTime();
        //将从 spring.factories 中获取的自动配置类进行转化,转成字符串数组
        String[] candidates = StringUtils.toStringArray(configurations);
        //定义 skip 数组,是否需要跳过,注意 skip 数组与 candidates 数组顺序一一对应
        boolean[] skip = new boolean[candidates.length];
        boolean skipped = false;

        //getAutoConfigurationImportFilters() 方法拿到 onBeanCondition、onClassCondition、onWebApplicationCondition
        //然后遍历这三个条件类去过滤从spring.factories加载的大量配置类
        Iterator var8 = this.getAutoConfigurationImportFilters().iterator();

        while(var8.hasNext()) {
            AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
            //调用各种aware方法,将 beanClassLoader、beanFactory 等注入到 filter 对象中
            //这里的 filter 对象是 onBeanCondition、onClassCondition 和 onWebApplicationCondition
            this.invokeAwareMethods(filter);
            //判断各种 filter 类与每个 candidate 是否匹配
            //这里实质是通过 candidate(自动配置类) 拿到棘突的 @ConditionOnClass、@ConditionOnBean 和 @ConditionOnWebApplication 里面的注解值
            //注意:candidates 数组与 match 数组一一对应
            boolean[] match = filter.match(candidates, autoConfigurationMetadata);
            //遍历 match 数组,注意 match 顺序跟 candidates 的自动配置类一一对应
            for(int i = 0; i < match.length; ++i) {
                //如果不匹配的话
                if (!match[i]) {
                    //不匹配的话将记录在 skip 数组中,标志 skip[i] = true 也与 candidates 数组一一对应
                    skip[i] = true;
                    //因为不匹配,将相应的自动配置类置空
                    candidates[i] = null;
                    skipped = true;
                }
            }
        }

        if (!skipped) {
            return configurations;
        } else {
            List<String> result = new ArrayList(candidates.length);

            int numberFiltered;
            for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) {
                if (!skip[numberFiltered]) {
                    result.add(candidates[numberFiltered]);
                }
            }

            if (logger.isTraceEnabled()) {
                numberFiltered = configurations.size() - result.size();
                logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
            }

            return new ArrayList(result);
        }
    }

 该方法去重和排除一些不必要的自动配置类。

2.2  selectImports()方法实现

截图:

SpringBoot源码-自动装配代码:

 public Iterable<DeferredImportSelector.Group.Entry> selectImports() {
        if (this.autoConfigurationEntries.isEmpty()) {
            return Collections.emptyList();
        } else {
            // 这里得到所有要排除的自动配置类的set集合
            Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
            //这里得到经过过滤所有符合条件的自动配置类的set集合
            Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
            //移除将要排除的自动配置类
            processedConfigurations.removeAll(allExclusions);
            //对标注有 @Order 注解的自动配置类进行排序
            return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
                return new DeferredImportSelector.Group.Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
            }).collect(Collectors.toList());
        }
    }

 三、创建一个自动配置类

1、创建⼀一个项⽬目,命名为 my-spring-boot-starter,引⼊入 SpringBoot 相关依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

2、编写配置⽂文件

截图:

SpringBoot源码-自动装配

 代码:

@ConfigurationProperties(prefix = "first")
public class MyStarter {

    public String firstStarter;

    public String getFirstStarter() {
        return firstStarter;
    }

    public void setFirstStarter(String firstStarter) {
        this.firstStarter = firstStarter;
    }

}

3. ⾃自动装配

截图:

SpringBoot源码-自动装配代码:

@Configuration
@EnableConfigurationProperties(MyStarter.class)
public class MyStarterPropertiesConfigure {

}

 4. 配置⾃自动类 在 /resources/META-INF/spring.factories ⽂文件中添加⾃自动配置类路路径

截图:

SpringBoot源码-自动装配

 

代码:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xxx.starter.my.MyStarterPropertiesConfigure

5、引⼊入⾃自定义 starter 依赖

截图:

SpringBoot源码-自动装配

 

将 my-spring-boot-starter 模块引入到 spring-parent 中

6、在模块中创建一个 controller 测试下

SpringBoot源码-自动装配

代码:

@RestController
@RequestMapping("/test")
public class MyStarterTestController {


    @Autowired
    private MyStarter myStarter;

    @GetMapping("/myStart")
    public Object getMsg(){
        String firstStarter = myStarter.getFirstStarter();
        return firstStarter;
    }



}

 yml配置:

SpringBoot源码-自动装配

7、访问 

http://localhost:9000/test/myStart

结果如下:

SpringBoot源码-自动装配

 

 

 参考文章:SpringBoot源码深度剖析——@SpringBootApplication注解和new SpringApplication().run()方法深度解密_生活,没那么矫情的博客-CSDN博客

 

 

 

到了这里,关于SpringBoot源码-自动装配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot源码-自动装配

      springboot的核心注解@SpringBootApplication 接着看 @SpringBootApplication 注解 截图: 代码:  接着看红框的注解 @EnableAutoConfiguration 截图: 代码:  接着看红框的 AutoConfigurationImportSelector.class 这个类 截图: 接着看接口 DeferredImportSelector 的实现 截图: 在这个DeferredImportSelector类中,

    2024年02月08日
    浏览(58)
  • 【Spring】深究SpringBoot自动装配原理

    早期的 Spring 项目需要添加需要配置繁琐的 xml ,比如 MVC 、事务、数据库连接等繁琐的配置。 Spring Boot 的出现就无需这些繁琐的配置,因为 Spring Boot 基于 约定大于配置 的理念,在项目启动时候,将约定的配置类自动装配到 IOC 容器里。 这些都因为 Spring Boot 有自动装配的特性

    2024年02月14日
    浏览(42)
  • SpringBoot自动装配原理学习与实战运用

    我们知道SpringBoot就是框架的框架,它解决了Spring在开发过程中繁琐的配置问题。例如在引入web、aop、data、cache等等场景,以往我们使用Spring时,会需要向容器中手动配置DispatchServlet、 AspectJAutoProxyingConfiguration等等配置类,而使用SpringBoot框架后,只需要引入spring-boot-starter-xx

    2023年04月13日
    浏览(41)
  • Spring Boot源码解析 - 自动装配原理

    Spring Boot 自动装配是 Spring Boot 框架的一个关键特性,它的目标是让开发者能够快速构建 Spring 应用程序,减少繁琐的配置工作。   @SpringApplication 从启动类 @SpringApplication 注解入手, @SpringBootApplication 是一个组合注解,它是 Spring Boot 框架中常用的一个主要注解之一。它结合了

    2024年01月19日
    浏览(44)
  • 一文足够,SpringBoot自动装配底层源码

    目录 自动装配原理 开始深入源码 总结自动装配原理 首先明白一个概念,什么是自动装配? 我们在项目中建一个yaml或者properties文件,里面配置一些参数,如redis,在pom中引入启动器,之后就能用redis,自动把这些集成到spring中,这就是自动装配。 先来提前剧透: 加载spring.

    2023年04月13日
    浏览(43)
  • SpringBoot源码解读与原理分析(五)SpringBoot的装配机制

    前面三小节分别介绍了Spring Framewoek的模块装配、条件装配和SPI机制。下面正式进入Spring Boot的装配机制。 实际上, Spring Boot的自动装配是模块装配+条件装配+SPI机制的组合使用 ,而这一切都凝聚在Spring Boot主启动类的@SpringBootApplication注解上。 @SpringBootApplication注解是由三个注

    2024年02月19日
    浏览(50)
  • 拆解Spring boot:Springboot为什么如此丝滑而简单?源码剖析解读自动装配

    🎉🎉欢迎光临,终于等到你啦🎉🎉 🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀 🌟持续更新的专栏 《Spring 狂野之旅:从入门到入魔》 🚀 本专栏带你从Spring入门到入魔   这是苏泽的个人主页可以看到我其他的内容哦👇👇 努力的苏泽 http://suzee.blog.csdn

    2024年03月23日
    浏览(44)
  • spring自动装配原理

    为了搞明白自动装配原理,需要知道spring容器管理bean的生命周期 分为四步: 1、实例化 读取spring配置文件 通过反射进行bean的实例化(eg:通过BeanFactory实例化) 2、属性赋值 解析 自动装配 (byName、byType、constractor、default)DI的体现 循环依赖 3、初始化 调用XXXAware回调方法

    2024年02月02日
    浏览(41)
  • Spring Boot 自动装配原理

    Java面试题目录 Spring Boot自动装配原理   Spring Boot启动类上的 @SpringBootApplication 注解中包含 @EnableAutoConfiguration 注解,表示开启自动装配。在@EnableAutoConfiguration注解中使用@Import注解引入 AutoConfigurationImportSelector 组件,此类中通过 SpringFactoriesLoader.loadFactoryNames() 方法来扫描所有

    2024年01月25日
    浏览(46)
  • SpringBoot原理-自动配置-原理分析-源码跟踪

    SpringBootApplication 该注解标识在SpringBoot项目的启动类上,是SpringBoot中 最为重要 的注解,该注解由三个部分组成。 @SpringBootConfiguration:该注解与@Configuration注解作用一样,用来声明当前类为一个配置类 @ComponentScan:组件扫描,默认扫描当前启动类所在包及其子包 @EnableAutoConf

    2024年02月09日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包