0201自动配置类的导入-自动装配原理-springboot2.7.x系列

1简介

Spring Boot是基于Spring框架的,它的原理也是基于Spring框架的。

Spring框架是一个非常强大的框架,它提供了一系列的模块来帮助开发人员构建企业级的应用程序。Spring框架的核心是控制反转(Inversion of Control,IoC)和面向切面编程(Aspect Oriented Programming,AOP)。

Spring Boot是在Spring框架的基础上,提供了自动配置、快速开发和更好的性能等功能,主要原理包括:

  1. 自动配置:Spring Boot基于约定优于配置的原则,提供了大量的自动配置,避免了繁琐的XML配置。
  2. 内嵌式容器:Spring Boot提供了内嵌式的Tomcat、Jetty、Undertow等容器,简化了应用程序的部署和启动。
  3. Starter依赖:Spring Boot提供了一系列的Starter依赖,开发人员可以只添加必要的Starter依赖,而不必担心版本冲突和依赖问题。
  4. 自动装配:Spring Boot的自动装配机制可以根据classpath中的依赖自动配置应用程序。
  5. Actuator:Spring Boot提供了Actuator模块,用于监控应用程序的健康状态、性能指标等。
  6. 外部化配置:Spring Boot可以将配置文件外部化,方便在不同的环境中部署应用程序。

总之,Spring Boot的原理是基于Spring框架的,它提供了一系列的功能来简化应用程序的开发、部署和运行,从而提高开发效率和应用程序的性能。

我们学习springboot步骤:

  • 学习springboot的主要底层原理
    • 自动装配
    • 条件注解Conditional
    • starter机制
  • 手写实现一个简单springboot框架

下面优先讲解下springboot的核心功能:自动装配。

2 springboot 启动

2.1 引入依赖

springboot在项目中依赖引入方式:

  • 方式一:

    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-parent</artifactId>
      <version>2.7.2</version>
    </parent>
    <dependencies>
      <dependy>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-xxx</artifactId>
      </dependy>
    </dependencies>
    
  • 方式二

     <properties>
            <spring-boot.version>2.7.10</spring-boot.version>
    </properties>
     <dependencyManagement>
              <!-- spring boot 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-dependencies</artifactId>
                  <version>${spring-boot.version}</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
    </dependencyManagement>
    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-xxx</artifactId>
            </dependency>
    </dependencies>
              
    

2.2 @SpringBootApplication

引入依赖之后,在启动类上加上@SpringBootApplication注解,它怎么就可以完成那么多jar包的Bean自动配置呢?

我们来看下@SpringBootApplication注解源码:

@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 {
  // 省略...
}

@Target(ElementType.TYPE),@Retention(RetentionPolicy.RUNTIME),@Documented,@Inherited这几个元注解我们不在详述,下面我们重点看下其他3个注解。

2.3 @EnableAutoConfiguration

@SpringBootConfiguration是Spring Boot中的一个注解,它是@Configuration注解的特化版本,用于标识一个类是Spring Boot应用程序的配置类。

在Spring Boot中,配置类通常用于定义应用程序中的各种组件和配置信息,例如数据源、Web服务器、消息队列等。通过@Configuration注解和相关注解,可以将这些组件注入到Spring容器中,从而让它们在应用程序中起作用。

与@Configuration注解不同的是,@SpringBootConfiguration注解还会触发Spring Boot的自动配置机制。它会扫描应用程序中的类路径和类库,找到符合条件的组件并进行自动配置。因此,使用@SpringBootConfiguration注解可以简化应用程序的配置和部署,提高开发效率和可维护性。

2.4 @SpringBootConfiguration

@ComponentScan是Spring Framework中的一个注解,它用于指定要扫描的组件包。在Spring Boot中,@ComponentScan注解通常用于扫描应用程序中的所有组件,并将它们注入到Spring容器中。

通过@ComponentScan注解,可以指定要扫描的包和类,也可以通过excludeFilters和includeFilters属性来排除或包含特定的组件。例如,可以使用excludeFilters属性来排除某些组件,例如某些自动配置类或不需要注入到容器中的类。同时,也可以使用includeFilters属性来只包含特定的组件,例如只包含某些接口的实现类。

