天天看點

一文搞懂SpringApplication對象的建構及spring.factories的加載時機

SpringBoot源碼系列:

一文搞懂Spring Boot中java -jar啟動jar包的原理

一文搞懂SpringBoot啟動流程及自動配置

一文搞懂SpringBoot内嵌的Tomcat

一文搞懂SpringApplication對象的建構及spring.factories的加載時機

一文搞懂Spring Boot 事件監聽機制

建構SpringApplication對象源碼:

1、調用啟動類的main()方法,該方法中調用SpringApplication的run方法。

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

2、調用SpringApplication的run()方法的重載方法,在發方法内建構了SpringApplication對象。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}
           

3、建構SpringApplication對象。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		//resourceLoader為null
		this.resourceLoader = resourceLoader;
		//PrimarySources(即啟動類)一定不能為null
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//初始化primarySources屬性
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//從Classpath中推斷Web應用類型。
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
           

建構過程:

接下來我們來詳細研究一下上述過程。在建構SpringApplication對象過程中,此時resourceLoader 為null,primarySources一定不為空且需要初始化為啟動類SpringbootdemoApplication

。從Classpath中推斷出Web應用類型并初始化webApplicationType,通過getSpringFactoriesInstances()擷取Spring工廠執行個體來初始化bootstrapRegistryInitializers,initializers(List<ApplicationContextInitializer<?>>應用程式上下文初始化器清單),

listeners(List<ApplicationListener<?>>

應用監聽器清單)等屬性。最後從堆棧追蹤stackTrace中推導出主類。我們的SpringApplication對象及建構完成。

4、從Classpath推斷Web應用類型,調用的是WebApplicationType類的deduceFromClasspath()方法。如果Classpath中包含DispatcherHandler,則表明目前WebApplicationType為REACTIVE;如果既不包含javax.servlet.Servlet也不包含Spring中的ConfigurableWebApplicationContext,則表明目前WebApplicationType為NONE; 上述兩種都不是則表明目前WebApplicationType是SERVLET

static WebApplicationType deduceFromClasspath() {
        // 如果org.springframework.web.reactive.DispatcherHandler的class檔案存在且可以加載
        //不存在org.springframework.web.servlet.DispatcherServlet
        //不存在org.glassfish.jersey.servlet.ServletContainer
        //則傳回REACTIVE表明 該應用程式應作為響應式Web應用程式運作,并應啟動嵌入式servlet Web 伺服器
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		//周遊SERVLET 名額類
		//如果不存在javax.servlet.Servlet也不存在org.springframework.web.context.ConfigurableWebApplicationContext
		//則傳回NONE表明該應用程式不應作為 Web 應用程式運作,也不應啟動嵌入式 Web 伺服器
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		//傳回SERVLET表明該應用程式應作為基于servlet的 Web 應用程式運作,并應啟動嵌入式 servlet Web 伺服器。
		return WebApplicationType.SERVLET;
	}
           

spring.factories的加載時機

1、在建構SpringApplication中,初始化其屬性bootstrapRegistryInitializers屬性時進行加載 /META-INF/spring.factories。

2、建構SpringApplication對象時,通過調用getSpringFactoriesInstances(Class type)擷取工廠執行個體。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		......
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	    ......
	}
           

3、我們以初始化bootstrapRegistryInitializers為例講解,getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args)中首先擷取ClassLoader ,通過SpringFactoriesLoader機制,根據ClassLoader從類路徑jar包中加載META-INF/spring.factories下的所有工廠類及其實作類 。

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 classLoader = getClassLoader();
		// 使用SpringFactoriesLoader機制加載出工廠名,并放入Set集合中確定其唯一性。但是在META-INF/spring.factories中并無BootstrapRegistryInitializer是以此處的names的size為0。
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//建立Spring工廠執行個體(但因為上述names的size為0,是以對于BootstrapRegistryInitializer來說它并不會建立SpringFactory執行個體)
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		//通過AnnotationAwareOrderComparator對Spring工廠執行個體排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
           

4、SpringFactoriesLoader中的loadFactoryNames來加載META-INF/spring.factories下的所有工廠類及其實作類

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		//擷取目前使用的ClassLoader
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
		    //為空,則擷取SpringFactoriesLoader的類加載器
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		//org.springframework.boot.BootstrapRegistryInitializer
		String factoryTypeName = factoryType.getName();
		//加載META-INF/spring.factories下的擴充類,沒有值的傳回一個空清單。
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}
           

5、重點關注SpringFactoriesLoader中的loadSpringFactories(ClassLoader classLoader),該方法具體實作了從META-INF/spring.factories下加載擴充類。

//工廠類所在位置,在多個jar檔案中都有。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        //檢查緩存中是否有工廠類
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		//緩存中沒有,初始化result
		result = new HashMap<>();
		try {
		    //加載META-INF/spring.factories下的一系列元素
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			//疊代周遊
			while (urls.hasMoreElements()) {
			    //spring-boot-版本号.jar檔案中Spring.factories所在的絕對路徑
				URL url = urls.nextElement();
				//建構UrlResource
				UrlResource resource = new UrlResource(url);
				//加載該UrlResource中的屬性(即Spring.factories中的鍵值對)
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
				    //父類工廠類型名
					String factoryTypeName = ((String) entry.getKey()).trim();
					//子類工廠實作類名數組(在Spring.factories中多個用逗号分隔)
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					//将工廠類型名,工廠實作類清單放入名為result的 Map<String, List<String>>中,key為工廠類型名,value為工廠實作類清單。
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			//将result中的所有list都置為包含唯一進制素的不可修改的list
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			//放入緩存。		
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}
           
bootstrapRegistryInitializers 的初始化中實作了加載META-INF/spring.factories中工廠擴充類(但是在META-INF/spring.factories并無bootstrapRegistryInitializers ),并将其放入緩存Map<String, List>,其中key為父類工廠名,value為其對應擴充類清單。之後initializers(ApplicationContextInitializer清單)以及listeners(ApplicationListener清單)的初始化都是從該緩存中擷取值。

6、接下裡我們看一下第三步中的createSpringFactoriesInstances()方法。由于bootstrapRegistryInitializers 在META-INF/spring.factories中并不存在。是以我們隻有它傳回的instances是空的即它不會建立SpringFactoriesInstances。但是初始化initializers,listeners時,卻會。我們看一下具體源碼。

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		//初始化names.size()大小的list存放建立出來的執行個體。
		List<T> instances = new ArrayList<>(names.size());
		//周遊傳入的工廠擴充類執行個體名
		for (String name : names) {
			try {
			    //通過反射擷取instance的Class對象
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				//instanceClass是一個type類型,向下,否則抛異常IllegalArgumentException
				Assert.isAssignable(type, instanceClass);
				//擷取instanceClass的構造器
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
				//通過BeanUtils的instantiateClass()方法執行個體化類。
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
			    //将執行個體化出來的類放入instances
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}