SpringBoot自动装配原理及分析

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

一、什么是自动装配

在使用SpringBoot的时候,会自动将Bean装配到IOC容器中。例如我们在使用Redis数据库的时候,会引入依赖spring-boot-starter-data-redis。在引入这个依赖后,服务初始化的时候,会将操作Redis需要的组件注入到Ioc容器中进行后续使用。
自动装配的大致过程如下:

  • 获取到组件(spring-boot-starter-data-redis)META-INF文件夹下的spring.factories文件
  • spring.factories文件中列出需要注入Ioc容器的类
  • 将实体类注入到Ioc容器中使用

二、自动装配原理源码分析

自动装配原理的大致流程是通过@SpringBootApplication进行实现,这个注解声明在SpringBoot的启动类上。

1.@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 {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

@Target({ElementType.TYPE})等前四个注解JDK中的元注解,用于修饰注解的注解
@Filter用于实现过滤,比如可以将某些类排除在外
@AliasFor注解用于为注解属性声明别名

2.@SpringBootConfiguration

其中@SpringBootConfiguration的本质其实是@Configuration,标识该Java类是一个配置类。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

3.@ComponentScan注解

@ComoponentScan是用来指定扫描路径的,如果不指定特定的扫描路径的话,扫描的路径是当前修饰类所在的包及其子包。

4.@EnableAutoConfiguration注解

@EnableAutoConfiguration这个注解是SpringBoot自动装配的关键。它也有两个注解@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class}).

@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 {};
}

@AutoConfigurationPackage
其中@AutoConfigurationPackage(basePackages =" ")的作用是指定SpringBoot要扫描的包,将包中的Bean注入IOC容器中。该注解内部是一个@Import注解,引入了一个Registrar.class。Registrar类的作用将将主启动类所在包以及子包里的所有组件扫描到Spring容器里。
注意:@AutoConfigurationPakage与@ComponentScan都是将Spring Boot启动类所在的包及其子包里面的组件扫描到IOC容器中,但区别在于@AutoConfigurationPackage还会扫描@Enitity、@Mapper等第三方依赖的注解,@ComponentScan只扫描@Controller/@Service/@Component/@Repository等Spring容器相关的注解。
参考:https://www.jb51.net/program/288508ml1.htm

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

@EnableAutoConfiguration注解最重要的是AutoConfigurationImportSelector.class,将需要装配的类装配到IoC容器中,下面重点分析一下这个类的实现

5.AutoConfigurationImportSelector.class

AutoConfigurationImportSelector实现了ImportSelector接口,那么我们清楚只需要关注selectImports方法的返回结果即可。
AutoConfigurationImportSelector中的selectImports方法是自动装配的核心实现,它主要是读取META-INF/spring.factories文件,经过去重、过滤,返回需要装配的配置类集合

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            // 返回的就是需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组
            // ["com.bobo.pojo.User","com.bobo.pojo.Person", ....]
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

我们清楚了该方法的作用就是要返回需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组。那么我接下来分析的关键是返回的数据是哪来的?所以呢进入getAutoConfigurationEntry方法中。

 protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            // 获取注解的属性信息
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            // 获取候选配置信息 加载的是 当前项目的classpath目录下的 所有的 spring.factories 文件中的 key 为
            // org.springframework.boot.autoconfigure.EnableAutoConfiguration 的信息
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

getAttributes方法:获取@EnableAutoConfiguration中的exclude、excludeName等
getCandidateConfigurations方法:获取所有自动装配的配置类,也就是读取spring.factories文件,后面会再次说明
removeDuplicates方法:去除重复的配置项
getExclusions方法:根据@EnableAutoConfiguration中的exclude、excludeName移除不需要的配置类
fireAutoConfigurationImportEvents方法:广播事件
最后根据多次过滤、判重返回配置类合集
SpringBoot自动装配原理及分析,Spring,spring boot,java,spring

6.getCandidateConfigurations方法

 protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        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方法,扫描classpath下的META-INF/spring.factories文件,里面是以key=value形式存储,读取其中key=EnableAutoConfiguration,value就是需要装配的配置类,也就是getCandidateConfigurations返回的值

7.loadFactoryNames方法

	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		// 此处的类加载器是appClassLoader
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			//如果为空则再次获取
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// 类的全限定名称(即解析properties文件中需要的key值)
		String factoryTypeName = factoryType.getName();
		// 根据类加载器,加载classpath下/META-INF/spring.factories下所有的类名称列表
		// 从结果Map<String, List<String>>中,根据指定类型获取所有实现类名称的集合List<String>
		// 核心
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

  • loadSpringFactories(ClassLoader classLoader)返回的是所有META-INF/spring.factories文件解析完的结果
  • 返回值: 根据指定类名获取结果,没有则返回空列表

8. loadSpringFactories方法

	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		// 根据类加载器去缓存中获取加载好的结果集
		//因为SpringApplication实例化时就加载过一次了,这里就是从缓存中获取到值了
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			//结果不为空,则返回
			return result;
		}
		//已从缓存获取到结果,后面代码省略
		//已从缓存获取到结果,后面代码省略
		//已从缓存获取到结果,后面代码省略
	}

