简单分析SpringApplication.run的启动(1)

823 阅读6分钟

此篇文章记录自己学习,加深自己的理解,以及设计模式的运用。也请各位可以给出理解不足之处

准备步骤

1,搭建springboot例子

根据springboot官方网站,搭建简单的springboot工程,gs-spring-boot

2,主类代码

    @SpringBootApplication
    public class Application {
	public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {
            System.out.println("Let's inspect the beans provided by Spring Boot:");
            String[] beanNames = ctx.getBeanDefinitionNames();
            Arrays.sort(beanNames);
            for (String beanName : beanNames) {
                System.out.println(beanName);
            }
        };
    }
}

源码分析

以上操作结束后,开始对SpringApplication.run方法的分析,进入方法后,我们看到如下源码:

    public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
		return run(new Class<?>[] { primarySource }, args);
    }
    public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
    }

在第二个方法中,加载了两部分内容:
1,SpringApplication的构造方法。
2,SpringApplication的run方法。

1,构造方法

在SpringApplication的构造方法的方法中。

    public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 此处传入的null
		this.resourceLoader = resourceLoader;
		// 判断加载的自定义的启动类的class
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 默认是SERVLET的web应用程序
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 初始化的工厂,由工厂来加载spring的容器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		// 初始化的工厂,由工厂来加载spring的监听
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

ResourceLoader 使用策略模式加载资源的顶层接口。 构造方法中,主要定义了资源类文件,将springboot启动类存入Set中(有顺序要求)。获取用到的Application的启动类别是REACTIVE还是SERVLET 根据ApplicationContextInitializer.classApplicationListener.classMETA-INF/spring.factories文件中获取对应的类并实例化,使用setInitializerssetListeners分别存储。其中都是有序的集合。
getSpringFactoriesInstances

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		// 此方法源码作者对class文件加载模型的有深入理解,非常符合类的加载机制,getClassLoader该方法非常经典
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 加载springboot启动需要加载的配置类,加入到cache中,配置类路径为META-INF/spring.factories
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 通过ApplicationContextInitializer和ApplicationListener的构造信息,分别取的信息和访问,通过该构造加载方法实例化配置类信息
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		// 对实例化的类进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

其实从META-INF/spring.factories中读取类的时候是有序的,在实例化时变成无序,因此需要重新排序。AnnotationAwareOrderComparator由父级实现了Comparator接口。
由此可见构造方法中只是准备了启动要准备的资源文件。
由此可以看出,根据ApplicationContextInitializer.class取出的类并实例化的类有:
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
根据ApplicationListener.class取出的类并实例化的类有:
org.springframework.context.ApplicationListener=
org.springframework.boot.ClearCachesApplicationListener,
org.springframework.boot.builder.ParentContextCloserApplicationListener,
org.springframework.boot.context.FileEncodingApplicationListener,
org.springframework.boot.context.config.AnsiOutputApplicationListener,
org.springframework.boot.context.config.ConfigFileApplicationListener,
org.springframework.boot.context.config.DelegatingApplicationListener,
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
org.springframework.boot.context.logging.LoggingApplicationListener,
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

2run方法

run方式中的源码主要如下:

    // main方法中参数处理
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(
			args);
	// 此处的listeners,是从META-INF\spring.factories取出并实例化的
	// org.springframework.boot.context.event.EventPublishingRunListener
	// 在构造方法中我们的spring默认的web应用是servlet,此处实例化了
	// StandardServletEnvironment对象
	// 并加载了web应用的一些配置,并启用了Executor
	ConfigurableEnvironment environment = prepareEnvironment(listeners,
		applicationArguments);
	// 设置spring.beaninfo.ignore
	configureIgnoreBeanInfo(environment);
	// 在日志控制台打印的很酷的spring的字样
	Banner printedBanner = printBanner(environment);
	// 实例化
	// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
	// 加载注解类
	context = createApplicationContext();
	// 实例化异常报告
	// org.springframework.boot.diagnostics.FailureAnalyzers
	exceptionReporters = getSpringFactoriesInstances(
		SpringBootExceptionReporter.class,
		new Class[] { ConfigurableApplicationContext.class }, context);
	// 单独分析,前期加载容器
	prepareContext(context, environment, listeners, applicationArguments,
			printedBanner);
	// 单独分析,加载容器中的配置
	refreshContext(context);
	afterRefresh(context, applicationArguments);
	stopWatch.stop();
	if (this.logStartupInfo) {
		new StartupInfoLogger(this.mainApplicationClass)
			.logStarted(getApplicationLog(), stopWatch);
	}
	// 开始监听
	listeners.started(context);
	callRunners(context, applicationArguments);

