Spring Boot 之SpringApplication准备阶段以及运行阶段

SpringApplication类的直接作用是在main方法中通过自有的run方法启动spring应用

一般的,在idea新建spring boot 工程之后会有一个启动类,执行main方法也就启动了spring应用

@SpringBootApplication
public class HouseWebApplication {

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

}

基础技术以及衍生技术

Spring Framework

Spring 模式注解
Spring 应用上下文
Spring工厂加载机制
Spring应用上下文初始器
Spring Environment抽象
Spring应用事件/监听

构建在SpringBoot之上的技术

SpringApplication
SpringApplication Build API
SpringApplication 运行监听器
SpringAppliction 参数
SpringApplication 故障分析
SpringBoot应用事件/监听

SpringApplication 准备阶段

SpringApplication的准备阶段也可以说是构造器阶段,可以来看一看源码

    public SpringApplication(Class... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        
        // 第一部分 配置源
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        
		// 第二部分 推断Web应用类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        
   	 	// 第三部分 应用上下文初始器 和 应用事件监听器   
  		this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

		// 第四部分 :推导引用主类
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

配置Spring Bean 的源

可以看到 如上的引导类配置了一个模式注解 @SpringBootApplication
在这里插入图片描述
显而易见 可以通过JAVA配置Class 这也是Spring的模式注解.

Java 配置 Class 或 XML 上下文配置文件集合,用于 Spring Boot BeanDefinitionLoader 读取 ,并且将配置源解析加载为
Spring Bean 定义
数量:一个或多个以上

  • Java 配置 Class
    用于 Spring 注解驱动中 Java 配置类,大多数情况是 Spring 模式注解所标注的类,如 @Configuration 。

  • XML 上下文配置文件
    用于 Spring 传统配置驱动中的 XML 文件。

SpringApplication中有setSource的方法
我们可以看到注释中的英文解释,参数可以类名,包名以及XML源路径。

Spring Boot 通过BeanDefinitionLoader 中的构造方法分别加载两种数据源

	/**
	 * Set additional sources that will be used to create an ApplicationContext. A source
	 * can be: a class name, package name, or an XML resource location.
	 * <p>
	 * Sources set here will be used in addition to any primary sources set in the
	 * constructor.
	 * @param sources the application sources to set
	 * @see #SpringApplication(Class...)
	 * @see #getAllSources()
	 */
	public void setSources(Set<String> sources) {
		Assert.notNull(sources, "Sources must not be null");
		this.sources = new LinkedHashSet<>(sources);
	}

/**
 * Create a new {@link BeanDefinitionLoader} that will load beans into the specified
 * {@link BeanDefinitionRegistry}.
 * @param registry the bean definition registry that will contain the loaded beans
 * @param sources the bean sources
 */
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
   Assert.notNull(registry, "Registry must not be null");
   Assert.notEmpty(sources, "Sources must not be empty");
   this.sources = sources;
   //JAVA 配置CLASS 
   this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
   //XML配置 
   this.xmlReader = new XmlBeanDefinitionReader(registry);
   if (isGroovyPresent()) {
      this.groovyReader = new GroovyBeanDefinitionReader(registry);
   }
   this.scanner = new ClassPathBeanDefinitionScanner(registry);
   this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

案例 :自定义读取配置源

public class SpringApplicationBootstrap {

    public static void main(String[] args) {
//        SpringApplication.run(ApplicationConfiguration.class,args);

        Set<String> sources = new HashSet();
        // 配置Class 名称
        sources.add(ApplicationConfiguration.class.getName());
        SpringApplication springApplication = new SpringApplication();
        springApplication.setSources(sources);
        springApplication.run(args);

    }

    @SpringBootApplication
    public static class ApplicationConfiguration {

    }

}

推断Web应用类型

根据当前应用ClassPath中是否存在相关实现类来推断Web应用的类型,包括:

  1. Web Reactive: WebApplication.REACTIVE
  2. Web Servlet: WebApplication.SERVLET
  3. 非Web:WebApplication.NONE
    参考方法 :org.springframework.boot.WebApplicationType#deduceFromClasspath
	//默认Web应用的类型为servlet
    static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }

            return SERVLET;
        }
    }

当然可以在启动类中指定WebApplicationType 的类型

推断引导类

在这里插入图片描述

根据Main线程执行堆栈判断实际的引导类;
不需要主动传递main,通过遍历推断方式来推导。

参考方法:org.springframework.boot.SpringApplication#deduceMainApplicationClass

    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
            ;
        }

        return null;
    }

加载应用上下文初始器(ApplicationContextInitializer)

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

org.springframework.boot.SpringApplication#getSpringFactoriesInstances