因为SpringApplication实例化时就加载过一次了,所以这里可以从缓存中获取到值。具体原理查看Alian解读SpringBoot 2.6.0 源码(一):SpringApplication对象创建(Spring工厂加载机制)

总结

1)通过注解@SpringBootApplication=>@EnableAutoConfiguration=>@Import({AutoConfigurationImportSelector.class})实现自动装配
2)AutoConfigurationImportSelector类中重写了ImportSelector中selectImports方法,批量返回需要装配的配置类
3)通过Spring提供的SpringFactoriesLoader机制,扫描classpath下的META-INF/spring.factories文件,读取需要自动装配的配置类
4)依据条件筛选的方式,把不符合的配置类移除掉,最终完成自动装配
参考:
链接1
链接2

相关问题:

1.@Component,@Bean,@Import的区别

@Component作用于类,@Bean作用于方法,@Import作用于类

@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。

@Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。@Bean通常用于配置类中声明Bean,配合@Configuration使用。

@Import注解是Java中的元注解,,@Import注解是用于引入其他配置类或Bean的注解。它可以帮助将特定的配置类或Bean注册到Spring容器中,使其可供应用程序使用。
参考:https://blog.csdn.net/Ascend1977/article/details/131391041

2.@Bean一定需要搭配@Configuration才能使用吗?

不需要。Spring允许通过@Bean注解方法来向容器中注册Bean.默认情况下,bean应该是单例的,但是如果我们手动去调用@Bean方法,bean会被实例化多次,这破坏了bean的单例语义。
于是,Spring提供了@Configuration注解,当一个配置类被加上@Configuration注解后,Spring会基于该配置类生成CGLIB代理类,子类会重写@Bean方法,来保证bean是单例的.

参考:https://blog.51cto.com/u_16099278/7039588
https://blog.csdn.net/z69183787/article/details/121979457文章来源地址https://www.toymoban.com/news/detail-810119.html

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

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

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

相关文章

  • 【Spring】深究SpringBoot自动装配原理

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

    2024年02月14日
    浏览(26)
  • 【Spring Boot自动装配原理详解与常见面试题】—— 每天一点小知识

                                                                  💧 S p r i n g B o o t 自动装配原理详解与常见面试题 color{#FF1493}{Spring Boot自动装配原理详解与常见面试题} Sp r in g B oo t 自动装配原理详解与常见面试题 💧           🌷 仰望天空

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

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

    2024年03月23日
    浏览(33)
  • spring boot自动装配及自动装配条件判断

    第一步需要在pom.xml文件指定需要导入的坐标 要是没有自动提示需要检查maven有没有 实现代码 执行代码示例

    2024年02月20日
    浏览(26)
  • Spring Boot自动装配

    自动装配是 Spring Boot 最核心的功能之一,第三方可以基于这个特性非常方便的和 Spring 做整合,实现自己的 Starter,做到开箱即用。 Java 早期并不支持注解,所以那会儿 Spring 只能通过 xml 的形式来配置。早期项目里要引入一个功能模块,首先我们要引入 SDK,然后在 xml 里配置

    2024年01月23日
    浏览(32)
  • 深入了解Spring Boot自动装配

    Spring Boot的自动装配是一项强大的功能,能够简化应用程序的配置和开发过程。让我们通过一系列详细的例子来深入了解这一特性。 在Spring Boot中,自动装配是指框架根据应用程序的依赖关系,自动配置和装配相应的Bean,而无需手动设置。这使得开发者可以更专注于业务逻辑

    2024年01月23日
    浏览(32)
  • Spring Boot如何实现自动装配

    Spring Boot的自动装配是它的一大特点,可以大大提高开发效率,减少重复性代码的编写。本文将详细讲解Spring Boot如何实现自动装配。 在传统的Spring框架中,我们需要手动配置和管理Bean的依赖关系,但在Spring Boot中,大量的配置可以自动完成。这是因为Spring Boot中引入了自动装

    2024年02月04日
    浏览(31)
  • spring自动装配原理

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

    2024年02月02日
    浏览(27)
  • 【Spring Boot 源码学习】自动装配流程源码解析(下)

    《Spring Boot 源码学习系列》 上篇博文,笔者带大家了解了自动装配流程中有关自动配置加载的流程; 本篇将介绍自动装配流程剩余的内容,包含了自动配置组件的排除和过滤、触发自动配置事件。 在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,

    2024年02月11日
    浏览(28)
  • 【Spring Boot 源码学习】自动装配流程源码解析(上)

    《Spring Boot 源码学习系列》 上篇博文,笔者带大家从整体上了解了AutoConfigurationImportSelector 自动装配逻辑的核心功能及流程,由于篇幅有限,更加细化的功能及流程详解还没有介绍。本篇开始将从其源码入手,重点解析细化后的自动装配流程源码。 在开始本篇的内容介绍之前

    2024年02月14日
    浏览(28)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包