Springboot的自动装配解读

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

目录

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

1.Springboot的自动装配

1.1 组件装配

1.1.1 组件

1.2 Spring Framework 的模块装配

1.2.1 @Import注解

1.2.2 BeanDefinition 

1.3 Spring Framework 的条件装配

1.3.1 @Profile

1.3.2 @Conditional 

1.3.3 MetaData元数据接口(补充)

AnnotatedTypeMetadata

AnnotationMetadata

MethodMetadata

 1.4 SPI机制

1.5 Springboot 的装配机制


 

1.Springboot的自动装配

1.1 组件装配

Spring Framework本身有一个IOC容器,该容器会统一管理其中的Bean对象,Bean对象可以理解为组件,要理解组件装配,首先要理解组件的概念。

1.1.1 组件

基于Spring Framework 的应用在整合第三方技术时,要把第三方框架中的核心API配置到Spring Framework的配置文件或注解配置类中,以供Spring Framework统一管理。此处的配置是关键,通过编写XML配置文件或注解配置类,将第三方框架中的核心API以对象的形式注册到IOC容器中。这些核心API对象会在适当的位置发挥其作用,以支撑项目的正常运行。IOC容器中的核心API对象本身就是一个个的bean对象,即组件;将核心API配置到XML配置文件或注解配置类的行为称为组件装配。

Spring Framework本身只有-一种组件装配方式,即手动装配,而Spring Boot基于原生的手动装配,通过模块装配+条件装配+SPI机制,可以完美实现组件的自动装配。手动装配指的是开发者在项目中通过编写XML配置文件、注解配置类、配合特定等方式将所需的组件注册到IOC容器中, 即ApplicationContext中。Springboot中的核心特性是组件的自动装配,自动装配的核心是,本应该由开发者编写的配置,转为框架自动根据项目中整合的场景依赖,合理地做出判断并装配合适的Bean到IOC容器中。

1.2 Spring Framework 的模块装配

1.2.1 @Import注解

可以参考 Springboot 核心注解和基本配置解读_山河亦问安的博客-CSDN博客

1.2.2 BeanDefinition 

BeanDefinition 是定义 Bean 的配置元信息接口,这里举例通过BeanDefinition来进行Bean对象的创建。代码如下:

   DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class).addPropertyValue("username", "admin");
        defaultListableBeanFactory.registerBeanDefinition("user01",beanDefinitionBuilder.getBeanDefinition());
        User user01 = defaultListableBeanFactory.getBean("user01", User.class);
        System.out.println(user01);

        RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class);
        //构造参数
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, "root");
        beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
        defaultListableBeanFactory.registerBeanDefinition("user02", beanDefinition);
        User user2 = defaultListableBeanFactory.getBean("user02", User.class);
        System.out.println(user2);

1.3 Spring Framework 的条件装配

1.3.1 @Profile

@Profile 注解可以标注在组件上,当一个配置属性激活的时候,它才会起作用。Profile提供了一种基于环境的配置,根据当前项目的不同运行的环境,可以动态的注册与当前运行环境匹配的组件。

使用@Profile:

@Profile("test")
public class User {
    private String username;
}

为ApplicationContext 设置Profile

 AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.getEnvironment().setActiveProfiles("test");
        annotationConfigApplicationContext.register(User.class);
        annotationConfigApplicationContext.refresh();
        User bean = annotationConfigApplicationContext.getBean(User.class);
        System.out.println(bean);

AnnotationConfigApplicationContext 在创建对象的时候,如果直接传入了配置类,则会立即初始化IOC容器,在不传入配置类的情况下,内部不会执行初始化逻辑,而是要等到手动调用其refresh方法后才会初始化IOC容器,在初始化的过程中会顺便将环境配置一并处理。

1.3.2 @Conditional 

 可以参考 Springboot 核心注解和基本配置解读_山河亦问安的博客-CSDN博客

 判断是否存在的条件判断类:

public class MyConditional implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getBeanFactory().containsBeanDefinition(Admin.class.getName());
    }
}

上面的matches方法中使用BeanDefinition而不是Bean做判断,这是因为考虑的是当条件匹配时Admin对象可能尚未创建,导致条件匹配出现偏差。

1.3.3 MetaData元数据接口(补充)

元数据是关于数据的数据,元数据是数据的描述和上下文,它有助于组织,查找,理解和使用数据。

Springboot的自动装配解读

AnnotatedTypeMetadata

这个接口表示的是注解元素(AnnotatedElement)的元数据,只要能在上面标注注解的元素都属于注解元素。

public interface AnnotatedTypeMetadata {

    // 此元素是否标注有此注解,annotationName:注解全类名
    boolean isAnnotated(String annotationName);

