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一样。可以参考之前的帖子