• SpringBoot启动流程分析之创建SpringApplication对象(一)


    SpringBoot启动流程分析之创建SpringApplication对象(一)

    目录:


    流程分析

    1、SpringApplication的构造方法

    来看一下在SpringApplication对象的构造方法中都做了哪些事。

    public SpringApplication(Class... primarySources) {
    	this(null, primarySources);
    }
     
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    	this.resourceLoader = resourceLoader;
            //判断primarySources不能为空
    	Assert.notNull(primarySources, "PrimarySources must not be null");
            //将primarySources放入SpringApplication的全局变量primarySources,Set集合中
    	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            //从类路径中推断应用程序类型放到SpringApplication的全局变量webApplicationType 
    	this.webApplicationType = WebApplicationType.deduceFromClasspath();
            //从META-INF/spring.factories文件中获取ApplicationContextInitializer接口的实现类并利用反射创建对象返回放入SpringApplication的全局变量initializers,List集合中
    	setInitializers((Collection) getSpringFactoriesInstances(
    				ApplicationContextInitializer.class));
            //同上,也是从META-INF/spring.factories文件中获取ApplicationListener接口的实现类并利用反射创建对象返回放入SpringApplication的全局变量listeners,List集合中
    	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            //通过获取当前调用栈,找到入口方法main所在的类,放入SpringApplication的全局变量mainApplicationClass 
    	this.mainApplicationClass = deduceMainApplicationClass();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1.1、推断应用程序类型
    private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
    			"org.springframework.web.context.ConfigurableWebApplicationContext" };
     
    private static final String WEBFLUX_INDICATOR_CLASS = "org."
    			+ "springframework.web.reactive.DispatcherHandler";
     
    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."
    			+ "web.servlet.DispatcherServlet";
     
    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
     
     
    static WebApplicationType deduceFromClasspath() {
            //ClassUtils.isPresent()从默认classloader中判断是否存在对应的类型
    	if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
    			&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
    			&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
    		return WebApplicationType.REACTIVE;
    	}
    	for (String className : SERVLET_INDICATOR_CLASSES) {
    		if (!ClassUtils.isPresent(className, null)) {
    			return WebApplicationType.NONE;
    		}
    	}
    	return WebApplicationType.SERVLET;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    推断逻辑是:

    先是判断默认的classloader中是否存在org.springframework.web.reactive.DispatcherHandler、且不存在org.springframework.web.servlet.DispatcherServlet、org.glassfish.jersey.servlet.ServletContainer,如果为true返回WebApplicationType.REACTIVE;

    然后循环String数组,判断如果默认的classloader中是否不存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,如果不存在,则认为不是web应用程序,返回WebApplicationType.NONE;

    最后是返回WebApplicationType.SERVLET。

    三种返回类型的解释如下:

        1、WebApplicationType.NONE:不是web应用程序
    
        2、WebApplicationType.SERVLET:基于servlet的Web应用程序运行
    
        3、WebApplicationType.REACTIVE:响应式的web应用程序
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1.2、设置Initializers

    传入参数是class类型即ApplicationContextInitializer.class,最终调用方法是getSpringFactoriesInstances,注意第二个参数,传的是一个空的Class数组,则加载所有的配置的类,方便后续给定限定类型的时候无需再次加载,直接从cache中读取。

    SpringFactoriesLoader.loadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader):这个方法,其作用是使用给定的类加载器从“META-INF/spring.factories”加载给定类型的工厂实现的完全限定类名,即得到所有ApplicationContextInitializer接口实现类的完全限定类名,去重。

    setInitializers((Collection) getSpringFactoriesInstances(
    				ApplicationContextInitializer.class));
     
    private  Collection getSpringFactoriesInstances(Class type) {
    		return getSpringFactoriesInstances(type, new Class[] {});
    	}
     
    private  Collection getSpringFactoriesInstances(Class type,
    		Class[] parameterTypes, Object... args) {
    	ClassLoader classLoader = getClassLoader();
    	// Use names and ensure unique to protect against duplicates
            //得到所有ApplicationContextInitializer接口的实现类
    	Set names = new LinkedHashSet<>(
    			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            //创建所有ApplicationContextInitializer接口实现类的实例
    	List instances = createSpringFactoriesInstances(type, parameterTypes,
    			classLoader, args, names);
            //根据order排序
    	AnnotationAwareOrderComparator.sort(instances);
    	return instances;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    随后当做参数传递到createSpringFactoriesInstances方法中,这个方法主要作用就是根据传入的type类型,parameterTypes参数类型(空Class数组)以及得到的完全限定类名集合创建对象实例,其中getDeclaredConstructor方法作用是得到指定参数类型的构造方法,parameterTypes为空数组即的得到的就是默认构造方法。构造方法基本都是空的,所以无需关心创建Initializers实例的时候在构造方法中执行了什么操作。这些对象的initialize方法会在后面的run方法中被调用。

    @SuppressWarnings("unchecked")
    private  List createSpringFactoriesInstances(Class type,
    		Class[] parameterTypes, ClassLoader classLoader, Object[] args,
    		Set names) {
            //new 一个跟检索出来的接口实现类相同size的List
    	List instances = new ArrayList<>(names.size());
    	for (String name : names) {
    		try {
                            //通过类加载器加载类
    			Class instanceClass = ClassUtils.forName(name, classLoader);
                            //判断是否为ApplicationContextInitializer接口的实现类
    			Assert.isAssignable(type, instanceClass);
                            //得到指定参数类型的构造方法
    			Constructor constructor = instanceClass
    					.getDeclaredConstructor(parameterTypes);
                            //创建对象
    			T instance = (T) BeanUtils.instantiateClass(constructor, args);
                            //放到List中,返回
    			instances.add(instance);
    		}
    		catch (Throwable ex) {
    			throw new IllegalArgumentException(
    					"Cannot instantiate " + type + " : " + name, ex);
    		}
    	}
    	return instances;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    spring.factories文件内容中Initializers如下。

    在这里插入图片描述
    在这里插入图片描述

    1.3、设置Listener

    这一步跟上面设置Initializers执行的操作是一样的。spring.factories文件内容中Listener如下。
    在这里插入图片描述

    整理一下Listener对应的Event

    监听器事件类型
    BackgroundPreinitializerApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ApplicationStartedEvent、ApplicationReadyEvent、ApplicationFailedEvent、ApplicationContextInitializedEvent
    ClearCachesApplicationListenerContextRefreshedEvent
    ParentContextCloserApplicationListenerParentContextAvailableEvent
    FileEncodingApplicationListenerApplicationEnvironmentPreparedEvent
    AnsiOutputApplicationListenerApplicationEnvironmentPreparedEvent
    ConfigFileApplicationListenerApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent
    DelegatingApplicationListenerApplicationEnvironmentPreparedEvent
    ClasspathLoggingApplicationListenerApplicationEnvironmentPreparedEvent、ApplicationFailedEvent
    LoggingApplicationListenerApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ContextClosedEvent、ApplicationFailedEvent
    LiquibaseServiceLocatorApplicationListenerApplicationStartingEvent
    1.4、推断main方法所在类

    StackTraceElement数组包含了StackTrace(堆栈轨迹)的内容,通过遍历它可以得到当前方法以及其定义类、调用行数等信息。
    在这里插入图片描述

    private Class deduceMainApplicationClass() {
    	try {
                    //获取StackTraceElement数组,也就是这个栈的信息。
    		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
    		for (StackTraceElement stackTraceElement : stackTrace) {
    			if ("main".equals(stackTraceElement.getMethodName())) {
                                    //stackTraceElement.getClassName(),得到定义类,即main方法所在类
    				return Class.forName(stackTraceElement.getClassName());
    			}
    		}
    	}
    	catch (ClassNotFoundException ex) {
    		// Swallow and continue
    	}
    	return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    SpringApplication对象的创建过程就完成了。

    Springboot 启动还可以使用流式API SpringApplicationBuilder的方式启动:

    @SpringBootApplication
    public class RegisterApplication {
    	public static void main(String[] args) {
    		new SpringApplicationBuilder(RegisterApplication.class)
    				// 设置当前应用类型
    				.web(WebApplicationType.SERVLET)
    				// 设置 banner 横幅打印方式、有关闭、日志、控制台
    				.bannerMode(Banner.Mode.OFF)
    				// 设置自定义的 banner
    				.banner()
    				// 追加自定义的 initializer 到集合中 
    				.initializers()
    				// 追加自定义的 listeners 到集合中
    				.listeners()
    				.run(args);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    《机器学习实战》8.预测数值型数据:回归
    弹性伸缩:高可用架构利器(架构+算法+思维)
    centos7安装mysql8.x的注意事项,与5.x版本有许多不同
    Vue源码篇
    set容器 集合 常用 API操作 (只读迭代器)
    【翻译】Raft 共识算法:概览
    Java-面向对象的特征之一:封装
    【云原生】Java设计模式8,校验、审批流程改善神器,责任链模式
    Linux 零拷贝splice函数
    CentOs程序环境准备
  • 原文地址:https://blog.csdn.net/lvyuanj/article/details/138597573