SpringBoot之启动过程(1)——实例化SpringAppliction


spring启动过程的源码阅读分为两个文章记录,分别是实例化SpringApplication和run()方法。
本文讲的是实例化SpringApplication

1. 从main方法开始解读

新建了一个和很普通的springboot项目进入main方法中查看源码

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

1.1. SpringApplication实例化的时候,做了哪些事情?

SpringApplication.run(GenDemoApplication.class, args); 方法内部,实际上是实例化了一个SpringApplication 并调用了run方法。

废话少说,直接看源码。

	public class SpringApplication {
		// main方法从这里开始调用
		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);
		}
		public SpringApplication(Class<?>... primarySources) {
			this(null, primarySources);
		}
		
		// 看这里,构造方法在这里 ///
		// 看这里,构造方法在这里 ///
		// 看这里,构造方法在这里 ///
		// 执行 SpringApplication.run(GenDemoApplication.class, args);
		// 根据方法的调用链一路查看,实际调用了此构造方法方法,然后调用了此类中的run(args)方法
		@SuppressWarnings({ "unchecked", "rawtypes" })
		public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
			this.resourceLoader = resourceLoader; // 这里传入的是 null
			Assert.notNull(primarySources, "PrimarySources must not be null"); // 判空,传入的class对象不能是空(main方法中传入的是 GenDemoApplication.class )
			this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 用一个Set集合将GenDemoApplication.class 保存起来
			this.webApplicationType = deduceWebApplicationType(); // 此处会自动判断我们创建的应用是什么类型的,实际是根据我们应用的包而决定(看下文的源码解读)

			
			/// 重点阅读 /
			/// 重点阅读 /
			/// 重点阅读 /
			/// getSpringFactoriesInstances() ///
			// 此步骤其实是对 this.initializers 赋值,关键的代码是 getSpringFactoriesInstances(ApplicationContextInitializer.class)
			// 通过工厂实例 getSpringFactoriesInstances(ApplicationContextInitializer.class) 下面解读
			setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
			
			// 此步骤其实是对 this.listeners 赋值,关键的代码是 getSpringFactoriesInstances(ApplicationListener.class)
			// 通过工厂实例 getSpringFactoriesInstances(ApplicationListener.class) 下面解读
			setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 

			// 找出执行main方法的类,并赋值
			this.mainApplicationClass = deduceMainApplicationClass(); 
		}
	}

1.2. 关于函数getSpringFactoriesInstances()

由于后边的源码中多次用到了此函数,所以还是先看看这个函数到底做了什么伟大而又神秘的事情。
这是SpringApplication类的一个私有成员方法,两个重载。所以具体看第二个函数的代码啦。

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
	
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		// 获取当前类加载器	
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// 这句代码先解读SpringFactoriesLoader.loadFactoryNames(type, classLoader)
		// 在文件META-INF/spring.factories 读取配置文件中定义的多个Factory接口的全限类名,
		// 这些Factory都是框架运行需要的实体哟,非常重要的
		// 这句代码就是从这些配置文件中读取这些类名(当然是指定 Class<T> type),就这么简单^_^
		// 源码就不读了,内部实现就是有个读取配置文件的过程,其中还有一个缓存,将度过的配置放入内存中
		// 至于你问为什么要用一个Set集合来保存这些类名,人家源码中的注释不是写的很明白了吗,是为了去重^_^
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 有了类名了,就可以实例化这些Factory了,没错,这一步就是将这些类实例化
		// 这里的代码也就和你简单啦,其实就是运用了javaSE的反射代码实现将类实例化
		// type,就是实例化的父类或者接口的类型
		// parameterTypes args 就是构造函数所需要的参数的类型  以及  参数值
		//  names 全限类名的集合
		// 内部用到了Class类的isAssignableFrom()方法来判断需要实例化的了是否符合type的类型,如果不符合是不会实例化的(这句是废话,不用理会)
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		// 排序,这里的排序会根据 @Order @Priority 注解进行排序,当然还支持Ordered接口的
		// 使用spring提供的的AnnotationAwareOrderComparator对给定列表进行排序。这个类实现了OrderComparator接口
		// 有句源码的注释:Optimized to skip sorting for lists with size 0 or 1,in order to avoid unnecessary array extraction.(这句注释我一直没明白什么意思)
		// 这下明白了,原来执行顺序是这句代码决定的
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

