【一】Spring Boot 源码分析之启动主流程

版权声明:转载注明出处 https://blog.csdn.net/jy02268879/article/details/86678184

目录

1.生成一个SpringApplication的实例new SpringApplication(Sources)

1.1initialize方法主要做的事情有:

1.1.1.配置source

1.1.2.配置是否为web环境

 1.1.3.创建并且设置初始化器

1.1.4.创建并且设置监听器

1.1.5.配置应用的主方法所在类

 2.运行SpringApplication的实例进行初始化操作

2.1run方法主要做的事有:

2.1.1.stopWatch.start()

2.1.2.Headless配置

 2.1.3.创建SpringApplicationRunListeners,发送广播

2.1.4.配置环境模块ConfigurableEnvironment     

2.1.5.配置Banner

2.1.6.ConfigurableApplicationContext spring容器

2.1.7广播

2.1.8计时结束


debug调式SpringBoot 1.5.8的源码,运行在tomcat上。

Spring Boot 通过扫描classpath下的实例决定装载哪个web容器。

我这里pom.xml文件用的spring-boot-starter-web所以,会启动tomcat来作为servlet的容器。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

流程图:

顺序是从左往后,左边全部完了才执行右边的。

入口:启动类

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

进入SpringApplication中可以看到run方法做了两件事

	public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
		return new SpringApplication(sources).run(args);
	}

1.生成一个SpringApplication的实例new SpringApplication(Sources)

SpringApplication的构造方法中调用了initialize方法

	private void initialize(Object[] sources) {
        //配置source
		if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}
        //配置是否为web环境
		this.webEnvironment = deduceWebEnvironment();
        //设置初始化器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        //设置监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//配置应用的主方法所在类
        this.mainApplicationClass = deduceMainApplicationClass();
	}

1.1initialize方法主要做的事情有:

1.1.1.配置source

if (sources != null && sources.length > 0) {
			this.sources.addAll(Arrays.asList(sources));
		}

1.1.2.配置是否为web环境

this.webEnvironment = deduceWebEnvironment();
private boolean deduceWebEnvironment() {
		for (String className : WEB_ENVIRONMENT_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return false;
			}
		}
		return true;
	}

 1.1.3.创建并且设置初始化器

从spring.factories文件中创建所有的初始化器

setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));

通过断点可以看到有如下6种初始化器

org.springframework.boot.context.config.DelegatingApplicationContextInitializer

org.springframework.boot.context.ContextIdApplicationContextInitializer

org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer

org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer

org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer

org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

这里详细讲一下怎么得到工厂类实例的

进入getSpringFactoriesInstances方法 :

获取传入的工厂类名、类加载器

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {

        //类加载器
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

		
		Set<String> names = new LinkedHashSet<String>(
        //通过类加载器获取指定的spring.factories文件,获取文件中工厂类的全路径,得到工厂类名
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));

        //通过类路径反射得到工厂的class对象、构造方法,生成工厂类实例返回
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

SpringFactoriesLoader.loadFactoryNames方法:

通过类加载器获取指定的spring.factories文件,获取文件中工厂类的全路径,得到工厂类名

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		try {

            //通过类加载器获取指定的spring.factories文件
			Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			List<String> result = new ArrayList<String>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));

                //获取文件中工厂类的全路径
				String factoryClassNames = properties.getProperty(factoryClassName);
				result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
			}
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
					"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

createSpringFactoriesInstances方法:

通过类路径反射得到工厂的class对象、构造方法,生成工厂类实例返回

