SpringApplication类的直接作用是在main方法中通过自有的run方法启动spring应用。
具体的run方法为:
public static ConfigurableApplicationContext run(Object source, String... args)
该方法最终会把source传给SpringApplication的构造方法中的初始化方法:
private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
该初始化方法一共做了以下几件事:
一、判断sourse长度
首先,如果sources长度大于0(即是数组或容器),则把这些sources全部传到内置的hashset中。由此可见,run方法的第一个参数中是可以传入多个source的。
二、判断web环境是否正确
this.webEnvironment = deduceWebEnvironment();
判断整个环境中是否存在javax.servlet.Servlet类和
org.springframework.web.context.ConfigurableWebApplicationContext类这两个类,如果存在话,则表示web环境正常,返回true,否则返回false。
三、设置Initializers
首先会调getSpringFactoriesInstances方法加载ApplicationContextInitializer类,这个方法最终会运行SpringFactoriesLoader.loadFactoryNames方法,这个方法的目的是从“META-INF/spring.factories文件”中加载配置。
可实际上我们一般在开发springboot项目时并没有创建过spring.factories文件,可还是自动加载了各种配置,原因如下:
在项目的main方法上一般都会设置@SpringBootApplication注解,@SpringBootApplication注解中包含了多个注解,其中有一个@EnableAutoConfiguration注解尤为重要。
通过@EnableAutoConfiguration注解会将org.springframework.boot.autoconfigure.EnableAutoConfiguration这个包作为查找META-INF/spring.factories文件的根目录,即spring.factories文件会从org.springframework.boot.autoconfigure.EnableAutoConfiguration包中查找。而这个包里面自然有已经写好的spring.factories文件了。
整个springboot中会看到很多@Enable开头的注解,所有的@Enable开头的注解的作用都是通过@Import将“特定的bean”加载到Ioc容器中。@EnableAutoConfiguration的作用就是通过SpringFactoriesLoader将所有的标注了@Configuration注解的类加载(既有boot.autoconfigure包自带的标注了@Configuration的类,也可以是自己创建的标注了@Configration的类,都会被作为配置加载)
四、设置Listeners
listener的加载与之前的Initializers加载同理,都是寻找META-INF/spring.factories文件,然后被@EnableAutoConfiguration注解把路径转移到了org.springframework.boot.autoconfigure.EnableAutoConfiguration/META-INF/spring.factories中,之后就能找到已经提前写好的相关监听配置了。
五、设置mainApplicationClass
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; }
该方法是通过遍历栈,找到main方法,从而找到启动类,并将其赋值给mainApplicationClass属性。