这下好了,弄明白了这个函数是做什么的了,简单的说,就是实例化spring事先定义好的一些接口,这些接口的子类或者实现类是在文件META-INF/spring.factories中事先定义好的。这样,spring运行时所需要的很多很多类都会被实例化,并且进行了排序。

1.3. 通过getSpringFactoriesInstances()构建了哪些实体,到底是哪些?

1.3.1. 简单的说说

在SpringApplication的构造方法源码中,我们看到了两次调用getSpringFactoriesInstances()方法,传入的参数分别是ApplicationContextInitializer.class 和 ApplicationListener.class 。字面上来看,“应用上下文初始化器” 和 “应用监听器”。这两个到底是什么呢?
这两个类是spring定义的接口类,源码中的注释是这样的:

1.3.1.1. ApplicationContextInitializer.class:

Callback interface for initializing a Spring ConfigurableApplicationContextprior to being refreshed.
Typically used within web applications that require some programmatic initializationof the application context. For example, registering property sources or activatingprofiles against the context’s environment. See ContextLoader and FrameworkServlet supportfor declaring a “contextInitializerClasses” context-param and init-param, respectively.
ApplicationContextInitializer processors are encouraged to detectwhether Spring’s Ordered interface has beenimplemented or if the Orderannotation is present and to sort instances accordingly if so prior to invocation.

看到这里,我才反应过来,这不是就是那个初始化接口吗(实现这个接口,在类上添加注解 @Order(2) ,数字越小越先执行),原来是在这里被实例化的,下面举个例子:

@Order(2)
public class MyApplicationContextInitializer implements ApplicationContextInitializer{
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
    	// 这里随便打印点东西
        System.out.println("bean count====="+applicationContext.getBeanDefinitionCount());
    }
}

接口的实现以及使用,看链接

1.3.1.2. ApplicationListener.class

Interface to be implemented by application event listeners.Based on the standard java.util.EventListener interfacefor the Observer design pattern.
As of Spring 3.0, an ApplicationListener can generically declare the event typethat it is interested in. When registered with a Spring ApplicationContext, eventswill be filtered accordingly, with the listener getting invoked for matching eventobjects only.

没错,这个就是spring application事件机制的接口,在这里实例化了。配合着ApplicationEvent.class 一起用,就可以实现自己的事件了,通过 ApplicationEventPublisher 接口中的方法 publishEvent(ApplicationEvent event)方法发布事件,所有监听该事件的监听者都会执行void onApplicationEvent(E event) 方法。
接口的实现以及使用,看链接

1.4. spring判断应用类型 deduceWebApplicationType() 方法解读

public class SpringApplication {
	private WebApplicationType deduceWebApplicationType() {
					
			if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
					&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
					/* 判断是否存在 
					"org.springframework.web.reactive.DispatcherHandler" 类(webflux的包引用,这个以后再研究)
					和 "org.springframework.web.servlet.DispatcherServlet" 类(spring-webmvc包下的一个类,即引用了 spring-boot-starter-web)
					*/	
				return WebApplicationType.REACTIVE; // 响应式web应用==reactive web Spring5版本的新特性
			}
			for (String className : WEB_ENVIRONMENT_CLASSES) {
				/* 
				不包含这两个类(及没有引用 spring-boot-starter-web)
				"javax.servlet.Servlet",
				"org.springframework.web.context.ConfigurableWebApplicationContext"
				*/
				if (!ClassUtils.isPresent(className, null)) {
					return WebApplicationType.NONE; // 一个普通的应用,非web项目
				}
			}
			return WebApplicationType.SERVLET; // 本次创建的项目是此类的
		}
}

2. 总结

  • spring应用启动时,第一步是初始化了SpringApplication
  • 实例化SpringApplication作了三件主要的事情,分别是:
    • 鉴别项目的类型,项目类型分为三种:
      • WebApplicationType.REACTIVE:响应式Web,reactive web Spring5版本的新特性。
      • WebApplicationType.SERVLET:基于servlet的web应用。
      • WebApplicationType.NONE:一个普通的应用,非web项目。
    • 实例化文件META-INF/spring.factories中配置的ApplicationContextInitializer(应用初始化)的子类。
    • 实例化文件META-INF/spring.factories中配置的ApplicationListener(springBoot事件监听)的子类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值