Springboot如何实现自动装配

Springboot如何实现自动装配

首先附上springboot启动的整理流程图,可以发现实现自动装配的主要是里面的两个流程prepareContext()和refreshContext()。
在这里插入图片描述
下面就让我们进入这两个方法,去分析spring如何完成自动装配。

prepareContext()

prepareContext里面的核心方法是load(context, sources.toArray(new Object[0]));,

sourcce就是我们的main函数所在的类。
在这里插入图片描述
创建一个loader用于加载我们的class文件createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);

然后调用loader.load();,我们传进去的是一个class,所以代码会进入到load((Class<?>) source);

private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		if (source instanceof Class<?>) {
			return load((Class<?>) source);
		}
		if (source instanceof Resource) {
			return load((Resource) source);
		}
		if (source instanceof Package) {
			return load((Package) source);
		}
		if (source instanceof CharSequence) {
			return load((CharSequence) source);
		}
		throw new IllegalArgumentException("Invalid source type " + source.getClass());
	}

把启动类Main注册进beanDefinition
在这里插入图片描述

refreshContext()

一路点进来,走到invokeBeanFactoryPostProcessors(beanFactory);进行BeanFactoryPostProcessor的扩展。

String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, 
					postProcessor.postProcessBeanDefinitionRegistry(registry);

根据类型,获取到ConfigurationClassPostProcessor,解析注解就是在这个类完成的,走到ConfigurationClassPostProcessor的processConfigBeanDefinitions方法

从容器中的获取所有已经注册的bdName

String[] candidateNames = registry.getBeanDefinitionNames();

在这里插入图片描述
判断这些bd上面是否有@configuration或者@Bean注解

		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

根据判断,只有main上面有加了configuration注解,为什么,因为Main上面有@SpringBootApplication注解,

@SpringBootApplication
public class Main {

而@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 {

SpringBootApplication上面就有@Configuration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

所以我们就走到了parser.parse(candidates),其中candidates只有我们的main。
判断bd是否AnnotatedBeanDefinition,是,走进parse方法。

if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}

走到doProcessConfigurationClass(configClass, sourceClass);方法,接下来就是解析注解的逻辑了。
首先判断是否加了@Component,@Configuration里面就有@Component,所以满足,解析@Component。

Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
		if (!memberClasses.isEmpty()) {

这个memberClasses为空,所以直接return了。

然后判断是否有@ComponentScan,SpringBootApplication就有ComponentScan,所以现在解析@ComponentScan

Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);

basePackages就是我们的包名com.hhcui

scanner.doScan(StringUtils.toStringArray(basePackages));

执行完doScan后,然后程序又调用parse(bdCand.getBeanClassName(), holder.getBeanName());去解析扫描到的BD上面的注解,这样就完成了对启动函数所在包下面所有BD的扫描。
接下来就解析@Import注解了

processImports(configClass, sourceClass, getImports(sourceClass), true);
有两个import,自动装配有关的是AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class)

processImports方法就把AutoConfigurationImportSelector add进了deferredImportSelectorHandler。然后所有注解都解析完毕了
执行this.deferredImportSelectorHandler.process()。

然后执行
handler.processGroupImports();
grouping.getImports();
然后到AutoConfigurationImportSelector的process方法

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);


protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

factoryClassName = org.springframework.boot.autoconfigure.EnableAutoConfiguration,然后从META-INF/spring.factories找到所有的org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的key-value。

至此,所有需要自动装配的类都形成了BD放到了我们的容器中,后面就是bd转化成bean的过程了,这部分和spring一样。可以参考之前的帖子
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值