springboot 自动装配

目录

1、自动装配是什么及作用

2、spring自动装配的原理

2.1、启动类上注解的作用

@SpringBootConfiguration这个注解包含了@Configuration,@Configuration里面又包含了一个@Component注解,也就是说,这个注解标注在哪个类上,就表示当前这个类是一个配置类,而配置类也是spring容器中的组件

@EnableAutoConfiguration这个注解是开启自动配置的功能,里面包含了两个注解

@ComponentScan这个注解的作用就是扫描当前包及子包的注解


1、自动装配是什么及作用

springboot的自动装配实际上就是为了从spring.factories文件中获取到对应的需要进行自动装配的类,并生成相应的Bean对象,然后将它们交给spring容器来帮我们进行管理

2、spring自动装配的原理

2.1、启动类上注解的作用

@SpringBootApplication
这个注解是springboot启动类上的一个注解,是一个组合注解,也就是由其他注解组合起来,它的主要作用就是标记说明这个类是springboot的主配置类,springboot应该运行这个类里面的main()方法来启动程序

这个注解主要由三个子注解组成:

  • @SpringBootConfiguration
  • @EnableAutoConfiguratio
  • @ComponentScan
@SpringBootConfiguration
这个注解包含了@Configuration,@Configuration里面又包含了一个@Component注解,也就是说,这个注解标注在哪个类上,就表示当前这个类是一个配置类,而配置类也是spring容器中的组件

@EnableAutoConfiguration
这个注解是开启自动配置的功能,里面包含了两个注解
  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage
这个注解的作用说白了就是将主配置类(@SpringBootApplication标注的类)所在包以及子包里面的所有组件扫描并加载到spring的容器中,这也就是为什么我们在利用springboot进行开发的时候,无论是Controller还是Service的路径都是与主配置类同级或者次级的原因


@Import(AutoConfigurationImportSelector.class)
上一个注解我们把所有组件都加载到了容器里面,这个注解就是将需要自动装配的类以全类名的方式返回,那是怎么找到哪些是需要自动装配的类呢?

1、AutoConfigurationImportSelector这个类里面有一个方法selectImports(),如下

2、在selectImport()方法里调用了一个getAutoConfigurationEntry()方法,这个方法里面又调用了一个getCandidateConfigurations()方法

3、在getCandidateConfigurations()方法里面调用了loadFactoryNames()方法

4、loadFactoryNames()方法里面又调用了一个loadSpringFactories()方法

5、关键就在这个loadSpringFactories()方法里面,在这个方法里,它会查找所有在META-INF路径下的spring.factories文件


6、在META-INF/spring.factories这个文件里面的数据是以键=值的方式存储,然后解析这些文件,找出以EnableAutoConfiguration为键的所有值,以列表的方式返回

@ComponentScan
这个注解的作用就是扫描当前包及子包的注解

一般我们启动一个项目只需要再启动类上面增加@SpringBootApplication 注解,就能自动的装备bean对象,原理是什么呢? 下面从SpringApplication.run() 开始进行解析一下

SpringApplication.run()

从上图中我们很容易就进入到了SpringApplication.run()方法中, 注意我上述框起来的两行代码分别是创建上下文和刷新上下文

创建上下文 this.createApplicationContext

从上述源码中,我们可以知道spring 通过反射的方式创建了一个Context, 可以根据WebApplicationType创建指定的上下文, 这里以AnnotationConfigServletWebServerApplicationContext为例子,进入源码

AnnotationConfigServletWebServerApplicationContext

从上述源码中,我们可以知道spring 通过反射的方式创建了一个Context, 可以根据WebApplicationType创建指定的上下文, 这里以AnnotationConfigServletWebServerApplicationContext为例子,进入源码

AnnotationConfigServletWebServerApplicationContext

在这个上下文中,可以看到这里创建了两个对象, 一个是根据注解的Reader 和 根据路径的Scanner,并且传入了自身的上下文引用

从上面两图中,我们可以看见帮助可上下文注册了BeanFactory 的后置处理器, 比如框住的部分就是增加了ConfigurationClassPostProcessor后置处理器

更新上下文this.refreshContext(context)

