Spring启动原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baijunzhijiang_01/article/details/84076230

从spring.factories文件中加载key对应的类的实例

读取spring.factories的key和value并通过反射选择性的创建一些实例的过程。源码如下:

spring.factories文件

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

# Application Context Initializers
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

# Application Listeners
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

读取spring.factories文件中


    // type一般是接口值,通常为spring.factories文件中的k值。
    // names 是key对应的value值的set集合。
    // 根据key,value来创建spring.factories中key对应的value中的每个类的实例。
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}


    // 根据key,value的值,通过反射创建spring.factories中key对应的value中的每个类的实例
	private <T> List<T> createSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				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;
	}

SpringBoot启动流程

一、首先实例化SpringApplication对象

  1. 初始化
  2. 使用SpringFactoriesLoader扫描META-INF/spring.factories目录下的初始化器和10个监听器
  3. 设置main类
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 推断webApplicationType
		// 推断逻辑: 存在这两个类javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext
		this.webApplicationType = deduceWebApplicationType();
		// 初始化6个初始化器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
	    // 初始化10个监听器器
		setListeners((Collection)	getSpringFactoriesInstances(ApplicationListener.class));
		// 设置main类
		this.mainApplicationClass = deduceMainApplicationClass();
	}
自动装载配置的过程
  1. 使用SpringFactoriesLoader扫描META-INF/spring.factories目录下的url
  2. 根据读到的url连接获取Resource文件
  3. PropertiesLoaderUtils从文件中获取Properties
  4. Properties转成set后,将Properties的value,然后将key和value放置到MultiValueMap的对象Result中
  5. 将result的数据更新到缓存cache中。
	// SpringFactoriesLoader的缓存
	private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();


				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					List<String> factoryClassNames = Arrays.asList(
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
					result.addAll((String) entry.getKey(), factoryClassNames);
				}

二、SpringApplication实例运行run方法

  1. 创建一个计时器stopwatch,分别打印各个阶段耗费时间
  2. 声明context和SpringBoot异常报告集合
  3. 配置java.awt.headless
  4. 实例化EventPublishingRunListener,用于在SpringAllication的run方法执行到不同阶段,发布相应的事件给相应的监听器
  5. 初始化默认应用参数类
  6. 准备Spring环境,根据webApplicationType值来创建WEB环境或者是Standard环境,同时初始化systemProperties
  7. 配置environment中spring.beaninfo.ignore的属性
  8. 打印 banner
  9. 根据webApplicationType实例化一个AnnotationConfigServletWebServerApplicationContext容器对象
  10. 异常报告集合
  11. 准备环境,包括(绑定environment对象到Context
  12. 刷新环境
  13. 计时器停止计时,监听器发布Context启动完成事件给其他的监听器
  14. 获取所有实现ApplicationRunner接口的类,获取所有实现CommandLineRunner的类根据其@Order进行排序,执行run()方法。
public ConfigurableApplicationContext run(String... args) {
        //1. 创建一个计时器
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置java.awt.headless
		configureHeadlessProperty();
		// 实例化一个监听器,用于在SpringAllication的run方法执行到不同阶段,发布相应的时间给相应的监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
		    // 初始化默认应用参数类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 准备Spring环境,根据webApplicationType值来创建WEB环境 或者是Standard环境,同时初始化systemProperties
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			
			//配置environment中spring.beaninfo.ignore的属性
			configureIgnoreBeanInfo(environment);
			// 打印 banner
			Banner printedBanner = printBanner(environment);
			//根据webApplicationType实例化一个AnnotationConfigServletWebServerApplicationContext对象
			context = createApplicationContext();
			
			// 异常报告集合
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 准备环境,包括(绑定environment对象到Context,
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			// 刷新环境
			refreshContext(context);
			// 刷新环境后
			afterRefresh(context, applicationArguments);
			// 计时器停止计时。
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			// 监听器发布Context启动完成事件给其他的监听器
			listeners.started(context);
			//获取所有实现ApplicationRunner接口的类,获取所有实现CommandLineRunner的类根据其@Order进行排序,执行run()方法。
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
		    // 发送消息,广播给监听器ApplicationReadyEvent事件。
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

准备环境

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 创建or配置the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置environment
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 通知EventPublishingRunListener事件监听器,去广播一个ApplicationEnvironmentPreparedEvent事件给所有的监听器
		listeners.environmentPrepared(environment);
		// 绑定environment到当前SpringApplication实例
		bindToSpringApplication(environment);
		//
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

准备容器

  1. 容器设置环境参数
  2. 后置处理容器( internalConfigurationAnnotationProcessor,internalAutowiredAnnotationProcessor, internalRequiredAnnotationProcessor, internalCommonAnnotationProcessor,internalEventListenerProcessor)
  3. 完成initializer的初始化
  4. 监听器发送事件给广播,广播给相应的监听器处理事件。
  5. 注册applicationArguments到容器的单例工场中
  6. 注册banner
  7. 监听器发送事件给广播,广播给相应的监听器处理事件。
  8. 装载bean到SpringIOC容器中。

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		// 应用初始化容器
		
		// BackgroundPreinitializer完成MessageConverterInitializer,MBeanFactoryInitializer,ValidationInitializer,JacksonInitializer,ConversionServiceInitializer初始化
		//ConfigurationWarningsApplicationContextInitializer 为Context添加一个bean工厂后置处理器
        //ContextIdApplicationContextInitializer 给ApplicationContext设置一个ID,注册到容器中
        //DelegatingApplicationContextInitializer 初始化器实际上将初始化的工作委托给context.initializer.classes环境变量指定的初始化器(通过类名)
        // ServerPortInfoApplicationContextInitializer,将自己作为一个监听器注册到Context中。监听EmbeddedServletContainerInitializedEvent类型的事件。然后将内嵌的Web服务器使用的端口给设置到ApplicationContext中。
        // SharedMetadataReaderFactoryContextInitializer 创建一个用于在ConfigurationClassPostProcessor和Spring Boot间共享的CachingMetadataReaderFactory。
		applyInitializers(context);
		// 监听器发送事件给广播,广播给相应的监听器处理事件。
		// 将listeners加入到容器的listeners属性中。
		// Loaded config file 'file:/D:/../target/classes/application.yml' 
		listeners.contextPrepared(context);
		// 如果开始打印日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		// 注册banner
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		// internalConfigurationAnnotationProcessor
        // internalAutowiredAnnotationProcessor
        // internalRequiredAnnotationProcessor
        // internalCommonAnnotationProcessor
        // internalEventListenerProcessor
        // internalEventListenerFactory
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 装载bean到SpringIOC容器中。
		load(context, sources.toArray(new Object[0]));
		// 监听器发送事件给广播,广播给相应的监听器处理事件。
		listeners.contextLoaded(context);
	}

后置处理容器,为容器设置beanNameGenerator,resourceLoader

	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(
					AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context)
						.setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context)
						.setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
	}


