Spring Boot 源码分析之 SpringApplication 构建

302 阅读3分钟

基于Spring Boot 2.2.5 Release版本。

SpringApplication

首先调用静态方法
SpringApplication.run  返回ConfigurableApplicationContext 应用上下文。

 
	//这个run 静态方法其实是调用下面的那个,不过他把单个primarySource 转为了数组
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}

	//根据primarySources 构建SpringApplication对象。
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

然后 构建SpringApplication对象
构建方法有2个,

	public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
        //将primarySources 数组转化为LinkedHashSet
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //确定应用的类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //获取初始化类集合
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		//获取监听类集合
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//根据调用栈推测main 类。
        this.mainApplicationClass = deduceMainApplicationClass();
	}

WebApplicationType.deduceFromClasspath
//推测程序类型
static WebApplicationType deduceFromClasspath() {
       //org.springframework.web.reactive.DispatcherHandler 存在,
       //并且org.springframework.web.servlet.DispatcherServlet 不存在,
      // 并且org.glassfish.jersey.servlet.ServletContainer 不存在
      //则应用是WEBFLUX类型的
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
    //查找"javax.servlet.Servlet" 和"org.springframework.web.context.ConfigurableWebApplicationContext"
		//如果任意类不存在则应用类型为NONE
       for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
     //定义为SERVLET 类型
		return WebApplicationType.SERVLET;
	}

尝试创建MVC 应用和 WEBFLUX应用观察。

getSpringFactoriesInstances 方法
   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 = getClassLoader();
		// Use names and ensure unique to protect against duplicates
        //使用SpringFactoriesLoader依据类型加载
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

SpringFactoriesLoader工厂加载机制是Spring内部提供的一个约定俗成的加载方式,会加载资源META-INF/spring.factories文件中记录的所有类。这个Properties格式的文件中的key是接口、注解、或抽象类的全名,value是以逗号 “ , “ 分隔的实现类,使用SpringFactoriesLoader来实现相应的实现类注入Spirng容器中。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		//查看缓存
    	MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		
    	//FACTORIES_RESOURCE_LOCATION 就背定义为META-INF/spring.factories
		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            //将spring.factories 中的内容,以接口名/抽象类名/具体类名为key,具体实现类名为Value存放在result中。
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
            //以classLoader为key, result为value存放在内存中。
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

loadSpringFactories 就是加载

  • spring-boot-2.2.5.RELEASE.jar!/META-INF/spring.factories
  • spring-boot-autoconfigure-2.2.5.RELEASE.jar!/META-INF/spring.factories
  • spring-beans-5.2.4.RELEASE.jar!/META-INF/spring.factories
  • mybatis-spring-boot-autoconfigure-2.1.1.jar!/META-INF/spring.factories
  • pagehelper-spring-boot-autoconfigure-1.2.5.jar!/META-INF/spring.factories

内容到result。

createSpringFactoriesInstances

//拿到类名,实例化对象
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<?> 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;
	}

SpringApplication构建方法流程:
1.初始化一些属性的初始值。
2.推断应用的类型,从是否引用WEBFLUX 和MVC来判断的应用类型。
3.依靠getSpringFactoriesInstances方法获取实现了ApplicationContextInitializer.class接口的所有实例对象。

  • getSpringFactoriesInstances 先获取classload,再通过SpringFactoriesLoader获取ApplicationContextInitializer.class对应的实现类
  • 再通过createSpringFactoriesInstances把实现类实例化。

4.再依靠getSpringFactoriesInstances方法方法实现了ApplicationListener.class接口的所有实例对象。
5.再通过deduceMainApplicationClass推测是哪个类