欢迎您访问新疆栾骏商贸有限公司,公司主营电子五金轴承产品批发业务!
全国咨询热线: 400-8878-609

新闻资讯

常见问题

如何通过一张图搞懂springBoot自动注入原理

作者:用户投稿2026-01-11 07:00:55
目录
  • @SpringBootApplication注解解读
  • 1.@SpringBootConfiguration
  • 2.@EnableAutoConfiguration
    • @Inherited
    • @AutoConfigurationPackage
    • AutoConfigurationImportSelector.class
  • 3.@ComponentScan
    • 总结 

      @SpringBootApplication注解解读

      为什么我们的启动类上标注一个@SpringBootApplication注解,再加一个run()方法就可运行起来,可以看出我们的@SpringBootApplication注解是多么的强大。

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

      @SpringBootApplication其实是由三个字注解来合成的。我们可以完全用这个三个注解来替换掉@SpringBootApplication。

      @SpringBootConfiguration
      @EnableAutoConfiguration
      @ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
              @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
      public class App {
          public static void main(String[] args) {
              SpringApplication.run(App.class, args);
          }
      }
      

      我们来说下这三个注解的一个作用,分别做了什么事情,为什么区区几个注解就可以让我们的程序跑起来。

      1.@SpringBootConfiguration

      其实点击进去就是一个 @Configuration,没什么特别的,学习过spring的朋友都知道,这是用于标注一个配置类的。

      2.@EnableAutoConfiguration

      重点就是在于@EnableAutoConfiguration 这个注解,我们先看下他的一个构成。

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

      @Inherited

      @Inherited : 如果标注此注解标识子类也可以进行继承,这个不太重要

      我们先来看.@EnableAutoConfiguration 注解中的@AutoConfigurationPackage注解里面的内容有什么 ? 可以看到导入了一个AutoConfigurationPackages.Registrar类型的组件

      @AutoConfigurationPackage

      @AutoConfigurationPackage:

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

      我们来看下导入的这个AutoConfigurationPackages.Registrar组件做了什么事情?

      registerBeanDefinitions? registerBeanDefinitions是不是就是往我们的容器中添加组件啊?那我们来看看他具体导入了那些组件。你可以将断点打在这两个方法上

      	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
      
      		@Override
      		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
      		}
      
      		@Override
      		public Set<Object> determineImports(AnnotationMetadata metadata) {
      			return Collections.singleton(new PackageImports(metadata));
      		}
      
      	}
      

      只要把BeanDefinition注册进去,往后我们只要调用getBean()就会得到该对象,当然不一定得我们自己调用,spring在底层最后刷新容器的时候,就会初始化所有的单实例bean,这也就是为什么我们直接使用相关注解就可以获取到该对象的原有。

      getBean()有两层意思:

      1.容器中有直接从缓存中获取

      2.容器中没有直接创建一个,也就是走bean的生命周期创建

      AutoConfigurationImportSelector.class

      @Import(AutoConfigurationImportSelector.class) 核心组件,我们来看看他做了什么事情。

      AutoConfigurationImportSelector他呢继承了DeferredImportSelector,DeferredImportSelector他呢有个内部接口Group,而Group中又有个方法process()
      所以我们来到AutoConfigurationImportSelector中的process()方法打上断点进行观察

      	@Override
      		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
      			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
      					() -> String.format("Only %s implementations are supported, got %s",
      							AutoConfigurationImportSelector.class.getSimpleName(),
      							deferredImportSelector.getClass().getName()));
      			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
      					.getAutoConfigurationEntry(annotationMetadata);
      			this.autoConfigurationEntries.add(autoConfigurationEntry);
      			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
      				this.entries.putIfAbsent(importClassName, annotationMetadata);
      			}
      		}
      
      

      这个方法一上来就要获取一些自动注入的条目autoConfigurationEntry,那我们看下他是怎么获取的,获取的究竟是什么东西?
      通过堆栈信息我们可以看到是通过invokeBeanDefinitionRegistryPostProcessors()这个方法调用过来的,说明在工厂初始化完毕,你可以往里面仍一些bean的定义信息了。

      进去就是这个方法

      	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);
      	}
      

      我们再进入这个方法

      在进入loadFactoryNames()方法中

      那我们去看看这个文件里面有什么

      结合上面的代码他是不是要加载org.springframework.boot.autoconfigure.EnableAutoConfiguration里面的内容,这些就是一些配置类的信息

      其实这个文件 spring.factories 里面的每一个类的权限的名称就是一个配置类,只不过springBoot把这些配置类都写好了,在我们某些场景需要的时候直接给你注入就行,这就说明了我们不用写那么多配置文件的原因。

      我随便点击一个类进去看看

      加载完成后: 怎么多?是不是全部第用得上啊?

      你看下面的代码,是不是要排除,移除掉,过滤掉一些啊,排除就是我们自己在注解中写的排除属性进行排除,让后就是经过一系列的筛选过滤。

      最后将这些信息put到entries中

      3.@ComponentScan

      @ComponentScan这个注解的功能就是扫描当前类上标注此注解所在包路径的所有类,这也就是为什么我们需要把主启动类放在最外面的原有,如果你想把配置类放在别的地方,那你就得自己手动指定包扫描路径.

      最后奉上超清晰的图

      总结