当前位置: 首页 > news >正文

SpringBoot原理揭秘--自动装配(终)

上一章节我们理论的讲了讲springBoot自动装配本质上是依赖于springFrameWork的模块装配和条件装配,其中模块装配则主要是自定义注解+@import注解对于普通类,importSelector子类,ImportBeanDefinitionRegister子类和配置类的装配,而对于条件模块则是更加细化的控制哪些情况下装配哪些bean,主要又profile注解和conditional两大注解来控制,profile注解是根据环境,通过硬编码或者环境指令来触发,而conditional则更加灵活可以根据自定义条件来进行条件装配。

下面我们来实际结合代码来看看,springBoot到底是如何装配的

@SpringBootApplication
public class MybatisTestApplication {public static void main(String[] args) {SpringApplication.run(MybatisTestApplication.class, args);}}

当我们使用springBoot的时候一个必备的注解就是@SpringBootApplication这个注解,而这个注解恰恰也蕴含着自动装配的秘密。下面我们来看看

@SpringBootApplication

@SpringBootApplication 是 Spring Boot 应用的核心注解,它是一个组合注解,整合了多个 Spring 框架的注解,用于快速启动和配置 Spring 应用

@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 {/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/@AliasFor(annotation = EnableAutoConfiguration.class)Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0*/@AliasFor(annotation = EnableAutoConfiguration.class)String[] excludeName() default {};}

我们可以看到在@SpringBootApplication这个注解内部含有多个注解,分别是@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

这三个注解则是组成自动装配的核心。下面我们一个一个看

@SpringBootConfiguration

@SpringBootConfiguration实际上是@Configuration的子注解,他的主要作用则是与Configuration注解的作用一样被注解的类充当配置类,唯一的区别是@SpringBootConfiguration被注解的类会被首先加载到ioc容器中,也就是说是@SpringBootConfiguration注解可以控制加载bean的顺序

@ComponentScan

@ComponentScan注解的主要作用则是扫描当前类的包下的所有用户自定义的bean,将这些bean加载到ioc容器中去,从 @SpringBootApplication 注解所在类的同级包开始,递归扫描所有子包。

如果组件位于 @SpringBootApplication 所在包的上级目录,则不会被自动扫描。

@ComponentScan 会识别以下注解标记的类:

  • @Component:通用组件注解。
  • @Service:业务逻辑层组件。
  • @Repository:数据访问层组件(自动处理数据库异常)。
  • @Controller:Web 控制器组件。
  • @Configuration:配置类。
  • 其他自定义注解(需通过 @Component 元注解派生)。

@EnableAutoConfiguration

@EnableAutoConfiguration则是springBoot的能够完成自动装配的核心,对于SpringBootConfiguration使用来确定程序入口的,而ComponentScan则是扫描用户自定义的bean加载到ioc中,那么对于系统默认的bean以及第三方的默认bean则是通过EnableAutoConfiguration注解来加载进去的

下面我们看看EnableAutoConfiguration注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {/*** Exclude specific auto-configuration classes such that they will never be applied.* @return the classes to exclude*/Class<?>[] exclude() default {};/*** Exclude specific auto-configuration class names such that they will never be* applied.* @return the class names to exclude* @since 1.3.0*/String[] excludeName() default {};
}

在EnableAutoConfiguration注解中则是有两个重要注解分别是AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)。下面我们来看看AutoConfigurationPackage注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {/*** Base packages that should be registered with {@link AutoConfigurationPackages}.* <p>* Use {@link #basePackageClasses} for a type-safe alternative to String-based package* names.* @return the back package names* @since 2.3.0*/String[] basePackages() default {};/*** Type-safe alternative to {@link #basePackages} for specifying the packages to be* registered with {@link AutoConfigurationPackages}.* <p>* Consider creating a special no-op marker class or interface in each package that* serves no purpose other than being referenced by this attribute.* @return the base package classes* @since 2.3.0*/Class<?>[] basePackageClasses() default {};}

AutoConfigurationPackage注解的主要作用就是读取当前:用于标记自动配置类应扫描的基础包路径。它与自动配置机制紧密配合,确保组件(如 @Repository@Service 等)能被正确发现。

核心作用

  1. 指定自动配置扫描包
    告诉 Spring Boot 自动配置类应该从哪个包路径开始扫描组件(如 @Component@Repository 等)。

  2. 与 @ComponentScan 的区别

    • @ComponentScan:手动指定需要扫描的包路径。
    • @AutoConfigurationPackage:由 Spring Boot 自动应用,默认扫描启动类所在的包及其子包。
  3. 支持第三方库集成
    当第三方库需要自动配置时,可通过 @AutoConfigurationPackage 将其组件注册到主应用的上下文中。

注解的两个属性basePackages和basePackageClasses则是可以手动指定的,而之所以能自动锁定当前注解所在包主要是由于AutoConfigurationPackages.Registrar类和AutoConfigurationPackages类来决定的,下面我们来看看这两个类的源代码

public abstract class AutoConfigurationPackages {public static void register(BeanDefinitionRegistry registry, String... packageNames) {if (registry.containsBeanDefinition(BEAN)) {addBasePackages(registry.getBeanDefinition(BEAN), packageNames);}else {RootBeanDefinition beanDefinition = new RootBeanDefinition(BasePackages.class);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);addBasePackages(beanDefinition, packageNames);registry.registerBeanDefinition(BEAN, beanDefinition);}}static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}}private static final class PackageImports {private final List<String> packageNames;PackageImports(AnnotationMetadata metadata) {AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {packageNames.add(basePackageClass.getPackage().getName());}if (packageNames.isEmpty()) {packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));}this.packageNames = Collections.unmodifiableList(packageNames);}List<String> getPackageNames() {return this.packageNames;}@Overridepublic boolean equals(Object obj) {if (obj == null || getClass() != obj.getClass()) {return false;}return this.packageNames.equals(((PackageImports) obj).packageNames);}@Overridepublic int hashCode() {return this.packageNames.hashCode();}@Overridepublic String toString() {return "Package Imports " + this.packageNames;}}}

