@SpringBootApplication注解及自动装配

参考:ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!
@SpringBootApplication结构图如下:
在这里插入图片描述
首先看看这个比较底层的@Import注解

@Import

首先来看看该注解的注释说明:

Indicates one or more component classes to import — typically @Configuration classes.
Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes

基本作用就是导入bean,由上可知通常有四个方式:

  1. @Configuration 注解类
  2. ImportSelector接口实现类
  3. ImportBeanDefinitionRegistrar接口实现类
  4. 普通java类

验证一下,现在有一个Student类需要注入IoC容器:

方式1:
在这里插入图片描述
方式2:
![在这里插入图片描述](https://img-blog.csdnimg.cn/8b02274996014e4985df4ba80cb120c3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAVC5ZLkJhbw==,size_20,color_FFFFFF,t_70,g_se,x_16

方式3:
在这里插入图片描述
方式4:

在这里插入图片描述

@AutoConfigurationPackage

看看它的注释:

Registers packages with AutoConfigurationPackages. When no base packages or base package classes are specified, the package of the annotated class is registered.
可以看到,这个注解这里就是将org.springframework.boot.autoconfigure.AutoConfigurationPackages注入IoC容器,原理是@Import一个ImportBeanDefinitionRegistrar接口的实现类,@import前后BeanDefinition变化如下两图所示:
在这里插入图片描述在这里插入图片描述

@EnableAutoConfiguration

基于Spring应用程序上下文进行自动配置,根据依赖尝试猜测并配置Bean;另外,被@EnableAutoConfiguration注解的类所在的 p a c k a g e package package 通常作为扫描注解@Entity的根路径,这也是为什么@SpringBootApplication放在顶级 p a c k a g e package package 下面。
注意,注解了@EnableAutoConfiguration的类的其他注解可以生效,如@Configuration、@Bean、@Controller等,但是这个类之外的其他类(包括同一个包中的其他类)的注解都不会生效,扫描包中其他类注解起作用的是@ComponentScan及其他特殊的途径。
综上,@EnableAutoConfiguration扫描的是注解它的类和classpath下依赖包中META-INF文件夹下spring.factories中以org.springframework.boot.autoconfigure.EnableAutoConfiguration为key的value类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	Class<?>[] exclude() default {};
	String[] excludeName() default {};
	}

需要注意的是,虽然@EnableAutoConfiguration上有个@Import一个实现了ImportSelector接口的类,但它却不是通过ImportSelector接口的selectImports方法来工作的 而是通过DeferredImportSelector接口的内部接口Groupvoid process(AnnotationMetadata metadata, DeferredImportSelector selector);
为什么?答案就在ConfigurationClassParser.java(专门用于处理配置注解的类,由BeanDefinitionRegistryPostProcessor接口方法调用)中的processImports 方法,因为@EnableAutoConfiguration上的AutoConfigurationImportSelector.class实现了DeferredImportSelector接口,字面意思就是延迟import,这个接口import的东西会在扫描处理完所有的@Configuration注解之后(包括依赖包中的和手动写的)再处理。
小结一下:SpringBoot先加载扫描所有注解了@Configuration的类(同步解析@Component,@ComponentScan,@Import,@ImportResource,@Bean,@PropertySource注解),然后对于@Import(类名.class)中类如果没有继承自DeferredImportSelector接口则再扫描后立即进行解析处理,而如果该类继承自
DeferredImportSelector接口则不执行,等扫描完了所有的注解后再统一通过DeferredImportSelector接口的内部接口Groupvoid process(AnnotationMetadata metadata, DeferredImportSelector selector); 来执行。
看他的注释:

A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.

注释意思也是,处理完所有@Configuration beans后再进行处理。从package org.springframework.context.annotation.ConfigurationClassParser类中的parser方法也可以看出:



class ConfigurationClassParser {
		
public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					// parse没有注解`DeferredImportSelector`接口的@Import注解和其他那几个注解
					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 (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}
		// 统一处理继承自`DeferredImportSelector`接口的@Import注解
		this.deferredImportSelectorHandler.process();
	}

			protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
		// Process any @PropertySource annotations
		...
		// Process any @ComponentScan annotations
		...
		// Process any @ImportResource annotations
		...
		// Process individual @Bean methods
		...
		// 处理@Import
		processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
		...
		}

		// Process any @Import annotations
		private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
			boolean checkForCircularImports) {
			// 如果import的类继承了ImportSelector接口
			if (candidate.isAssignable(ImportSelector.class)){
				// 如果是DeferredImportSelector类型,defer延迟的意思
				if (selector instanceof DeferredImportSelector){
					// 这里只把selector转为DeferredImportSelectorHolder存入this.deferredImportSelectors,执行不在这块,在parse方法的最后一行执行。
					this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
				} else {					
					// 不是DeferredImportSelector类型就执行selectImports方法
					String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
					Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
					processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
				}
}
}

通过调用org.springframework.context.annotation;包下的ConfigurationClassPostProcessor类解析加了@Configuration@Component@ComponentScan@Import@ImportResource等注解。

凡是注解了@Configuration的类,beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);;没有注解@Configuration但是注解了@Component@ComponentScan@Import@ImportResource的类,beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);

来看代码:

