Ensamblaje automático: una breve discusión sobre SpringBoot

SpringBoot vsersion 2.2.6

1. SpringApplication.run (Application.class, args);

De este método como objeto de investigación

1.1 @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 contiene tres anotaciones importantes @SpringBootConfiguration y @EnableAutoConfiguration @ComponentScan

1.2 @SpringBootConfiguration

La capa inferior es @Configuration annotation
@Configuration La clase actual como clase de configuración es equivalente a una etiqueta Beans. Puede usar @Bean para declarar múltiples Beans en el contenedor IOC con esta anotación.

1.3 @EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
    
	// isEnabled用到了这个变量 去environment拿这个key值的value
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	...
protected boolean isEnabled(AnnotationMetadata metadata) {
    
    
	if (getClass() == AutoConfigurationImportSelector.class) {
    
    
		return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
	}
	return true;
}

@AutoConfigurationPackage Esta anotación importa una ruta de exploración registrada @Import (AutoConfigurationPackages.Registrar.class) a la variable global
@Import (AutoConfigurationImportSelector.class)

1.4 AutoConfigurationImportSelector

selectImports -> getAutoConfigurationEntry

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
    
    
		//spring.boot.enableautoconfiguration environment里的这个key值为false 返回空
		if (!isEnabled(annotationMetadata)) {
    
    
			return EMPTY_ENTRY;
		}
		// 获取注解的属性值
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// 获取spring.factories里的 org.springframework.boot.autoconfigure.EnableAutoConfiguration的value集合名称
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		// 去重
		configurations = removeDuplicates(configurations);
		// 收集排除的类名
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		// 去排除的数据
		configurations.removeAll(exclusions);
		// 由所有AutoConfigurationImportFilter类的实例再进行一次筛选
		configurations = filter(configurations, autoConfigurationMetadata);
		// 绑定事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
}

重点: starter 原理
selectImports -> getAutoConfigurationEntry -> getCandidateConfigurations

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    
    
	// 相当于Spring的SPI 约定META-INF/spring.factories 读取里面的配置信息
	// 核心发现配置类的方法 loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)
	// factoryType -> EnableAutoConfiguration.class
	// 发现factory.factories里面的这个org.springframework.boot.autoconfigure.EnableAutoConfiguration这个key的value的类
	// 自定义的starter也是利用了这个SPI的机制 网上自定义的starter的demo很多
	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;
}

2. SpringgApplication

Constructor SpringgApplication

public SpringApplication(Class<?>... primarySources) {
    
    
	this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    
    
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// servlet REACTIVE NONE webApplication属性
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	// spring.factories 初始化类
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// spring.factories 监听类
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

SpringApplication.run

public ConfigurableApplicationContext run(String... args) {
    
    
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	// java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
	configureHeadlessProperty();
	// 从META-INF/spring.factories中获取监听器
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
    
    
		// 封装命令行参数
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 构造应用上下文环境,完成后回调SpringApplicationRunListeners的environmentPrepared方法
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		// 处理需要忽略的Bean
		configureIgnoreBeanInfo(environment);
		// 打印banner
		Banner printedBanner = printBanner(environment);
		// 创建webApplication容器
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] {
    
     ConfigurableApplicationContext.class }, context);
		// 准备上下文环境,将environment保持到IOC容器中
        // 执行applyInitializers 回调ApplicationContextInitializer的initialize方法
        // 回调SpringApplicationRunListeners的contextPrepared方法
        // 回调SpringApplicationRunListeners的contextLoaded方法
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		// 完成组件的扫描,创建,加载等
		refreshContext(context);
		// 从beanFactory获取所有的ApplicationRunner
		// CommandLinedRunner进行回调
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
    
    
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
    
    
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
    
    
		listeners.running(context);
	}
	catch (Throwable ex) {
    
    
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

Supongo que te gusta

Origin blog.csdn.net/weixin_45657738/article/details/110876035
Recomendado
Clasificación