private <T> List<T> createSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set<String> names) {
		List<T> instances = new ArrayList<T>(names.size());
		for (String name : names) {
			try {

                //通过类路径反射得到工厂的class对象
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				Assert.isAssignable(type, instanceClass);

                //得到构造方法
				Constructor<?> constructor = instanceClass
						.getDeclaredConstructor(parameterTypes);

                //得到实例
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException(
						"Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}

1.1.4.创建并且设置监听器

从spring.factories文件中创建应用监听器

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

这里的getSpringFactoriesInstances方法跟上面创建设置初始化器一样,只是入参不一样,这里入参是ApplicationListener.class

通过断点可以看到有如下10种监听器

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.liquibase.LiquibaseServiceLocatorApplicationListener
org.springframework.boot.logging.ClasspathLoggingApplicationListener
org.springframework.boot.logging.LoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer 

其中

ConfigFileApplicationListener监听器的核心作用就是读取application.yml配置文件内容 

1.1.5.配置应用的主方法所在类

this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

 2.运行SpringApplication的实例进行初始化操作

SpringApplication的实例创建好后调用run方法

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
        //开始计时(初始化需要花费多少时间)
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
        //配置headless
		configureHeadlessProperty();
        //实现类只有EventPublishingRunListener
        //通过SpringApplicationRunListeners广播
		SpringApplicationRunListeners listeners = getRunListeners(args);
        //收到广播的类执行相应的操作
		listeners.starting();
		try {
            //// 构造一个应用程序参数持有类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);

            // 配置环境模块
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);

            //配置Banner
			Banner printedBanner = printBanner(environment);

            // 创建Spring容器
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);

            // 广播出ApplicationReadyEvent事件给相应的监听器执行
			listeners.finished(context, null);

            //计时结束
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}

2.1run方法主要做的事有:

2.1.1.stopWatch.start()

开始计时初始化需要花费多少时间

2.1.2.Headless配置

configureHeadlessProperty();
	private void configureHeadlessProperty() {
		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
				SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
	}

 2.1.3.创建SpringApplicationRunListeners,发送广播

SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener

		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

 SpringApplicationRunListenesr类有5个方法,这五个方法调用的时间不同,发送了不同的事件

方法 何时调用 对应事件
starting
run方法执行时就调用 ApplicationStartedEvent
environmentPrepared
ApplicationContext创建之前并且环境信息准备好的时候调用 ApplicationEnvironmentPreparedEvent
contextPrepared
ApplicationContext创建好并且在source加载之前调用一次
contextLoaded
ApplicationContext创建并加载之后并在refresh之前调用 ApplicationPreparedEvent
finished
run方法结束之前调用 ApplicationReadyEvent或ApplicationFailedEvent

2.1.4.配置环境模块ConfigurableEnvironment     

配置一些环境信息。

ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);

创建应用程序的环境信息。如果是web程序,创建StandardServletEnvironment;否则,创建StandardEnvironment。

配置一些环境信息。比如profile,命令行参数。

广播出ApplicationEnvironmentPreparedEvent事件给相应的监听器执行

环境信息的校对

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 创建应用程序的环境信息。如果是web程序,创建StandardServletEnvironment;否则,创建StandardEnvironment
		ConfigurableEnvironment environment = getOrCreateEnvironment();

        // 配置一些环境信息。比如profile,命令行参数
		configureEnvironment(environment, applicationArguments.getSourceArgs());

        // 广播出ApplicationEnvironmentPreparedEvent事件给相应的监听器执行
		listeners.environmentPrepared(environment);

        // 环境信息的校对
		if (!this.webEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		return environment;
	}

2.1.5.配置Banner

是否在控制台上打印自定义的banner

Banner printedBanner = printBanner(environment);
private Banner printBanner(ConfigurableEnvironment environment) {
		if (this.bannerMode == Banner.Mode.OFF) {
			return null;
		}
		ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
				: new DefaultResourceLoader(getClassLoader());
		SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
				resourceLoader, this.banner);
		if (this.bannerMode == Mode.LOG) {
			return bannerPrinter.print(environment, this.mainApplicationClass, logger);
		}
		return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
	}

2.1.6.ConfigurableApplicationContext spring容器

2.1.6.1创建spring容器

context = createApplicationContext();

如果是web程序,那么构造org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext容器 ,否则构造org.springframework.context.annotation.AnnotationConfigApplicationContext容器

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {

                // 如果是web程序,那么构造org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext容器
                // 否则构造org.springframework.context.annotation.AnnotationConfigApplicationContext容器
				contextClass = Class.forName(this.webEnvironment
						? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
	}

2.1.6.2配置spring容器

设置Spring容器的环境信息。

回调方法,Spring容器创建之后做一些额外的事。

SpringApplication的的初始化器开始工作。

遍历调用SpringApplicationRunListener的contextPrepared方法。目前只是将这个事件广播器注册到Spring容器中。

把应用程序参数持有类注册到Spring容器中,并且是一个单例。

广播出ApplicationPreparedEvent事件给相应的监听器执行

prepareContext(context, environment, listeners, applicationArguments,printedBanner);
private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {

        // 设置Spring容器的环境信息
		context.setEnvironment(environment);

         // 回调方法,Spring容器创建之后做一些额外的事
		postProcessApplicationContext(context);

        // SpringApplication的的初始化器开始工作
		applyInitializers(context);

        // 遍历调用SpringApplicationRunListener的contextPrepared方法。目前只是将这个事件广播器注册到Spring容器中
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// 把应用程序参数持有类注册到Spring容器中,并且是一个单例
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[sources.size()]));

        // 广播出ApplicationPreparedEvent事件给相应的监听器执行
		listeners.contextLoaded(context);
	}

