springboot run方法源码(一)-初始化及第三步

版本

本文采用的是springboot 1.5.9版本,没有使用2.X版本。
pom.xml里加入:

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

启动篇SpringApplication

建立一个springboot项目后,自动会新建一个项目的Application类

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

本篇的内容便是对这个run方法进行详细讲解。

顺着run进入源码,

    public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
        return (new SpringApplication(sources)).run(args);
    }

这里的参数sources就是我们的启动类SpringbootCodeReadApplication,可以看到这里new了类SpringApplication,因此接下来看看该类的构造方法。

    public SpringApplication(Object... sources) {
		...忽略
        this.initialize(sources);
    }

在这里忽略了不重要的代码,只保留了构造方法中最重要的initialize()方法。

初始化initialize()

    private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
		//1.判断当前环境是否是web环境,判断方法是检查classPath中是否
		//存在javax.servlet.Servlet类 &&存在ConfigurableWebApplicationContext类
        this.webEnvironment = this.deduceWebEnvironment();  
       //2.设置“META-INF/spring.factories”中ApplicationContextInitializer相关的Inializers到SpringApplication中
       	this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //3.设置“META-INF/spring.factories”中ApplicationListener相关的Listeners到SpringApplication中
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        //4.找到main方法所在的类。
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

第一步的作用是判断当前环境是否是web环境,因为本文在pom.xml中设置了spring-boot-starter-web,所以是web环境。
第二步和第三步是在SpringApplication中设置Inializers和ApplicationListener,两者都用到了getSpringFactoriesInstances()方法,接下来对这个方法进行讲解:

    private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    	//1.获取当前线程classLoader,用于后续通过反射加载类
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        //2.重点:获得type对应的类的名称(去重),下面会详细介绍
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        //3.在第二步获得类名后,使用反射来创建实例
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        //4.排序,不详细介绍
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

其中最关键的是第二步中的loadFactoryNames()方法:

 public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
 		//1.获取工厂的类名
        String factoryClassName = factoryClass.getName();

        try {
        	//2.从"META-INF/spring.factories"中获得urls
            Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
            ArrayList result = new ArrayList();
			//3. 遍历 urls
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                //4.获取url对应的properties
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                //5.从properties获取factoryClassName类包含的类名
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }

            return result;
        } catch (IOException var8) {
        ...}
    }

这段代码直接去看比较困难,需要结合debug来进行介绍。
从下图可以看到,假设我们正在获取ApplicationContextInitializer的Initializer,那么此时factoryClassName为org.springframework.context.ApplicationContextInitializer。中可以看到,urls中的其中一个元素为绝对路径XXX/spring.factories。

springboot run方法源码(一)-初始化及第三步_第1张图片

现在我们打开这个文件来看看。
springboot run方法源码(一)-初始化及第三步_第2张图片

可以看到,spring.factories中一共含有七个大类,其中绿框就是我们本次要找的ApplicationContextInitializer,同时,从红框中可以看到,ApplicationContextInitializer由四个小类组成。记住七和四这两个数字。

现在我们回到代码中,第四步获得properties实际上就是上面提到的七个大类极其对应的小类。
在这里插入图片描述
因为factoryClassName=ApplicationContextInitializer,所以第五步中factoryClassNames 为上面提到的四个小类。并在加到list后返回。

现在回到getSpringFactoriesInstances()方法,该方法还剩下第三步没讲:
第三步实际上就是使用java的反射机制,创建刚刚获得的类名对应的实例,代码如下:

    private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList(names.size());
        Iterator var7 = names.iterator();

        while(var7.hasNext()) {
            String name = (String)var7.next();
			//使用反射获得示例
            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            } catch (Throwable var12) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12);
            }
        }

        return instances;
    }