prepareContext方法

    private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		// 将ConfigurableEnvironment放入上下文容器
		context.setEnvironment(environment);
		// 根据前面的处理加载一些配置信息,此处我的理解是作者对以上加载类做的后续补充
		postProcessApplicationContext(context);
		// 单例加载上下文容器
		applyInitializers(context);
		// 将容器内容加载到监听中,并启用了Executor
		listeners.contextPrepared(context);
		// 启动日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		// 获取bean工厂
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		// 由bean工厂来注册main的参数
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		// 由bean工厂来注册很酷的启动时打印的spring的字样,该字样可以配置,由使用者配置
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		
		// 加入到监听并启用了Executor,此处方法和contextPrepared功能一样
		listeners.contextLoaded(context);
	}

refreshContext方法,此处方法比较复杂

    private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
			    // 注册一个新的虚拟机关机挂钩。
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
	
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}

此处的((AbstractApplicationContext) applicationContext).refresh();
主要分析
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext

    @Override
	public final void refresh() throws BeansException, IllegalStateException {
		try {
			super.refresh();
		}
		catch (RuntimeException ex) {
			stopAndReleaseWebServer();
			throw ex;
		}
	}

该类的父类中的refresh方法,AbstractApplicationContext

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 准备此上下文以进行刷新
			// 注解的前期准备并加载到监听中,主要分析
			// AnnotationConfigServletWebServerApplicationContext
			prepareRefresh();
			// 告诉子类刷新内部bean工厂
			// 此方法是抽象方法,是刷新子类中的工厂bean,即ServletWebServerApplicationContext中的工厂类
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// 准备bean工厂以在此上下文中使用,加载较多的类
			prepareBeanFactory(beanFactory);
			try {
				// 允许在上下文子类中对bean工厂进行后处理,抽象方法,查看ServletWebServerApplicationContext中的方法,主要也是加载了一些web应该范围如request和response等
				postProcessBeanFactory(beanFactory);
				// 在上下文中调用注册为bean的工厂处理器,加载注解类
				invokeBeanFactoryPostProcessors(beanFactory);
				// 注册拦截bean创建的bean处理器,拦截器的处理
				registerBeanPostProcessors(beanFactory);
				// 初始化此上下文的消息源
				initMessageSource();
				// 初始化此上下文的事件多播器
				initApplicationEventMulticaster();
				// 在特定的上下文子类中初始化其他特殊bean
				// 抽象方法,查看即ServletWebServerApplicationContext中的onRefresh方法
				onRefresh();
				// 检查监听器bean并注册
				registerListeners();
				// 实例化所有剩余(非延迟初始化)单例.
				finishBeanFactoryInitialization(beanFactory);
				// 发布相应的事件
				finishRefresh();
			}
			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();
				// Reset 'active' flag.
				cancelRefresh(ex);
				// Propagate exception to caller.
				throw ex;
			}
			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

注解SpringBootApplication

在整个启动的过程中,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使用了EnableAutoConfiguration注解

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

EnableAutoConfiguratio导入了AutoConfigurationImportSelector类

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered 

在上下文容器进行加载时,由ConfigurationClassPostProcessor来处理如下自定义的类,包括其注解类。

 @SpringBootApplication
    public class Application {
	public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

小结

简单过了一遍源码,分析了SpringApplication.run的启动,以及加载类的一些主要方法。
可以看到Spring 应用上下文生命周期控制,注解驱动bean;Spring事件/监听机制加载初始化组件。有各自职责的工厂负责初始化不同功能的类;又定义了不同的监听对象服务不同类别加载过程中出现的事件。