首頁 > 軟體

SpringBoot spring.factories載入時機分析

2023-09-06 14:02:15

spring.factories作用

這個類似於Java中的SPI功能,SpringBoot啟動的時候會讀取所有jar包下面的META-INF/spring.factories檔案;

並且將檔案中的 介面/抽象類 對應的實現類都對應起來,並在需要的時候可以範例化對應的實現類

下面我們來分析一下原始碼看看spring.factories的使用場景

原始碼解析

啟動SpringApplication,看看構造方法

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

其中方法getSpringFactoriesInstances( ApplicationContextInitializer.class) 是用於獲取Spring中指定類範例用的;並且獲取的時候是根據讀取整個專案中檔案路徑為META-INF/spring.factories 中的內容範例化對應的範例類的;

例如這裡的ApplicationContextInitializer 是一個介面,那麼應該範例化哪些他的實現類呢?那就找META-INF/spring.factories檔案 ; 那麼我們在spring-boot:2.1.0jar包中找到了這個檔案

讀取到需要範例化的實現類為

org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

並且還在spring-boot-autoconfigure-2.1.0.RELEASE.jar中找到了這個檔案

那麼檔案中的兩個實現類也會被範例化;加上上面4個總共有6個

org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

可以看到不僅僅只是把org.springframework.context.ApplicationContextInitializer 的範例類解析了出來;而是所有的都解析了出來並且儲存下來了.下次其他的類需要被範例化的時候就可以直接從記憶體裡面拿了;

上面過程拿到了範例類之後,接下來就是範例化的過程了

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

方法createSpringFactoriesInstances就是建立範例的過程;可以看到傳入了對應的介面類org.springframework.context.ApplicationContextInitializer ;接下來就會範例化 上面找到了對應的實現類;

	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;
	}

範例化的過程如果,沒有什麼特別需要講解的;

上面有個方法 AnnotationAwareOrderComparator.sort(instances);是用來排序所有範例的; 實現類需要實現 介面Ordered ; getOrder返回的值越小,優先順序更高

用法

知道spring.factories的用法之後, 那麼我們就可以利用這個特性實現自己的目的;

例如我們也可以寫一個介面類ApplicationContextInitializer的實現類;

等等之類的;

以上就是SpringBoot spring.factories載入時機分析的詳細內容,更多關於spring.factories載入時機的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com