    //取得指定类型注解的所有的属性 - 值(k-v)
    // annotationName:注解全类名
    // classValuesAsString:若是true表示 Class用它的字符串的全类名来表示。这样可以避免Class被提前加载
    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName);
    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);

}

AnnotationMetadata

它是ClassMetadataAnnotatedTypeMetadata的子接口,具有两者共同能力,并且新增了访问注解的相关方法。可以简单理解为它是对注解的抽象。

public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
 
	//拿到当前类上所有的注解的全类名(注意是全类名)
	Set<String> getAnnotationTypes();
	// 拿到指定的注解类型
	//annotationName:注解类型的全类名
	Set<String> getMetaAnnotationTypes(String annotationName);
	
	// 是否包含指定注解 (annotationName:全类名)
	boolean hasAnnotation(String annotationName);
	//这个厉害了,用于判断注解类型自己是否被某个元注解类型所标注
	//依赖于AnnotatedElementUtils#hasMetaAnnotationTypes
	boolean hasMetaAnnotation(String metaAnnotationName);
	
	// 类里面只有有一个方法标注有指定注解,就返回true
	//getDeclaredMethods获得所有方法, AnnotatedElementUtils.isAnnotated是否标注有指定注解
	boolean hasAnnotatedMethods(String annotationName);
	// 返回所有的标注有指定注解的方法元信息。注意返回的是MethodMetadata 原理基本同上
	Set<MethodMetadata> getAnnotatedMethods(String annotationName);
}

MethodMetadata

public interface MethodMetadata extends AnnotatedTypeMetadata {
    //方法名
	String getMethodName();
    //定义方法的类全限定名
	String getDeclaringClassName();
    //返回的值全限定名
	String getReturnTypeName();
}

MetadataReader  

public interface MetadataReader {

    /**
     * 返回class文件的IO资源引用
     */
    Resource getResource();

    /**
     * 为基础class读取基本类元数据,返回基础类的元数据。
     */
    ClassMetadata getClassMetadata();

    /**
     *为基础类读取完整的注释元数据,包括注释方法的元数据。返回基础类的完整注释元数据
     */
    AnnotationMetadata getAnnotationMetadata();
}

 MetadataReaderFactory

MetadataReaderFactory接口 ,MetadataReader的工厂接口。允许缓存每个MetadataReader的元数据集。

public interface MetadataReaderFactory {
    /**
     * 根据class名称创建MetadataReader
     */
    MetadataReader getMetadataReader(String className) throws IOException;

    /**
     * 根据class的Resource创建MetadataReader
     */
    MetadataReader getMetadataReader(Resource resource) throws IOException;

}

 

 1.4 SPI机制

Spring中的SPI相较于JDK原生的SPI更加的高级实用,因为它不仅限于接口或者抽象类,可以是任何一个类,接口和注解。在Springboot中大量用到SPI机制加载自动配置类和特殊组件。

声明SPI文件

将SPI文件放在项目的META-INF目录下,且文件名必须为spring.factories。代码如下:

com.example.demo.entity.User=\
com.example.demo.entity.User

spring.factories中被检索的类,接口和注解的全限定名作为key,具体要检索的类的全限定名作为value,多个类之间用逗号进行分割。

测试获取

  List<User> users = SpringFactoriesLoader.loadFactories(User.class, Demo2Application.class.getClassLoader());
        for (User user : users) {
            System.out.println(user);
        }

1.5 Springboot 的装配机制

Springboot中的自动装配其实就是模块装配+条件装配+SPI机制的组合使用

@SpringbootApplication的组成可以参考  Springboot 核心注解和基本配置解读_山河亦问安的博客-CSDN博客

这里主要讲解@EnableAutoConfiguration,点开@EnableAutoConfiguration注解

@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注解本身组合了一个@Import注解

@Import({AutoConfigurationPackages.Registrar.class})

@AutoConfiguration注解所做的是将主启动类所在的包记录下来,注册到AutoConfigurationPackages中。在Springboot 2.3.0版本后,注解中多了两个属性,可以手动指定应用的根包路径,如下:

 String[] basePackages() default {};

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


简单介绍了@AutoConfigurationPackage 注解本身,下面把目标转移到导人的内部类
AutoConfigurationPackages.Registrar.class上。 Registrar本身是一ImportBeanDefinitionRegistrar,它的作用是以编程式向IOC 容器中注册 bean对象,而Registrar 要注册的对象实际上是默认主启动类所在的包路径(也就是@AutoConfiqurationPackage注解要记录的根包)。Registrar代码如下:

 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }
    }

 debug如下:

Springboot的自动装配解读
 