在更新上下文的时候,最终会进入到AbstractApplicationContext 的refresh 方法中,如下图所示

invokeBeanFactoryPostProcessors

invokeBeanFactoryPostProcessors是一个BeanFactory 类型的后置处理方法,在该方法获取所有注册的BeanFactoryPostProcessor,并且执行postProcessBeanFactory方法,代码如下图

// AbstractApplicationContext# invokeBeanFactoryPostProcessors
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
        .......
    }

// PostProcessorRegistrationDelegate# invokeBeanFactoryPostProcessors
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
       .......
        invokeBeanFactoryPostProcessors((Collection)nonOrderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
        .......
    }

// PostProcessorRegistrationDelegate# invokeBeanFactoryPostProcessors
    private static void invokeBeanFactoryPostProcessors(Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
        Iterator var2 = postProcessors.iterator();
        while(var2.hasNext()) {
            BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var2.next();
            postProcessor.postProcessBeanFactory(beanFactory);
        }
    }

如上图代码所示, 最终会执行BeanFactoryPostProcessor.postProcessBeanFactory, 还记得在创建上下文中我们最后一张图,我们注册了一些BeanFactoryPostProcessor吗? 就以ConfigurationClassPostProcessor 为例,在执行postProcessBeanFactory时,进入该方法,最后调用processConfigBeanDefinitions进行解析和注册BeanDefinitions

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		.... 省略
        if (!this.registriesPostProcessed.contains(factoryId)) {
            this.processConfigBeanDefinitions((BeanDefinitionRegistry)beanFactory);
        }
    }


public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {  
    ......		
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
    HashSet alreadyParsed = new HashSet(configCandidates.size());

    do {
        // 解析
        parser.parse(candidates);  
        Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());
		.....
		....
        // 注册
        this.reader.loadBeanDefinitions(configClasses);
		...
    }
    }

在进入parse()方法,实际上是进入到ConfigurationClassParser.parse

ConfigurationClassParser.parse

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<>();

    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                // 对于每个有注解的类,都执行方法parse(AnnotationMetadata metadata, String beanName)
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            ...
                catch (BeanDefinitionStoreException ex) {
                    ...
                }
        }
        //后续处理DeferredImportSelector的实现类
        processDeferredImportSelectors();
    }
    
    
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
        processConfigurationClass(new ConfigurationClass(metadata, beanName));
    }
    

    
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		.... 省略
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);
		.... 省略

ConfigurationClassParser.doProcessConfigurationClass

经过多次调用,最终调用了doProcessConfigurationClass方法,在该方法中针对不同的注解类型, 执行了不同的注解执行方法,比如针对启动类中@ComponentScan 注解, 这里会直接执行,扫描指定包下面对象并封装成BeanDefinitionHolder,还有一个需要关注的是@Import 装配processImports方法

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {

		// 执行 @PropertySource 注解
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				....
		}

		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// 配置类使用 @ComponentScan 注解 -> 立即执行扫描
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan,sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		//  执行 @Import 注解
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		//  执行 @ImportResource 注解
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		// 处理 @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// 处理接口中的默认方法
		processInterfaces(configClass, sourceClass);

		// 执行父类,如果存在
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}

ConfigurationClassParser.processImports

这里省略了其他代码,只留下关键代码selector.selectImports, 在@EnableAutoConfiguration 注解中, @Import({AutoConfigurationImportSelector.class}) 有这么一个注解, 因此,这里selector.selectImports调用的实际上是AutoConfigurationImportSelector.selectImports 方法

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

			...... 省略其他代码
			String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
			Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
			...... 省略其他代码			
	}

AutoConfigurationImportSelector.selectImports

在进入到selectImports 后,调用了getCandidateConfigurations,最终调用到了SpringFactoriesLoader.loadSpringFactories 方法

SpringFactoriesLoader.loadSpringFactories

根据代码我们可以发现, 最终类加载器会从META-INF/spring.factories中获取所有全类限定名,便可提供给上层方法进行获取,最后创建对应的BeanDefinition对象,在后面Bean的实例化过程中,便可实现自动装配了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Upaaui

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

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

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

打赏作者

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

抵扣说明:

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

余额充值