实现:
在这里插入图片描述
技术:
1、实现类:org.springframework.core.io.support.SpringFactoriesLoader
2、配置资源:META-INF/spring.factories (源码可见)
3、排序:org.springframework.core.annotation.AnnotationAwareOrderComparator#sort(java.util.List<?>)

加载 SpringApplication 运行监听器( SpringApplicationRunListeners )

利用 Spring 工厂加载机制,读取 SpringApplicationRunListener 对象集合,并且封装到组合类
SpringApplicationRunListeners
加载方式和加载应用上下文初始器一样
在这里插入图片描述

SpringApplication运行阶段

所谓的运行阶段,也就是run方法执行,可以先看一下源码

/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 *
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		// 1.计时工具
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		// 声明上下文
		ConfigurableApplicationContext context = null;
		// 声明异常报告集合
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

		// 2.设置headless模式 就是设置系统属性java.awt.headless(不需要关注)
		configureHeadlessProperty();

		// 3.使用工厂方法模式 加载SpringApplication运行监听器(SpringApplicationRunListeners)
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 使用组合对象的设计模式 迭代的执行starting()
		listeners.starting();
		try {
			// 获取默认的应用参数 (不关注)
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 4.创建配置Environment(不关注)
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			// 打印Banner(不关注)
			Banner printedBanner = printBanner(environment);
			// 5.创建spring-boot上下文
			context = createApplicationContext();

			// 获取启动错误报告实例
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[]{ConfigurableApplicationContext.class}, context);
			// 6.上下文启动之前准备
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			// 7.刷新上下文
			refreshContext(context);
			// 8.刷新后处理
			afterRefresh(context, applicationArguments);
			// 计时结束
			stopWatch.stop();

			// 打印日志
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}

			// 监听spring上下文,此时上下文已启动,Spring Bean已初始化完成
			listeners.started(context);
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
		// 启动运行监听
		try {
			listeners.running(context);
		} catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
  • 加载:SpringApplication 运行监听器

  • 运行:SpringApplication 运行监听器

    创建:应用上下文,Environment
    失败:故障分析报告
    回调:CommandLineRunner,ApplicationRunner
    
  • 监听:SpringBoot事件,Spring事件

加载SpringApplication运行监听器(SpringApplicationRunListeners)

利用Spring工厂加载机制,读取SpringApplicationRunListener对象集合,并且封装到组合类SpringApplicationRunListeners

/**
 * Listener for the {@link SpringApplication} {@code run} method.
 * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
 * and should declare a public constructor that accepts a {@link SpringApplication}
 * instance and a {@code String[]} of arguments. A new
 * {@link SpringApplicationRunListener} instance will be created for each run.
 *
 * @author Phillip Webb
 * @author Dave Syer
 * @author Andy Wilkinson
 */
public interface SpringApplicationRunListener {

	/**
	 * Spring刚启动
	 */
	void starting();

	/**
	 * ConfigurableEnvironment 准备妥当,允许将其调整
	 */
	void environmentPrepared(ConfigurableEnvironment environment);

	/**
	 * ConfigurableApplicationContext 准备妥当,允许将其调整
	 */
	void contextPrepared(ConfigurableApplicationContext context);

	/**
	 * ConfigurableApplicationContext 已装载,但仍未启动
	 */
	void contextLoaded(ConfigurableApplicationContext context);

	/**
	 * ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成
	 */
	void started(ConfigurableApplicationContext context);

	/**
	 * Spring 应用正在运行
	 */
	void running(ConfigurableApplicationContext context);

	/**
	 * Spring 应用运行失败
	 */
	void failed(ConfigurableApplicationContext context, Throwable exception);

}

监听SpringBoot事件,Spring事件

SpringBoot通过SpringApplicationRunListener 实现类EventPublishingRunlistener 利用Spring Framework 事件API,广播SpringBoot事件

在这里插入图片描述

SpringFramework 事件/监听器编程模型

Spring应用事件
    普通应用事件:ApplicationEvent
    应用上下文事件:ApplicationContextEvent

Spring应用监听器
    接口编程模型:ApplicationListener
    注解编程模型:EventListener

Spring应用事件广播器
    接口ApplicationEventMulticaster
    实现类 SimpleApplicationEventMulticaster
        执行模式: 同步或者异步
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

   @Nullable
   private Executor taskExecutor;

   @Nullable
   private ErrorHandler errorHandler;
    
    
        @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            //异步执行
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            //同步执行
            else {
                invokeListener(listener, event);
            }
        }
    }

创建 Spring 应用上下文( ConfigurableApplicationContext )

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

Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
Web Servlet: AnnotationConfigServletWebServerApplicationContext
非 Web: AnnotationConfigApplicationContext

创建 Environment

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

Web Reactive: StandardEnvironment
Web Servlet: StandardServletEnvironment
非 Web: StandardEnvironment
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值