到此,getSpringFactoriesInstances()方法介绍完毕,现在我们回到最初的initialize()方法:

    private void initialize(Object[] sources) {
        if (sources != null && sources.length > 0) {
            this.sources.addAll(Arrays.asList(sources));
        }
		//1.判断当前环境是否是web环境,判断方法是检查classPath中是否
		//存在javax.servlet.Servlet类 &&存在ConfigurableWebApplicationContext类
        this.webEnvironment = this.deduceWebEnvironment();  
       //2.设置“META-INF/spring.factories”中ApplicationContextInitializer相关的Inializers到SpringApplication中
       	this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //3.设置“META-INF/spring.factories”中ApplicationListener相关的Listeners到SpringApplication中
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
         //4.找到main方法所在的类。
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

第三步与第二步是相同的操作,因此接下来只剩第四步没有介绍。
第四步其实就是找到main()方法所在的类,在本文中就是启动类SpringbootCodeReadApplication,代码如下:

    private Class<?> deduceMainApplicationClass() {
        try {
        	//1.从RuntimeException中获得所有堆栈
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                //找到main方法所在的类。
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
        }

        return null;
    }

至此,initialize()讲解完毕。


启动run():

run方法是本文最核心的篇章,并且该方法内容量很大,需要一点一点耐心分析。
首先,对整个run方法的具体步骤进行注释,一共有10几步,如下代码所示:

	/**
	 * 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 stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		//2. 设置系统属性java.awt.handless为true,
		//该属性表示系统可以在缺少显示设备、键盘或鼠标这些外设的情况下进行使用,本文不详细讲解。
		configureHeadlessProperty();
		//3. 获得runListeners,并启动(调用listener的onApplicationEvent方法)
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			//4. 创建DefaultApplicationArguments(该类含有args),然后调用prepareEnvironment()
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,					applicationArguments);
			//5. 打印banner
			Banner printedBanner = printBanner(environment);
			//6. 创建Springboot的上下文ApplicationContext
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
			//7. prepareContext
			prepareContext(context, environment, listeners, applicationArguments,printedBanner);
			//8. spring最出名的refresh()方法
			refreshContext(context);
			//9. TODO
			afterRefresh(context, applicationArguments);
			//10. TODO
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			return context;
		}
		catch (Throwable ex) {
			//11. 处理异常
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}

第三步 获得runListeners,并启动

现在对第三步进行分析,先进入getRunListeners(args):

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		//3.1 创建Class数组,该数组用于getSpringFactoriesInstances()方法中反射时找到对应的构造函数。
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 3.2 调用getSpringFactoriesInstances()获得SpringApplicationRunListener相关的listeners的实例
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

第一步先跳过,在第二步中可以看到,getRunListeners(args)实际上是通过调用getSpringFactoriesInstances()方法来获取SpringApplicationRunListener相关的listeners实例,。
getSpringFactoriesInstances()方法在上一章“初始化initialize() ”的已详细介绍,因此在这里不再重复介绍,只给出获得的结果:

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

可以看到,本次getSpringFactoriesInstances()获取到的是EventPublishingRunListener类。

现在我们回到第一步,之所以要创建{ SpringApplication.class, String[].class }这样一个数组,是因为反射时需要找到对应的构造方法Constructor,在下面的代码中可以看到,EventPublishingRunListener类拥有参数为(SpringApplication.class, String[].class)的构造方法,因此按该构造方法生成实例。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	...public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		//注意:这里会将initialize方法中存放的listeners提取出来
		//然后存放到this.initialMulticaster中
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
	...}

该构造方法有需要注意的点:这里会将initialize()方法中存放的listeners提取出来然后存放到this.initialMulticaster里。

接下来介绍启动监听器listeners.starting();
一直往下走,每一个监听器都会调用下面的代码:

	@Override
	@SuppressWarnings("deprecation")
	public void starting() {
		this.initialMulticaster
				.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
	}

接着:

    public void multicastEvent(ApplicationEvent event) {
        this.multicastEvent(event, this.resolveDefaultEventType(event));
    }

resolveDefaultEventType()方法不详细介绍,该方法的作用是创建一个ResolvableType类。接下来开始看multicastEvent()方法:

	@Override
	public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
		//上面已经创建了,所以type不会为null
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		//1. getApplicationListeners(event,type)是将EventPublishingRunListener中存放的listeners取出来,
		// 接着只保留将该event对应的listeners(判断方法的具体实现过于复杂,知道功能即可)并放到一个LinkedList中
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			//遍历listner,每个listener都创建线程,并调用invokeListener()
			Executor executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(new Runnable() {
					@Override
					public void run() {
						invokeListener(listener, event);
					}
				});
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

顺着invokeListener走,最后会到下面的代码,可以看到,开启listener其实也就是调用 onApplicationEvent(event) 方法:

	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		}
		catch (ClassCastException ex) {
			...}
	}

总结一下:获得runListeners是从spring.factories文件中获得SpringApplicationRunListener相关的listener,启动实际上是建立一个线程池,并调用onApplicationEvent(event)方法。


你可能感兴趣的:(springboot,源码)