二、Spring Boot 核心之理解SpringApplication

一、SpringApplication基本使用

运行
        // 运行
        SpringApplication.run(SpringApplicationApplication.class, args);
自定义
        // 自定义
        SpringApplication springApplication = new SpringApplication(SpringApplicationApplication.class);
        springApplication.setBannerMode(Banner.Mode.CONSOLE);
        // 设置运行环境    【NONE:非Web环境,SERVLET:WebServlet,REACTIVE:WebFlux异步】
        springApplication.setWebApplicationType(WebApplicationType.NONE);
        // 设置配置文件环境
        springApplication.setAdditionalProfiles("prod");
        // Headless模式是系统的一种配置模式。在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式。
        // 在开发图形化界面时可设置为false
        springApplication.setHeadless(true);
通过 SpringApplicationBuilder API 调整
        new SpringApplicationBuilder(SpringApplicationApplication.class)
                .bannerMode(Banner.Mode.CONSOLE)
                .web(WebApplicationType.NONE)
                .profiles("prod")
                .headless(true);

二、SpringApplication构造阶段

核心代码如下,以下小节均基于此构造函数分析
	/**
	 * Create a new {@link SpringApplication} instance. The application context will load
	 * beans from the specified primary sources (see {@link SpringApplication class-level}
	 * documentation for details. The instance can be customized before calling
	 * {@link #run(String...)}.
	 * @param resourceLoader the resource loader to use
	 * @param primarySources the primary bean sources
	 * @see #run(Class, String[])
	 * @see #setSources(Set)
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
配置 Spring Boot Bean 源

Java 配置 Class 或 XML 上下文配置文件集合,用于 Spring Boot BeanDefinitionLoader 读取 ,并且将配置源解析加载为

  • Spring Bean 定义
    • 数量:一个或多个以上
启动配置Bean源
  • 第一种@SpringBootApplication注解标注的类,不仅限于本身,eg:

        public static void main(String[] args) {
            // 运行
            SpringApplication.run(ApplicationApplication.class, args);
        }
    
        @SpringBootApplication
        public static class ApplicationApplication{
    
        }
    
  • 第二种通过setSources配置:``,eg:

setSources源码:
在这里插入图片描述

public static void main(String[] args) {
        // 运行
//        SpringApplication.run(ApplicationApplication.class, args);
        Set<String> sourceSet = new HashSet<>();
        sourceSet.add(ApplicationApplication.class.getName());
        SpringApplication springApplication = new SpringApplication();
        //  sources can be:a class name, package name, or an XML resource location.
        // 源可以是一个类的名字、包名、本地xml文件路径
        springApplication.setSources(sourceSet);
        springApplication.run(args);
    }

    @SpringBootApplication
    public static class ApplicationApplication{

    }
推断 Web 应用类型

根据是否存在某个类来判断当前应用类型:WebServlet、WebFlux和普通应用

  1. org.springframework.boot.SpringApplication#SpringApplication

  2. SpringApplication:this.webApplicationType = WebApplicationType.deduceFromClasspath();

  3. org.springframework.boot.WebApplicationType#deduceFromClasspath方法代如下:

    private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
    		"org.springframework.web.context.ConfigurableWebApplicationContext" };
    
    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
    
    private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
    
    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
    
    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
    
    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
    
    static WebApplicationType deduceFromClasspath() {
    	if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) // 1
    			&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
    		return WebApplicationType.REACTIVE;
    	}
    	for (String className : SERVLET_INDICATOR_CLASSES) { // 2
    		if (!ClassUtils.isPresent(className, null)) {
    			return WebApplicationType.NONE;
    		}
    	}
    	return WebApplicationType.SERVLET; // 3
    }
    

由以上代码可知:

  1. web.reactive和web.servlet共存时,返回SERVLET应用环境,只存在WEBFLUX_INDICATOR_CLASS时,才为WebApplicationType.REACTIVE
  2. SERVLET_INDICATOR_CLASSES中类均不存在时,返回return WebApplicationType.NONE
  3. 默认返回WebApplicationType.SERVLET
加载应用上下文初始器 ( ApplicationContextInitializer ) & 加载应用事件监听器( ApplicationListener )

利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer 实现类,并排序对象集合。

  • 实现类: org.springframework.core.io.support.SpringFactoriesLoader
  • 配置资源: META-INF/spring.factories
  • 排序: AnnotationAwareOrderComparator#sort
    SpringApplicationg构造函数中加载代码如下:
  1. org.springframework.boot.SpringApplication#SpringApplication
  2. SpringApplication:
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

实际调用方法:

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 通过工厂机制获取实现类名字 SpringFactoriesLoader->META-INF/spring.factories->names 
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//  创建实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		// 根据order排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
推断引导类(Main Class)

通过异常堆栈来判断符合条件(包含main方法)的类

  1. org.springframework.boot.SpringApplication#SpringApplication
  2. SpringApplication:this.mainApplicationClass = deduceMainApplicationClass();
  3. 源码和debug->stackTrace截图如下
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;
	}

在这里插入图片描述

三、SpringApplication.run运行阶段

核心代码

public ConfigurableApplicationContext run(String... args) {
		// 计时器初始化
        StopWatch stopWatch = new StopWatch();
        // 开始计时
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        // 配置Headless参数
        this.configureHeadlessProperty();
        // 加载监听器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 运行监听器
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }
加载 SpringApplication 运行监听器( SpringApplicationRunListeners )

核心代码

        // 加载监听器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        // 运行监听器
        listeners.starting();

getRunListeners:

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        // 通过工厂机制加载SpringApplicationRunListener的实现类,实际即为:org.springframework.boot.context.event.EventPublishingRunListener
        // 加载完毕之后,构造SpringApplicationRunListeners组合对象(设计模式),
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }
运行 SpringApplication 运行监听器( SpringApplicationRunListeners )

SpringApplicationRunListener 监听多个运行状态方法:

监听方法 阶段说明 Spring Boot 起始版本
starting() Spring 应用刚启动 1.0
environmentPrepared(ConfigurableEnvironment) ConfigurableEnvironment 准备妥当,允许将其调整 1.0
contextPrepared(ConfigurableApplicationContext) ConfigurableApplicationContext 准备妥当,允许将其调整 1.0
contextLoaded(ConfigurableApplicationContext) ConfigurableApplicationContext 已装载,但仍未启动 1.0
started(ConfigurableApplicationContext) ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成 2.0
running(ConfigurableApplicationContext) Spring 应用正在运行 2.0
failed(ConfigurableApplicationContext,Throwable) Spring 应用运行失败 2.0
监听 Spring Boot 事件 / Spring 事件

Spring Boot 通过 SpringApplicationRunListener 的实现类 EventPublishingRunListener 利用 Spring Framework 事件API ,广播 Spring Boot 事件。

Spring Framework 事件/监听器编程模型
  • Spring 应用事件
    • 普通应用事件: ApplicationEvent
    • 应用上下文事件: ApplicationContextEvent
  • Spring 应用监听器
    • 接口编程模型: ApplicationListener
    • 注解编程模型: @EventListener
  • Spring 应用事广播器
    • 接口: ApplicationEventMulticaster
    • 实现类: SimpleApplicationEventMulticaster
    • 执行模式:同步或异步
EventPublishingRunListener 监听方法与 Spring Boot 事件对应关系
监听方法 Spring Boot 事件 Spring Boot 起始版本
starting() ApplicationStartingEvent 1.5
environmentPrepared(ConfigurableEnvironment) ApplicationEnvironmentPreparedEvent 1.0
contextPrepared(ConfigurableApplicationContext)
contextLoaded(ConfigurableApplicationContext) ApplicationPreparedEvent 1.0
started(ConfigurableApplicationContext) ApplicationStartedEvent 2.0
running(ConfigurableApplicationContext) ApplicationReadyEvent 2.0
failed(ConfigurableApplicationContext,Throwable) ApplicationFailedEvent 1.0

contextPrepared不对应事件,contextLoaded对应ApplicationPreparedEvent事件

创建 Environment

根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableEnvironment 实例:

  • Web Reactive: StandardEnvironment
  • Web Servlet: StandardServletEnvironment
  • 非 Web: StandardEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

prepareEnvironment:

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

getOrCreateEnvironment:

	private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

创建 Spring 应用上下文( ConfigurableApplicationContext )

根据准备阶段的推断 Web 应用类型创建对应的 ConfigurableApplicationContext 实例:

  • Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
  • Web Servlet: AnnotationConfigServletWebServerApplicationContext
  • 非 Web: AnnotationConfigApplicationContext
context = createApplicationContext();

createApplicationContext:

	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				// 根据构造阶段推断的 Web 应用类型来创建Spring应用上下文
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章