2.1.6.3刷新spring容器

refreshContext(context);
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();
	}

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				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();
			}
		}
	}

其中

prepareRefresh方法

  1. 设置Spring容器的启动时间,撤销关闭状态,开启活跃状态。
  2. 初始化属性源信息
  3. 验证环境信息里一些必须存在的属性

 obtainFreshBeanFactory方法

初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作

prepareBeanFactory方法

  1. 设置classloader用于加载bean,设置表达式解析器,设置资源编辑注册器
  2. 添加ApplicationContextAwareProcessor这个BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware这5个接口的自动注入。因为ApplicationContextAwareProcessor把这5个接口的实现工作做了
  3. 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去。ResourceLoader、ApplicationEventPublisher、ApplicationContext这3个接口对应的bean都设置为当前的Spring容器
  4. 注入一些其它信息的bean,比如environment、systemProperties等 

postProcessBeanFactory方法

提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess

不同的Spring容器做不同的操作。

 AnnotationConfigEmbeddedWebApplicationContext类

	@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 调用父类EmbeddedWebApplicationContext的实现
		super.postProcessBeanFactory(beanFactory);

        // 查看basePackages属性,如果设置了会使用ClassPathBeanDefinitionScanner去扫描basePackages包下的bean并注册
		if (this.basePackages != null && this.basePackages.length > 0) {
			this.scanner.scan(this.basePackages);
		}

        // 查看annotatedClasses属性,如果设置了会使用AnnotatedBeanDefinitionReader去注册这些bean
		if (this.annotatedClasses != null && this.annotatedClasses.length > 0) {
			this.reader.register(this.annotatedClasses);
		}
	}

父类EmbeddedWebApplicationContext

	@Override
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		beanFactory.addBeanPostProcessor(
				new WebApplicationContextServletContextAwareProcessor(this));
		beanFactory.ignoreDependencyInterface(ServletContextAware.class);
	}

 invokeBeanFactoryPostProcessors方法

激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor

在Spring容器中找出实现了BeanFactoryPostProcessor接口的processor并执行。Spring容器会委托给PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法执行。

registerBeanPostProcessors方法

注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor。

注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法。

从Spring容器中找出的BeanPostProcessor接口的bean,并设置到BeanFactory的属性中。之后bean被实例化的时候会调用这个BeanPostProcessor。

该方法委托给了PostProcessorRegistrationDelegate类的registerBeanPostProcessors方法执行

initMessageSource方法

初始化上下文中的资源文件,如国际化文件的处理等

initApplicationEventMulticaster方法

在Spring容器中初始化事件广播器,事件广播器用于事件的发布。

EventPublishingRunListener这个SpringApplicationRunListener会监听事件,

发生contextPrepared事件的时候EventPublishingRunListener会把事件广播器注入到BeanFactory中。

所以initApplicationEventMulticaster不再需要再次注册,只需要拿出BeanFactory中的事件广播器然后设置到Spring容器的属性中即可。

onRefresh方法

该方法中会去创建内嵌的Servlet容器,这里既是启动tomcat

private void createEmbeddedServletContainer() {
		EmbeddedServletContainer localContainer = this.embeddedServletContainer;
		ServletContext localServletContext = getServletContext();
		if (localContainer == null && localServletContext == null) {
			EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
			this.embeddedServletContainer = containerFactory
					.getEmbeddedServletContainer(getSelfInitializer());
		}
		else if (localServletContext != null) {
			try {
				getSelfInitializer().onStartup(localServletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context",
						ex);
			}
		}
		initPropertySources();
	}

registerListeners方法

把Spring容器内的时间监听器和BeanFactory中的时间监听器都添加的事件广播器中,并且广播出去。

finishBeanFactoryInitialization方法

实例化BeanFactory中已经被注册但是未实例化的所有实例(懒加载的不需要实例化)。即生成环境所需要的Bean。

实例化的过程各种BeanPostProcessor开始起作用。

finishRefresh方法

初始化生命周期处理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring启动的时候调用start方法开始生命周期,spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,启动后一直运行。
启动所有实现了Lifecycle接口的类。
通过spring的事件发布机制发布ContextRefreshedEvent事件。

2.1.7广播

listeners.finished(context, null);

2.1.8计时结束

stopWatch.stop();

猜你喜欢

转载自blog.csdn.net/jy02268879/article/details/86678184