AutoConfigurationPackages 是 Spring Boot 中的一个核心工具类,其主要作用是管理自动配置的基础包路径注册和查询。它不直接参与业务逻辑,而是为 Spring Boot 的自动配置机制(特别是 Spring Data 的实体扫描)提供基础设施支持。

也就是说真正进行包路径注册的类则是内部类Registrar 类,而这个类注册包路径的主要方法则是registerBeanDefinitions

		@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}

对于metadata是注解的元信息,用于表示一个 (或接口)上的注解元信息。它提供了访问该类上声明的所有注解及其属性的能力。而registry则是一个BeanDefinition的存储器,一般加载后的BeanDefinition都会放在BeanDefinitionRegistry 当中。

下面我们来看看方法是如何运行的可以看到实际上调用了AutoConfigurationPackages的register方法,其中参数第一个则是用来存储BeanDefinition的BeanDefinitionRegistry,而第二个是一个string类型的可变参数,下面我们来看一下第二个参数首先将metadata当作构造方法参数传入PackageImports类当中

		PackageImports(AnnotationMetadata metadata) {AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {packageNames.add(basePackageClass.getPackage().getName());}if (packageNames.isEmpty()) {packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));}this.packageNames = Collections.unmodifiableList(packageNames);}

可以看到首先则是从元信息对象metadata中取出AutoConfigurationPackage注解对应的属性信息attributes,而这个属性信息我们之前介绍过分别是basePackages和basePackageClasses从中得到一个基础包类名信息加入到packageNames 的一个集合中,也就是说PackageImports主要是从元信息数据中获取注解属性的基础包类路径。

之后通过PackageImports来将读取到的所有包路径的基本信息当作参数传入方法中去。

	public static void register(BeanDefinitionRegistry registry, String... packageNames) {if (registry.containsBeanDefinition(BEAN)) {addBasePackages(registry.getBeanDefinition(BEAN), packageNames);}else {RootBeanDefinition beanDefinition = new RootBeanDefinition(BasePackages.class);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);addBasePackages(beanDefinition, packageNames);registry.registerBeanDefinition(BEAN, beanDefinition);}}

进入register方法中查看逻辑,我们发现首先会判断registry当中是否含有这个bean,而这个bean实际上就是AutoConfigurationPackages,如果registry中含有这个bean那么则会将这个bean+这些类名加入到一个方法addBasePackages当中,如果没有含有这个bean,则证明是首次创建,则会创建一个BasePackages类的beanDefinition ,这个类的作用就是专门用来存储基础包路径的随后调用addBasePackages方法,最后将其注册到registry类中。

private static final String BEAN = AutoConfigurationPackages.class.getName();

最后总结一下,对于EnableAutoConfiguration的主要作用就是将包的基础路径进行封装为BasePackages类后存储,而这个BasePackages类则是AutoConfigurationPackages这个类的内部类,对于这个注解则是首先导入了AutoConfigurationPackages的内部类registry,通过这个内部类的register方法将注解的属性进行提取,随后调用AutoConfigurationPackages的register方法将其载入到BasePackages类中。为什么将数据封装到BasePackages类中呢,因为spring本身要适配许多第三方框架,那么哪些第三方框架则可以直接通过BasePackages来拿到本项目的根路径,近而得到项目的其他信息。

http://www.dtcms.com/a/307680.html

相关文章:

  • Cesium 快速入门(二)底图更换
  • Spring Cloud『学习笔记』
  • 前端项目如何同时导入一个库的不同版本
  • SpringMVC的核心架构与请求处理流程
  • React中的this绑定
  • 网关 + MDC 过滤器方案,5分钟集成 日志 traceid
  • Java学习-----SpringBoot的常用注解(下)
  • 嵌入式硬件中瓷片电容的基本原理与详解
  • WebRTC 多媒体 SDP 示例与解析
  • 嵌入式硬件学习(十)—— LED驱动+杂项设备驱动
  • 2025电商CPS分销与推客系统小程序开发:趋势、架构与实战解析
  • SpringBoot3.x引入Quartz,持久化到MySQL数据库
  • npm 设置国内镜像源
  • 中宇联:以“智云融合+AI”赋能全栈云MSP服务,深化阿里云生态合作
  • 【YOLOv1】
  • 多云场景实战:华为手机 QR 码绑定与 AWS云服务器终端登录全解
  • 全球SPAD技术发展全景:工艺节点、产能布局与中国突破
  • 科研快报 |无人机+AI:广东防控基孔热背后的技术革命
  • 无人机传感器系统架构解析
  • 了解SQL
  • HttpServletRequest详细解释
  • HttpServletRequest 和 HttpServletResponse核心接口区别
  • UDP_千兆光通信(四)Tri Mode Ethernet MAC ip核
  • 力扣 hot100 Day61
  • Mockito:Java单元测试Mock框架
  • Mac 系统下安装 nvm
  • 工业园区泵房物联网能耗优化解决方案:打造绿色低碳厂区
  • 组播 | 不同 VLAN 间数据转发实现逻辑 / 实验
  • 深入解读OpenTelemetry分布式链路追踪:原理与实践指南
  • tlias智能学习辅助系统--SpringAOP-基础-核心概念