二、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()ApplicationStartingEvent1.5
environmentPrepared(ConfigurableEnvironment)ApplicationEnvironmentPreparedEvent1.0
contextPrepared(ConfigurableApplicationContext)
contextLoaded(ConfigurableApplicationContext)ApplicationPreparedEvent1.0
started(ConfigurableApplicationContext)ApplicationStartedEvent2.0
running(ConfigurableApplicationContext)ApplicationReadyEvent2.0
failed(ConfigurableApplicationContext,Throwable)ApplicationFailedEvent1.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);
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三师兄东流

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值