在Spring Boot中,@ComponentScan注解通常被应用于启动类或配置类中,用于扫描应用程序中的所有组件。如果没有指定@ComponentScan注解,则默认会扫描启动类所在的包及其子包中的所有组件。

2.5 @EnableAutoConfiguration

@EnableAutoConfiguration是Spring Boot中的一个注解,它用于开启自动配置机制。通过@EnableAutoConfiguration注解,可以让Spring Boot自动根据应用程序中的依赖和配置来完成自动配置,从而简化应用程序的开发和部署

看下@EnableAutoConfigutaiton注解源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	// 省略...
}

2.6 @Import

@Import是Spring Framework中的一个注解,它用于将一个或多个类导入到当前配置类中。通过@Import注解,可以将其他配置类、普通类、甚至是第三方库中的类导入到当前配置类中,从而让它们在Spring容器中起作用。

在Spring Boot中,@Import注解通常用于导入一些自定义的配置类或第三方库中的类。例如,可以使用@Import注解导入一个自定义的配置类,从而将其中定义的Bean注入到Spring容器中。也可以使用@Import注解导入一个第三方库中的类,从而在应用程序中使用该类提供的功能。

自动装配的核心功能,通过AutoConfigurationImportSelector完成。

3 自动配置类的导入流程

下面我们就从启动类的run()方法,通过debugger来追踪下自动装配的流程,我们测试是开源项目pig的gateway模块即网关模块。

第一步:在ApplicationContext#refresh()方法invokeBeanFactoryPostProcessors处设置断点,该方法用于向容器中注册BeanFactory。代码3-1如下所示:

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 省略。。。
			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);
			  // 省略。。。
	}

断点截图3-1如下所示:

在这里插入图片描述

第二步:程序继续执行,忽略中间步骤,我们去看关于AutoConfigurationImportSelector开启自动配置类的导包的选择器中相关代码执行,即执行getAutoConfigurationEntry()方法,代码如下:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   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 = getConfigurationClassFilter().filter(configurations);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

继续追踪下getCandidateConfigurations()获取候选配置类方法,代码如下:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  // 加载jar包下META-INFO/spring.factories下的配置类,兼容springboot2.7.0以前版本
   List<String> configurations = new ArrayList<>(
         SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
  // 加载jar包下META-INFO/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中的类,为springboot2.7.0之后自动配置类存放路径
   ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
   // 省略空校验
   return configurations;
}
  • getSpringFactoriesLoaderFactoryClass()返回:EnableAutoConfiguration.class

追踪下loadFactoryNames()加载jar包下META-INFO/spring.factories下的配置类方法,代码如下:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  ClassLoader classLoaderToUse = classLoader;
  if (classLoaderToUse == null) {
    classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
  }
  String factoryTypeName = factoryType.getName();
  return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  // 缓存获取给定加载器key对应的值
  Map<String, List<String>> result = cache.get(classLoader);
  if (result != null) {
    return result;
  }

  result = new HashMap<>();
  try {
   // 加载META-INFO/spring.factories中的配置
    Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
    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();
        String[] factoryImplementationNames =
            StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
        for (String factoryImplementationName : factoryImplementationNames) {
          result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
              .add(factoryImplementationName.trim());
        }
      }
    }

    // 去重且map值list设置为不可修改
    result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
        .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
    cache.put(classLoader, result);
  }
  // 省略异常处理
  return result;
}
  • FACTORIES_RESOURCE_LOCATION:META-INF/spring.factories
  • 加载jar包下META-INF/spring.factories中配置
  • 其中EnableAutoConfiguration实现类,有的jar包放在spring.factories中,有的单独迁移放置在其他文件中,下面会将。
  • result为HashMap结构,key存放=前的key,值为ArrayList结构,里面存放实现类。
  • loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());获取key为EnableAutoConfiguration(完整包路径)对应的配置类。

我当前springboot版本2.7.10,我们看下spring-boot-autoconfigure-2.7.10.jar包下META-INF/spring.factories中内容

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Environment Post Processors
# 省略

