目录
@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的实例化过程中,便可实现自动装配了