注意register方法的第二个参数,它利用PackageImports导出了一组包名.而包名的来源是一个AnnotationMetadata,这个AnnotationMetadata本质上可以理解为AutoConfiaurationPackage 注解本身。换句话说,这个 PackageImports类提取出@AutoConfigurationPackage 注解中定义的两个属性( baserackages与basePackageClasses)。实际上源码也是如此,它做了一个约定大于配置的设计,PackageImports的构造方法中会先提取注解中的这两个属性,如果两个属性都没有定义会提取主启动类所在的包名。

@Import({AutoConfigurationImportSelector.class}):

将AutoConfigurationImportSelector这个类导入到spring容器中,AutoConfigurationImportSelector可以帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IOC容器(ApplicationContext)中。主干逻辑只有三步:加载自动配置类,移除被去掉的自动配置类,封装Entry返回。这里加载自动配置类的动作就是利用 Spring Framework 的 SPI机制.从spring.factories中提取出所有@EnableAutoConfiguration对应的配置值。可以通过三种途径移除被去掉的自动配置类:@springBootApplication或@EnableAutoConfiguration 注解的exclude、excludeName属性,以及全局配置文件的spring.autoconfigure.exclude 属性配置。底层源码会提取出这三个位置配置的自动配置类并移除。代码如下:

 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            //加载注解属性配置
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //加载自动配置类
            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 AutoConfigurationEntry(configurations, exclusions);
        }
    }

springboot底层实现自动配置的步骤是:
1.@SpringBootApplication起作用;
2.@EnableAutoConfiguration;
3.@AutoConfigurationPackage:这个组合注解主要是@Import(AutoConfigurationPackages.Registrar.class),它通过将Registrar类导入到容器中,而Registrar类作用是扫描主配置类同级目录以及子包,并将相应的组件导入到springboot创建管理的容器中;
4.@Import(AutoConfigurationImportSelector.class):它通过将AutoConfigurationImportSelector类导入到容器中,AutoConfigurationImportSelector类作用是通过selectImports方法实现将配置类信息交给SpringFactory加载器进行一系列的容器创建过程。

 

 

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

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

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

相关文章

  • springBoot自动装配机制

    自动配置原理 @SpringBootApplication 是一个组合注解,由 @ComponentScan、@EnableAutoConfiguration 和 @SpringBootConfiguration 组成 @SpringBootConfiguration 与普通 @Configuration 相比,唯一区别是前者要求整个 app 中只出现一次 @ComponentScan excludeFilters - 用来在组件扫描时进行排除,也会排除自动配置类

    2024年02月08日
    浏览(48)
  • SpringBoot源码-自动装配

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

    2024年02月08日
    浏览(58)
  • springboot自动装配大概原理

    自动装配 : pom.xml spring-boot-dependence:核心都依赖在父类工程中! 我们在写入或者引入springboot依赖的时候,不需要指定版,因为有这些仓库的版本 启动器:------spring boot的启动场景 比如spring-boot-starter-web,他就会帮我们导入web环境苏需要的依赖。 springboot会将所有的功能场景

    2023年04月25日
    浏览(37)
  • springBoot 自动装配

    1.前言 自动装配则是 SpringBoot 的核心,自动装配是如何实现的呢?为什么我们只要引入一个 starter 组件依赖就能实现自动装配呢,接下来就让我们一起来探讨下 SpringBoot 的自动装配机制 2.自动装配原理 提到自动装配,那么你首先得知道JDK的SPI(serviceproviderinterface)机制,因为

    2024年02月09日
    浏览(43)
  • SpringBoot自动装配原理及分析

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

    2024年01月21日
    浏览(42)
  • springboot启动流程 (3) 自动装配

    在SpringBoot中,EnableAutoConfiguration注解用于开启自动装配功能。 本文将详细分析该注解的工作流程。 启用SpringBoot自动装配功能,尝试猜测和配置可能需要的组件Bean。 自动装配类通常是根据类路径和定义的Bean来应用的。例如,如果类路径上有tomcat-embedded.jar,那么可能需要一个

    2024年02月09日
    浏览(47)
  • 一文足够,SpringBoot自动装配底层源码

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

    2023年04月13日
    浏览(42)
  • 【Spring】深究SpringBoot自动装配原理

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

    2024年02月14日
    浏览(42)
  • SpringBoot@Autowired自动装配失败解决方法

    今天写SpringBoot项目的时候,使用@Autowired注解idea报错了,那就记录一下怎么解决的吧。 打开idea的setting(设置)然后搜索inspection(检查) 然后找到Spring下的Spring Core的Code(代码)找到Autowired for bean class( Spring bean 注入点的自动装配问题),然后改成warning就可以了

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

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

    2023年04月13日
    浏览(41)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包