// 
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
		PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
		...
		@Override
		public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		// 本类中的方法
		processConfigBeanDefinitions(registry);
		}
		}

		public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
			List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
			String[] candidateNames = registry.getBeanDefinitionNames();
			for (String beanName : candidateNames) {
				BeanDefinition beanDef = registry.getBeanDefinition(beanName);
				// beandefinition里CONFIGURATION_CLASS_ATTRIBUTE设置过了,跳过
				if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
			// 这里这个最重要!!!
			// 跳到ConfigurationClassUtils类中的checkConfigurationClassCandidate方法
				else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
			...
			// 解析配置类,在此处会解析配置类上的注解(ComponentScan扫描出的类,@Import注册的类,以及@Bean方法定义的类)
        	// 注意:这一步只会将加了@Configuration注解以及通过@ComponentScan注解扫描的类才会加入到BeanDefinitionMap中
        	// 通过其他注解(例如@Import、@Bean)的方式,在parse()方法这一步并不会将其解析为BeanDefinition放入到BeanDefinitionMap中,而是先解析成ConfigurationClass类
        	// 真正放入到map中是在下面的this.reader.loadBeanDefinitions()方法中实现的
        	// parse方法最后this.deferredImportSelectorHandler.process();这个就是处理延迟importSelector也就是这里加载依赖jar包下所有META-INF文件夹spring.factories类中AutoConfiguration
			parser.parse(candidates);
			...
			// 将上一步parser解析出的ConfigurationClass类加载成BeanDefinition
			// 实际上经过上一步的parse()后,解析出来的bean已经放入到BeanDefinition中了,但是由于这些bean可能会引入新的bean,例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean,或者bean中存在被@Bean注解的方法
			// 因此需要执行一次loadBeanDefinition(),这样就会执行ImportBeanDefinitionRegistrar或者ImportSelector接口的方法或者@Bean注释的方法
			this.reader.loadBeanDefinitions(configClasses);
		}
}



abstract class ConfigurationClassUtils {
	...
	private static final Set<String> candidateIndicators = new HashSet<>(8);

	static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}
	...

	public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
			...
		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
		// 如果有@Configuration注解,beanDefinition里面某个属性设为full
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		// 这个isConfigurationCandidate就会从上面的candidateIndicators里面遍历比较
		// 如果有candidateIndicators任意一个注解,beanDefinition里面某个属性设为lite
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}
		...
}
}

具体可参考ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!

继而通过解析@EnableAutoConfiguration上的@Import(AutoConfigurationImportSelector.class)来调用getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)方法,里面也有个getCandidateConfigurations(annotationMetadata, attributes)来扫描。
具体调用流程可参考:AutoConfigurationImportSelector到底怎么初始化

总而言之,该注解作用就是在依赖的包的META-INF文件夹下的spring.factories文件种找org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,并扫描。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: @SpringBootApplication注解是Spring Boot应用程序的核心注解,它组合了以下三个注解:@Configuration,@EnableAutoConfiguration和@ComponentScan。它们为Spring Boot应用程序提供了自动装配和基础配置的功能。 ### 回答2: @SpringBootApplication是Spring Boot框架中的核心注解。它是一个复合注解,包含了多个注解的功能。 首先,@SpringBootApplication注解是一个组件扫描的开关,会自动扫描当前包以及子包下的所有组件,包括被@Configuration、@ComponentScan、@EnableAutoConfiguration注解修饰的类。这样就可以自动将组件(比如@Controller、@Service等)纳入Spring容器管理,无需配置xml文件。 其次,@SpringBootApplication注解还会自动启用Spring的自动配置功能。Spring Boot框架中内置了很多starter,这些starter包含了常用的第三方库的配置信息,比如数据库、Web、缓存等。当我们引入这些starter依赖后,他们就会根据项目中的配置和依赖自动进行相应的配置,无需手动编写大量的配置代码。 最后,@SpringBootApplication注解还会自动启用Spring Boot的特性,比如自动装配、Spring Boot Actuator等。自动装配是Spring Boot的重要特性之一,它简化了依赖注入的配置,通过一些约定大于配置的规则,可以实现将不同模块之间的依赖关系自动注入到Spring容器中。而Spring Boot Actuator是Spring Boot提供的监控和管理功能,可以通过HTTP接口或者JMX端口来获取应用的运行状态、性能指标、健康状况等信息,方便我们对应用进行监控和管理。 综上所述,@SpringBootApplication注解是Spring Boot框架中的核心注解,它包含了组件扫描、自动配置和特性启用等功能,简化了项目的配置和开发,提高了开发效率和系统的可管理性。 ### 回答3: @SpringBootApplication是Spring Boot框架中的核心注解之一。它是一个复合注解,包含了@Configuration、@EnableAutoConfiguration和@ComponentScan这三个注解的功能。 @Configuration注解表明该类是一个配置类,它可以替代传统的XML配置文件,用于定义配置bean的方法。在Spring Boot中,一般会将@Configuration注解用在主类上,表示这是一个配置类。 @EnableAutoConfiguration注解是开启自动配置的功能。通过@EnableAutoConfiguration,Spring Boot会根据项目的依赖和配置,自动地完成一系列的配置工作,如配置数据库连接、创建Web环境、配置Servlet容器等。这样可以大大简化了项目的配置工作,开发者只需要关注业务逻辑的编写即可。 @ComponentScan注解用于扫描指定包及其子包下的组件(如Controller、Service、Repository等),并将其注册为bean。被扫描的组件可以通过其他注解(如@Component、@Service、@Repository等)进行标识。通过@ComponentScan,Spring Boot可以自动地将这些组件加载到Spring容器中,使其可以被其他组件引用和调用。 综上所述,@SpringBootApplication注解的作用是将@Configuration、@EnableAutoConfiguration和@ComponentScan这三个注解的功能集成在一起,用于简化Spring Boot项目的配置和开发工作。通过使用@SpringBootApplication注解,开发者可以更加便捷地搭建和开发自己的Spring Boot应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

T.Y.Bao

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

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

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

打赏作者

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

抵扣说明:

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

余额充值