# Auto Configuration Import Listeners
# 省略

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Failure analyzers
# 省略

# Template availability providers
# 省略

# DataSource initializer detectors
# 省略

# Depends on database initialization detectors
# 省略
  • properties格式配置文件,key为父类或者父接口,值为,分隔的实现类

下面我们看下ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);执行

  • ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())加载META-INFO/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中配置类
  • forEach(configurations::add),把这些配置类和之前加载的配置类合并。

ImportCandidates#load()方法:

public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
   Assert.notNull(annotation, "'annotation' must not be null");
   ClassLoader classLoaderToUse = decideClassloader(classLoader);
   // location:META-INF/spring/%s.imports
   String location = String.format(LOCATION, annotation.getName());
   Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
   List<String> importCandidates = new ArrayList<>();
   while (urls.hasMoreElements()) {
      URL url = urls.nextElement();
      importCandidates.addAll(readCandidateConfigurations(url));
   }
   return new ImportCandidates(importCandidates);
}

展示部分org/springframework/boot/spring-boot-autoconfigure/2.7.10/spring-boot-autoconfigure-2.7.10.jar!/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中的配置类:

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
# 省略

到此springboot导入自动配置类流程部分介绍完成,剩下过滤部分,我们会放在下一篇@Conditionalxxx中讲解。

4 小结

  • loadSpringFactories()该方法就是从“META-INF/spring.factories”中加载给定类型的工厂实现的完全限定类名放到map中

  • loadFactoryNames()是根据SpringBoot的启动生命流程,当需要加载自动配置类时,就会传入org.springframework.boot.autoconfigure.EnableAutoConfiguration参数,从map中查找key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,这些值通过反射加到容器中,之后的作用就是用它们来做自动配置,这就是Springboot自动配置开始的地方

  • load():方法从“META-INFO/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports”中加载AutoConfiguration实现的完全限定类名与放入自动配置类map中

  • 只有这些自动配置类进入到容器中以后,接下来这个自动配置类才开始进行启动

  • 当需要其他的配置时如监听相关配置:listenter,就传不同的参数,获取相关的listenter配置

关于

结语

如果小伙伴什么问题或者指教,欢迎交流。

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/springboot-custom

参考:

[1]Springboot视频教程[CP/OL].P13.

[2]一文搞懂🔥SpringBoot自动配置原理[CP/OL].P13.

[3]ChatGPT

[4]spring boot 自动装配的实现原理和骚操作,不同版本实现细节,debug 到裂开…[CP/OL].P13.

[5]SpringBoot 自动装配原理[CP/OL].P13.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot 2.7 版本还没有发布,目前最新的稳定版本是 Spring Boot 2.6.x。自动装配是 Spring Boot 的一个重要特性,它可以根据你的项目依赖和配置自动完成各种组件的配置和注入。在 Spring Boot 中,自动装配是通过条件注解和配置元数据来实现的。 Spring Boot 的自动装配基于 Spring 的 IoC 容器和自动装配机制。当你引入某个依赖时,Spring Boot 会根据该依赖的条件注解和配置元数据,自动完成相应的配置和注入。例如,当你引入 Spring Data JPA 的依赖时,Spring Boot 会自动配置数据库连接池、实体管理器、事务管理器等相关组件。 要实现自动装配,你需要做以下几步: 1. 引入相关的依赖:在你的项目中引入需要使用的组件的依赖,例如数据库、消息队列等。 2. 提供必要的配置信息:在 application.properties 或 application.yml 文件中配置相应的属性,例如数据库连接信息、消息队列地址等。 3. 使用相关的注解:在需要使用自动装配的地方使用相应的注解,例如 @Autowired 注解用于依赖注入。 Spring Boot 的自动装配还提供了一些高级特性,例如条件装配、排除装配、自定义装配等。你可以通过条件注解和配置元数据来控制自动装配的行为,以满足不同环境和需求的配置。 希望以上信息对你有帮助!如果你有更多关于 Spring Boot 自动装配的问题,可以继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值