关联文章:
SpringBoot源码解析三部曲(一)——自动配置
SpringBoot源码解析三部曲(三)——运行流程.
目录
- 1、SpringApplication实例化简介
- 2、SpringApplication实例化流程
- 2.1、推断Web应用类型
- 2.2、ApplicationContextInitializer加载
- 2.3、ApplicationListener加载
- 2.4、推断入口类
1、SpringApplication实例化简介
常见启动入口类示例:
1
2
3
4
5
6
7
8
9@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class SpringBootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringBootDemoApplication.class, args); } }
启动入口类中,主要通过SpringApplication的静态方法-run方法进行SpringApplication类的实例化操作,然后再使用实例化对象调用另一个run方法来完成整个项目的初始化和启动。
SpringApplication源码中run方法:
1
2
3
4
5
6
7
8
9
10// 参数primarySource为加载的主要资源类,通常就是Spring Boot的入口类 // 参数args为传递给应用程序的参数信息 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
2、SpringApplication实例化流程
核心流程如图所示:
从图中可以看出,在SpringApplication对象实例化的过程中,主要做了3件事:参数赋值给成员变量、应用类型及方法推断、ApplicationContext相关内容加载及实例化。源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } @SuppressWarnings({ "unchecked", "rawtypes" }) public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { // 赋值成员变量resourceLoader this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 赋值成员变量primarySources this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 推断Web应用类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 加载并初始化ApplicationContextInitializer及相关实现类 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 加载并初始化ApplicationListener及相关实现类 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 推断main方法Class类 this.mainApplicationClass = deduceMainApplicationClass(); }
SpringApplication的核心构造方法有两个参数,第一个为ResourceLoader,ResourceLoader为资源加载的接口,默认采用的是DefaultResourceLoader。第二个为Class<?>… primarySources,默认传入Spring Boot入口类。作为项目引导类,此参数传入的类需要满足一个条件,就是被注解@EnableAutoConfiguration或其组合注解标注。
2.1、推断Web应用类型
赋值完成员变量后,接下来是推断Web应用类型,源码如下:
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43public enum WebApplicationType { // 非Web应用 NONE, // 基于SERVLET的Web类型 SERVLET, // 基于REACTIVE的Web类型 REACTIVE; 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"; // 基于classpath中类是否存在来进行类型推断,即判断指定的类是否存在于classpath下,根据判断结果进行组合推断应用类型 static WebApplicationType deduceFromClasspath() { // isPresent的核心机制是通过反射创建指定的类,根据在创建过程中是否抛出异常来判断该类是否存在 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { // 当DispatcherHandler存在,并且DispatcherServlet和ServletContainer都不存在,则Web类型为REACTIVE return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { // 当Servlet或ConfigurableWebApplicationContext不存在时,则为非Web应用 if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } // 走到这里说明,Servlet和ConfigurableWebApplicationContext都存在 return WebApplicationType.SERVLET; } }
2.2、ApplicationContextInitializer加载
Spring Boot使用的容器为ConfigurableApplicationContext,ApplicationContextInitializer是Spring IOC容器提供的一个接口,它是一个回调接口,主要用于用户在ConfigurableApplicationContext类型(或其子类型)的ApplicationContext做refresh方法调用刷新之前,对ConfigurableApplicationContext实例做进一步的设置或处理。
ApplicationContextInitializer接口源码为:
1
2
3
4public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { void initialize(C var1); }
ApplicationContextInitializer接口的initialize方法主要是为了初始化指定的应用上下文。而对应的上下文由参数传入,参数为ConfigurableApplicationContext的子类。
ApplicationContextInitializer的加载分为两个步骤:获取相关实例和设置实例。对应的方法分别为getSpringFactoriesInstances、setInitializers,getSpringFactoriesInstances源码如下:
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } /** * 用来获取spring.factories配置文件中的相关类, 并进行实例化操作 */ private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { // 获取类加载器 ClassLoader classLoader = getClassLoader(); // 加载META-INF/spring.factories中对应的配置, 并将结果存储于Set中, 方便去重 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 创建实例 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 排序 AnnotationAwareOrderComparator.sort(instances); return instances; } /** * 实例化注册类 */ @SuppressWarnings("unchecked") private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<>(names.size()); // 遍历加载到的类名(全限定类名) for (String name : names) { try { // 使用全路径类名通过反射获取class对象 Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); // 获取有参构造器 Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); // 执行构造函数获取实例 T instance = (T) BeanUtils.instantiateClass(constructor, args); // 将实例加入到返回结果中 instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex); } } return instances; }
getSpringFactoriesInstances方法依然是通过SpringFactoriesLoader类中的loadFactoryNames方法来获得META-INF/spring.factories文件中注册的对应配置,获取到这些配置类的全限定类名之后,通过反射生成实例。ApplicationContextInitializer的相关配置如下:
1
2
3
4
5
6
7org.springframework.context.ApplicationContextInitializer= org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer, org.springframework.boot.context.ContextIdApplicationContextInitializer, org.springframework.boot.context.config.DelegatingApplicationContextInitializer, org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer, org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
在完成配置类集合和实例化操作之后,调用setInitializers方法将实例化的集合添加到SpringApplication的成员变量initializers中,源码如下:
1
2
3
4
5public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) { // 新建一个List,并将其复制给SpringApplication的成员变量initializers this.initializers = new ArrayList<>(initializers); }
2.3、ApplicationListener加载
完成了ApplicationContextInitializer的加载之后,便会进行ApplicationListener的加载。它的常见应用场景为:当容器初始化完成之后,需要处理一些如数据的加载、初始化缓存、特定任务的注册等操作。在此阶段,更多的是用于ApplicationContext管理Bean过程的场景。
Spring事件传播机制是基于观察者模式实现的。比如,在ApplicationContext管理Bean生命周期的过程中,会将一些改变定义为事件(ApplicationEvent),ApplicationContext通过ApplicationListener监听ApplicationEvent,当事件被发布之后,ApplicationListener用来对事件做出具体的操作。
ApplicationListener的这个配置和加载流程和ApplicationContextInitializer完全一致,也是通过SpringFactoriesLoader的loadFactoryNames方法获得META-INF/spring.factories中对应的配置,然后再进行实例化,这里不再赘述。
看一下ApplicationListener使用,ApplicationListener源码如下:
1
2
3
4
5
6@FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { // onApplicationEvent方法用于处理应用程序事件,参数event为ApplicationEvent的子类,是具体接收到的事件 void onApplicationEvent(E event); }
从源码中可以看出,ApplicationListener接口和ApplicationEvent类配合使用,如果容器中存在ApplicationListener的Bean,当ApplicationContext调用publishEvent方法时,发布对应的事件,对应的Bean会被触发,即onApplicationEvent方法会被执行。
举个例子,当Application被初始化或刷新时,会触发ContextRefreshedEvent事件,可以实现一个ApplicationListener来监听该事件:
1
2
3
4
5
6
7
8
9
10// 对该类进行Bean的实例化 @Component public class ListenerDemo implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { // 打印容器里面初始化了多少个Bean System.out.println("监听器获得容器中初始化Bean的数量: " + contextRefreshedEvent.getApplicationContext().getBeanDefinitionCount()); } }
2.4、推断入口类
SpringApplication实例化的最后一步就是推断入口类,通过deduceMainApplicationClass进行推断,源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18private Class<?> deduceMainApplicationClass() { try { // 获取栈元素数组 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); // 遍历栈元素数组 for (StackTraceElement stackTraceElement : stackTrace) { // 匹配第一个main方法, 并返回 if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // 如果发生异常,忽略改一次,并继续执行 } return null; }
该方法先创建一个运行时异常,然后获得栈数组,遍历栈数组,判断类的方法中是否包含main方法。一个被匹配到的类会通过Class.forName方法创建对象,并将其返回,最后在上层方法中将对象赋值给SpringApplication的成员变量mainApplicationClass。
最后
以上就是负责小伙最近收集整理的关于SpringBoot源码解析三部曲(二)——SpringApplication实例化1、SpringApplication实例化简介2、SpringApplication实例化流程的全部内容,更多相关SpringBoot源码解析三部曲(二)——SpringApplication实例化1、SpringApplication实例化简介2、SpringApplication实例化流程内容请搜索靠谱客的其他文章。
发表评论 取消回复