加载bean到容器中

  1. 创建bean定义加载器Loader
  2. 设置Loader的beanNameGenerator,resourceLoader,environment
  3. loader开始工作,装载bean
	/**
	 * Load beans into the application context.
	 * @param context the context to load beans into
	 * @param sources the sources to load
	 */
	protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug(
					"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		// 创建bean定义加载器Loader
		BeanDefinitionLoader loader = createBeanDefinitionLoader(
				getBeanDefinitionRegistry(context), sources);
		// 设置Loader的beanNameGenerator,resourceLoader,environment
		if (this.beanNameGenerator != null) {
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
			loader.setEnvironment(this.environment);
		}
		// loader开始工作,加载bean到容器中
		loader.load();
	}

回调callRunner()方法

获取所有实现ApplicationRunner接口的类,获取所有实现CommandLineRunner的类根据其@Order进行排序,执行run()方法。


	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<Object>();
        // 1.获取所有实现ApplicationRunner接口的类
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        // 2.获取所有实现CommandLineRunner的类
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        // 3.根据其@Order进行排序
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<Object>(runners)) {
            // 4.调用ApplicationRunner其run方法
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
            // 5.调用CommandLineRunner其run方法
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}


ApplicationRunner接口实现类

实现了这两个接口(ApplicationRunner,CommandLineRunner)的类将会在callRunner的时候执行

@Component
public class TestApplicationRunner implements ApplicationRunner {

  @Override public void run(ApplicationArguments args) throws Exception {
    System.out.println("服务启动,TestCommandLineRunner执行启动加载任务.start..");
    if (null != args) {
      System.out.println(args.getOptionNames());
    }
    System.out.println("服务启动,TestCommandLineRunner执行启动加载任务.end..");
  }
}

来自SpringBoot框架的馈赠

StopWatch(秒表):计时器工具类。一个对开始时间,结束时间记录操作的Java类


public class StopWatchTest {

  private Logger LOG = LoggerFactory.getLogger(getClass());

  @Test
  public void testStopWatch(){
    StopWatch watch = new StopWatch();

    try {
      watch.start("watch1");
      Thread.sleep(2000L);
      watch.stop();

      watch.start("watch2");
      Thread.sleep(1030L);
      watch.stop();

      LOG.info(watch.prettyPrint());

    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

}

猜你喜欢

转载自blog.csdn.net/baijunzhijiang_01/article/details/84076230