小言_互联网的博客

Spring Boot源码分析——自动装配

555人阅读  评论(0)

Spring IOC容器可以自动装配(autowire)相互协作bean之间的关联关系,简单来说,Spring的自动装配可以帮助我们处理bean与bean之间的关系,不用我们去配置他们该使用哪个类。这样带来的好处是能明显减少配置的工作量(用bean模板其实也可以实现同样的效果),并且能使配置与代码同步更新。但其坏处就是会导致装配不明确,降低配置文件的可读性。

Spring自动装配有5种方式:

  • no:默认不使用
  • byName:根据属性名
  • byType:根据属性类型
  • constructor:根据构造器参数
  • autodetect:根据bean类的自省机制(introspection)来决定是用constructor还是byType

众所周知,所有Spring Boot项目的启动入口都是@SpringBootApplication,同时本文分析的“自动装配”也要从这下手。

参考文章:
Spring的自动装配
深入理解SpringBoot之自动装配
P.S. 本文源码是Spring Boot 1.3.7.RELEASE

1、@SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
//省略
}

@SpringBootApplication是个复合的注解,包括@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration。通过源码可以知道@SpringBootConfiguration本质上是个@Configuration,由于@ComponentScan没有指定扫描包,因此其默认扫描与该类同级的类或者同级包下的所有类。

2、@EnableAutoConfiguration

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

该注解就是开启自动装配功能,是自动装配的核心注解。Spring会试图在classpath下找到所有配置的bean,接着进行装配。装配过程中,会根据若干个Conditional条件(Spring4.X引入了@Conditional)定制规则来进行初始化。

@EnableAutoConfiguration主要是@Import(EnableAutoConfigurationImportSelector.class),引入EnableAutoConfigurationImportSelector.class,来看下其中的关键函数selectImports()

    @Override
	public String[] selectImports(AnnotationMetadata metadata) {
		try {
			AnnotationAttributes attributes = getAttributes(metadata);
			List<String> configurations = getCandidateConfigurations(metadata,
					attributes);
			configurations = removeDuplicates(configurations);
			Set<String> exclusions = getExclusions(metadata, attributes);
			configurations.removeAll(exclusions);
			configurations = sort(configurations);
			recordWithConditionEvaluationReport(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

从该函数可以看出,先从META-INF/spring-autoconfigure-metadata.properties获取元数据与元数据之间的相关属性,接着调用getCandidateConfigurations()函数

/**
	 * Return the auto-configuration class names that should be considered. By default
	 * this method will load candidates using {@link SpringFactoriesLoader} with
	 * {@link #getSpringFactoriesLoaderFactoryClass()}.
	 * @param metadata the source metadata
	 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
	 * attributes}
	 * @return a list of candidate configurations
	 */
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
		return SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
	}

	/**
	 * Return the class used by {@link SpringFactoriesLoader} to load configuration
	 * candidates.
	 * @return the factory class
	 */
	protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

在这里很巧遇到了SpringFactoriesLoader,他会获取EnableAutoConfiguration.class中的配置。
接着selectImports函数将相关数据进行去重、过滤、排除,最后得到需要装配的类。

3、invokeBeanFactoryPostProcessors

前面讲了怎么获取需要装配的类,那么接下来探讨下具体怎么装配。
从抽象类AbstractApplicationContext的refresh()方法下手,refresh()方法中的**invokeBeanFactoryPostProcessors(beanFactory)**就是关键。
可以看出该方法是触发BeanFactoryPostProcessors,那么跟进去看下

     /**
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before singleton instantiation.
	 */
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
	}
    public interface BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 * @param beanFactory the bean factory used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

来看下BeanFactoryPostProcessor接口的其中一种实现ConfigurationClassPostProcessor类(该类主要处理@Configuration注解)

/**
	 * Derive further bean definitions from the configuration classes in the registry.
	 */
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
		iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);

		RootBeanDefinition ecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class);
		ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME, ecbpp);

		int registryId = System.identityHashCode(registry);
		if (this.registriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
		}
		if (this.factoriesPostProcessed.contains(registryId)) {
			throw new IllegalStateException(
					"postProcessBeanFactory already called on this post-processor against " + registry);
		}
		this.registriesPostProcessed.add(registryId);

		processConfigBeanDefinitions(registry);
	}

postProcessBeanDefinitionRegistry方法是将configuration配置类中派生出bean定义

/**
	 * Build and validate a configuration model based on the registry of
	 * {@link Configuration} classes.
	 */
	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		//省略代码
		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
		do {
			parser.parse(candidates);
			parser.validate();
             //省略代码
	}

processConfigBeanDefinitions方法是基于@Configuration的类中构建和校验出配置的模型model,其中解析@Configuration关键类是ConfigurationClassParser,我们跟进去看下

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

		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Exception ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}

		processDeferredImportSelectors();
	}

很巧的是,parse()方法最后会调用前面聊到的processDeferredImportSelectors()。

private void processDeferredImportSelectors() {
		List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
		this.deferredImportSelectors = null;
		Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);

		for (DeferredImportSelectorHolder deferredImport : deferredImports) {
			ConfigurationClass configClass = deferredImport.getConfigurationClass();
			try {
				String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
				processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Exception ex) {
				throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
						configClass.getMetadata().getClassName() + "]", ex);
			}
		}
	}

processDeferredImportSelectors()中有句关键代码
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
其中deferredImport为DeferredImportSelectorHolder类,

private static class DeferredImportSelectorHolder {

		private final ConfigurationClass configurationClass;

		private final DeferredImportSelector importSelector;

		public DeferredImportSelectorHolder(ConfigurationClass configurationClass, DeferredImportSelector importSelector) {
			this.configurationClass = configurationClass;
			this.importSelector = importSelector;
		}

		public ConfigurationClass getConfigurationClass() {
			return this.configurationClass;
		}

		public DeferredImportSelector getImportSelector() {
			return this.importSelector;
		}
	}

DeferredImportSelectorHolder内部类中有DeferredImportSelector 的引用,到这里也就完成了整个自动装配的所有操作。

小结

  1. 自动装配归根到底,是使用SpringFactoriesLoader来加载所有被@EnableAutoConfiguration修饰的类中的配置,通过selectImports函数将相关数据去重、过滤、排除,最终确定需要装配的类;
  2. 当AbstractApplicationContext执行refresh()方法时,其中的invokeBeanFactoryPostProcessors方法会触发BeanFactoryPostProcessors去执行自动装配。
    比如处理@Configuration注解的ConfigurationClassPostProcessor类,其本身是BeanFactoryPostProcessors接口的具体实现,通过ConfigurationClassParser.parse()调用processDeferredImportSelectors(),执行DeferredImportSelector,来完成自动装配。

Spring Boot自动装配源码分析就到这里了,建议读者选择其中关键源码阅读,再加上调试源码加深理解。后面可能会不定期更新,有兴趣的朋友可以在评论区一起讨论研究。

最后有件很重要的事,那就是麻烦点赞关注赞赏,谢谢(๑•̀ㅂ•́)و✧


转载:https://blog.csdn.net/u014564102/article/details/105802065
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场