夯實Spring系列|第十章:IoC 依賴來源

夯實Spring系列|第十章:IoC 依賴來源

本章說明

本系列之前 7 ~ 9 章的主要講解 Spring IoC 依賴查找和依賴注入相關特性和使用方法,本章主要討論 依賴查找依賴注入 的依賴來源,大概分爲 7 個議題,之前章節中的一些遺留的問題也會在本章中進行說明。

本章源碼分析相對之前章節較多,如果看過之前的章節,會比較容易理解

1.項目環境

2.依賴查找的來源

  • 一般來源
來源 配置元數據
Spring BeanDefinition <bean id=“user” class="…" …>
- @Bean public User user(){…}
- BeanDefinitionBuilder
單例對象 API 實現
  • Spring 內建 BeanDefinition

註解驅動 Spring 應用上下文內建可查找的依賴(部分)

Bean 名稱 Bean 實例 使用場景
org.springframework.context.annotation. internalConfigurationAnnotationProcessor ConfigurationClassPostProcessor 對象 處理Spring配置類
org.springframework.context.annotation. internalAutowiredAnnotationProcessor AutowiredAnnotationBeanPostProcessor對象 處理 @Autowired 以及 @Value 註解
org.springframework.context.annotation. internalCommonAnnotationProcessor CommonAnnotationBeanPostProcessor對象 (條件激活) 處理 JSR - 250 註解,比如 @PostConstruct
org.springframework.context.event. internalEventListenerProcessor EventListenerMethodProcessor對象 處理標準 @EventListener 的 Spring 事件監聽方法
org.springframework.context.event. internalEventListenerFactory DefaultEventlistenerFactory 對象 @EvenListener 事件監聽方法適配爲 ApplicationListener
org.springframework.context.annotation. internalPersistenceAnnotationProcessor PersistenceAnnotationBeanPostProcessor 對象 (條件激活)處理 JPA 註解場景
  • Spring 內建單例對象
Bean 名稱 Bean 實例 使用場景
environment Environment 對象 外部化配置以及 Profiles
systemProperties java.util.Properties 對象 Java 系統屬性
systemEnvironment java.util.Map 對象 操作系統環境變量
messageSource MessageSource 對象 國際化文案
lifecycleProcessor LifecycleProcessor 對象 Lifecycle Bean 處理器
applicationEventMulticaster ApplicationEventMulticaster 對象 Spring 事件廣播

2.1 Spring 內建 BeanDefinition 註冊過程

相關源碼位置:AnnotationConfigUtils#registerAnnotationConfigProcessors()

部分源碼:

	public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
			BeanDefinitionRegistry registry, @Nullable Object source) {
        ...
 
		Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

		if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
		}

		if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
			RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
			def.setSource(source);
			beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
		}
        ...
		}

這個方法會顯示的去註冊一些內建的 Bean 依賴,這個方法在很多地方被調用
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-coJpeSLI-1587778381692)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200424085913314.png)]
比如我們在上下文 xml 配置文件中加入下面的配置,就會觸發 registerAnnotationConfigProcessors()方法的調用

    <context:component-scan base-package="com..."/>
    <context:annotation-config/>

當我們激活註解驅動的時候,這些 Bean 就會通過內建的方式放到我們的應用上下文中。

2.2 單例對象註冊

相關源碼位置:AbstractApplicationContext#prepareBeanFactory()

註冊一些內建的 Bean,如果引入更多的模塊,比如 AOP、事務,也會有相應通過 registerSingleton 註冊 Bean。

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		...
		// Register default environment beans.
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}

3.依賴注入的來源

依賴注入 的來源比 依賴查找 多了 非 Spring 容器管理對象 這一塊

來源 配置元數據
Spring BeanDefinition <bean id=“user” class="…" …>
- @Bean public User user(){…}
- BeanDefinitionBuilder
單例對象 API 實現
非 Spring 容器管理對象

3.1 非 Spring 容器管理對象(ResolvableDependency)

  • 對象不存在 spring 容器中(即通過 getBean 的方法無法查找),但是可以依賴注入

源碼位置:AbstractApplicationContext#prepareBeanFactory

	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        ...
		// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
        // 使用自動綁定的方式找到
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);
        ...

registerResolvableDependency() 這個方法的實現在 DefaultListableBeanFactory 中

	@Override
	public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
		Assert.notNull(dependencyType, "Dependency type must not be null");
		if (autowiredValue != null) {
			if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
				throw new IllegalArgumentException("Value [" + autowiredValue +
						"] does not implement specified dependency type [" + dependencyType.getName() + "]");
			}
			this.resolvableDependencies.put(dependencyType, autowiredValue);
		}
	}

通過 this.resolvableDependencies.put(dependencyType, autowiredValue);

可以看到將這個 4 個對象放到 resolvableDependencies 這個 ConcurrentHashMap 中。

在啓動Spring容器的時候,可以將斷點打在 DefaultListableBeanFactory#findAutowireCandidates 這個方法裏面,看到 resolvableDependencies 集合的元素,就是前面註冊的 4 個對象,而且因爲後面三個對象註冊的都是 this,也就是當前的應用上下文 applicationContext,所以是同一個對象。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QFCtjQju-1587778381695)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200424215602102.png)]

3.2 依賴來源示例

我們將這 4 個對象分別注入到類的屬性中,看看他們到底是什麼

/**
 * 依賴來源示例
 */
public class DependencySourceDemo {

    /**
     * 注入在 postProcessProperties 方法執行,早於 setter 注入 ,也早於 @PostConstruct
     */
    @Autowired
    private BeanFactory beanFactory;

    @Autowired
    private ResourceLoader resourceLoader;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @PostConstruct
    public void init() {
        System.out.println("beanFactory == applicationContext:" + (beanFactory == applicationContext));
        System.out.println("beanFactory == applicationContext.getBeanFactory:" + (beanFactory == applicationContext.getAutowireCapableBeanFactory()));
        System.out.println("resourceLoader == applicationContext:" + (resourceLoader == applicationContext));
        System.out.println("applicationEventPublisher == applicationContext:" + (applicationEventPublisher == applicationContext));
    }

    @PostConstruct
    public void initLookup() {
        getBean(BeanFactory.class);
        getBean(ResourceLoader.class);
        getBean(ApplicationContext.class);
        getBean(ApplicationEventPublisher.class);
    }

    private <T> T getBean(Class<T> beanType) {
        try {
            return beanFactory.getBean(beanType);
        } catch (NoSuchBeanDefinitionException e) {
            System.out.println("當前類型:" + beanType + " 無法在 BeanFactory 中查找!");
        }
        return null;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(DependencySourceDemo.class);

        applicationContext.refresh();

        DependencySourceDemo demo = applicationContext.getBean(DependencySourceDemo.class);
        System.out.println(demo.beanFactory);
        System.out.println(demo.resourceLoader);
        System.out.println(demo.applicationContext);
        System.out.println(demo.applicationEventPublisher);
        applicationContext.close();
    }

}

執行結果

beanFactory == applicationContext:false
beanFactory == applicationContext.getBeanFactory:true
resourceLoader == applicationContext:true
applicationEventPublisher == applicationContext:true
當前類型:interface org.springframework.beans.factory.BeanFactory 無法在 BeanFactory 中查找!
當前類型:interface org.springframework.core.io.ResourceLoader 無法在 BeanFactory 中查找!
當前類型:interface org.springframework.context.ApplicationContext 無法在 BeanFactory 中查找!
當前類型:interface org.springframework.context.ApplicationEventPublisher 無法在 BeanFactory 中查找!
org.springframework.beans.factory.support.DefaultListableBeanFactory@6b419da: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,dependencySourceDemo]; root of factory hierarchy
org.springframework.context.annotation.AnnotationConfigApplicationContext@3796751b, started on Fri Apr 24 11:23:33 CST 2020
org.springframework.context.annotation.AnnotationConfigApplicationContext@3796751b, started on Fri Apr 24 11:23:33 CST 2020
org.springframework.context.annotation.AnnotationConfigApplicationContext@3796751b, started on Fri Apr 24 11:23:33 CST 2020

由源碼可知,BeanFactory 註冊的對象是 beanFactory(通過 getBeanFactory() 方法獲取),而其他三個對象註冊的是 this(即當前的應用上下文 ApplicationContext)

  • beanFactory == applicationContext:false 不相等,而且在前面的文章中也分析過

  • 而其他三個對象,都是 this,所以其他這個對象本身其實都是 applicationContext,所以都相等

  • 這 4 個對象都是非 Spring 容器管理對象,通過 registerResolvableDependency 方法註冊,所以通過 BeanFactory#getBean() 依賴查找無法獲取,拋出 NoSuchBeanDefinitionException 異常。

    • 這個原因也比較好理解,DefaultListableBeanFactory 中分別用兩個對象來進行存儲
      • beanDefinitionMap 用來存儲一般註冊 BeanDefinition,比如 xml,註解,API
      • resolvableDependencies 用來存儲 非 Spring 容器管理對象(或者叫遊離對象)
    	/** Map from dependency type to corresponding autowired value. */
    	private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);
    
    	/** Map of bean definition objects, keyed by bean name. */
    	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    

可以猜測通過 beanFactory.getBean() 依賴查找是 beanDefinitionMap 集合,並未查找 resolvableDependencies 集合。那麼我們來看下源碼

	@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

doGetBean() 這個方法的內容較多,我們只貼出部分相關的代碼

  • 第一步 getSingleton(beanName)查找單例對象集合,如果有的話直接返回
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
  • 第二步是層次性的查找,BeanFactory parentBeanFactory = getParentBeanFactory(); 跳過
  • 第三步是合併 BeanDefinition,final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 跳過
  • AbstractBeanFactory 320 行,這裏的單例指的是 Bean 的作用域,和前面的單例對象並不是一個概念,BeanDefinition 註冊默認是 singleton。然後通過這個 mbd(合併後的 BeanDefinition ) 去 createBean,創建這個 bean。
			 //判斷這個合併之後的 beanDefintion 是否是單例的	
             if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
                ...
				}

那麼關鍵點就在 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); 這個合併的過程中。多次查找源碼可以看到合併的過程會調用 getBeanDefinition 方法如下:

	@Override
	public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
		BeanDefinition bd = this.beanDefinitionMap.get(beanName);
		if (bd == null) {
			if (logger.isTraceEnabled()) {
				logger.trace("No bean named '" + beanName + "' found in " + this);
			}
			throw new NoSuchBeanDefinitionException(beanName);
		}
		return bd;
	}

果然和我們猜想的一樣通過 beanDefinitionMap get() 獲取 BeanDefinition,而整個 getBean 依賴查找的過程中沒有查找過 resolvableDependencies 集合。所以通過依賴查找無法獲取 非 Spring 容器管理對象(Resolvable Dependency)

4.Spring 容器管理和遊離對象

依賴對象

來源 Spring Bean 對象 生命週期管理 配置元信息 使用場景
Spring BeanDefinition 依賴查找、依賴注入
單體對象 依賴查找、依賴注入
Resolvable Dependency 依賴注入

5.Spring BeanDefinition 作爲依賴來源

要素

  • 元數據:BeanDefinition
  • 註冊:BeanDefinitionRegistry#registrerBeanDefinition
  • 類型:延遲和非延遲
  • 順序:Bean 生命週期順序按照註冊順序

DefaultListableBeanFactory#registerBeanDefinition 相關源碼大致分爲

  • 第一步 ((AbstractBeanDefinition) beanDefinition).validate();//校驗 beanDefinition
    • 這個在BeanDefinitionBuilder#getBeanDefinition 方法中也使用
  • 第二步 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    • 通過 beanName 獲取 beanDefinition 信息
  • 第三步 如果 beanDefinition 存在的話做一些判斷
    • isAllowBeanDefinitionOverriding()//檢查是否能被覆蓋,默認 true
    • existingDefinition.getRole() < beanDefinition.getRole() 這個不太好理解,主要是基於業務架構方面的
      • (原代碼中的註解)e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE(需要具體的業務場景)
  • 第四步 最後將 beanDefinition 存放到集合中
    • this.beanDefinitionMap.put(beanName, beanDefinition);
  • 第五步 如果 beanDefinition 不存在
    • this.beanDefinitionMap.put(beanName, beanDefinition);//Map是無序的
    • this.beanDefinitionNames.add(beanName);//保證順序
      • ArrayList 存放 beanName 來保證 beanDefinition 調用的順序
    • 移除單例對象

6.單例對象作爲依賴來源

要素

  • 來源:外包普通 Java 對象(不一定是 POJO)
  • 註冊:SingletonBeanRegistry#registerSingleton

限制

  • 無生命週期管理
  • 無法實現延遲初始化 Bean

6.1 註冊單例對象

源碼位置:DefaultSingletonBeanRegistry#registerSingleton

就是簡單的 put 操作,將對象存放到 singletonObjects 中。

registerSingleton 的第一把鎖,主要是這個方法既調用了 get() 方法,又調用了 add() 方法,爲了線程安全。

addSingleton 中加鎖的原因是 addSingleton() 方法可能被別的地方調用,而且此方法也是一個多元的操作,爲了線程安全。

  • this.singletonObjects.put(beanName, singletonObject);
    • 加入 singletonObjects 集合
  • this.singletonFactories.remove(beanName);
    • ObjectFactory 操作的 Bean,用來進行延遲查找,如果這個 Bean 註冊成單例對象,和這個是一個互斥的操作,所以需要刪除
  • this.earlySingletonObjects.remove(beanName);
    • 這個早期的 SingletonObject 也是一個互斥的操作,所以需要刪除
  • this.registeredSingletons.add(beanName);
    • 這個也是爲了保存一個順序
	@Override
	public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
		Assert.notNull(beanName, "Bean name must not be null");
		Assert.notNull(singletonObject, "Singleton object must not be null");
		synchronized (this.singletonObjects) {
			Object oldObject = this.singletonObjects.get(beanName);
			if (oldObject != null) {
				throw new IllegalStateException("Could not register object [" + singletonObject +
						"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
			}
			addSingleton(beanName, singletonObject);
		}
	}

	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

6.2 單例對象依賴查找

依賴查找 還是通過 getBean 方法

源碼位置:AbstractBeanFactory#getBean(java.lang.String, java.lang.Object…)

先查詢的是 getSingleton() 而不是先查 BeanDefinition,如果找到 bean 直接返回,而 BeanDefinition 的方式會比較複雜,需要將 BeanDefinition 通過 doGetBean() 變成 Bean,並激活整個 Bean 的生命週期。

這個過程我們已經在本文 3.2 依賴來源示例 做了分析

7.非 Spring 容器管理對象作爲依賴來源

要素

  • 註冊:ConfigurableListableBeanFactory#registerResolvableDependency
    • 只有類型注入一種
    • 只能實現依賴注入

限制

  • 無生命週期管理
  • 無法實現延遲初始化 Bean
  • 無法通過依賴查找

7.1 registerResolvableDependency 示例

  • 我們將類型 String.calss 和 “xwf” 註冊到 ResolvableDependency 中,

  • 再通過 @Autowired 方法進行依賴注入

  • 最後通過 @PostConstruct 打印 name 的值

/**
 * ResolvableDependency 作爲依賴來源
 */
public class ResolvableDependencySourceDemo {

    @Autowired
    private String name;

    @PostConstruct
    public void init(){
        System.out.println(name);
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        //之前的寫法
        //applicationContext.register(ResolvableDependencySourceDemo.class);
        applicationContext.refresh();

         //如果採用這種方式,需要將register放到refresh之後 原因和refresh中代碼的spring應用上下文啓動過程有關
        applicationContext.register(ResolvableDependencySourceDemo.class);
        AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
        if (beanFactory instanceof ConfigurableListableBeanFactory) {
            ConfigurableListableBeanFactory configurableListableBeanFactory = (ConfigurableListableBeanFactory) beanFactory;
            configurableListableBeanFactory.registerResolvableDependency(String.class,"xwf");

        }
        
applicationContext.getBean(ResolvableDependencySourceDemo.class);

        applicationContext.close();
    }
}

如果像之前例子的寫法將 applicationContext.register(ResolvableDependencySourceDemo.class);寫在 refresh()之前,會報錯 NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available

因爲 applicationContext.refresh(); 執行過程中會去觸發依賴注入過程,而 @Autowired 標註的屬性 name 還沒有對象可以注入,所以我們先不註冊 ResolvableDependencySourceDemo.class ,將 applicationContext.register(ResolvableDependencySourceDemo.class); 放在 applicationContext.refresh();之後就不會報錯了,但是要觸發依賴注入的過程,還需要applicationContext.getBean(ResolvableDependencySourceDemo.class);通過依賴查找觸發。

那麼有沒有什麼更簡單的方法呢?

可以通過添加BeanFactoryPostProcessor,原因在 AbstractApplicationContext#refresh 的源碼中,有相關的容器生命週期設計;我們添加的BeanFactory 的後置處理,會在invokeBeanFactoryPostProcessors()這個裏面進行處理,而依賴注入過程在這之後的 finishBeanFactoryInitialization()

/**
 * ResolvableDependency 作爲依賴來源
 */
public class ResolvableDependencySourceDemo {

    @Autowired
    private String name;

    @PostConstruct
    public void init(){
        System.out.println(name);
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(ResolvableDependencySourceDemo.class);
        
        //第二種方式
        applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
            beanFactory.registerResolvableDependency(String.class,"xwf-post");
        });

        applicationContext.refresh();
        applicationContext.close();
    }
}

執行結果

xwf-post

8.外部化配置作爲依賴來源

要素

  • 類型:非常規 Spring 對象依賴來源

限制

  • 無生命週期管理
  • 無法實現延遲初始化 Bean
  • 無法通過依賴查找

8.1 示例

我們先在 resources/META-INF 目錄下面新建一個 default.properties 的配置文件

user.id = 1
usr.name = 小仙
user.resource = classpath://META-INF/default.properties

我們分別將三個值注入到 ExternalConfigurationDependencyDemo 的屬性中

/**
 * 外部化配置依賴來源
 */
@Configuration
@PropertySource(value = "META-INF/default.properties", encoding = "gbk")
public class ExternalConfigurationDependencyDemo {

    @Value("${user.id:-1}")
    private Long id;

    @Value("${usr.name}")
    private String name;

    @Value("${user.resource:classpath://default.properties}")
    private Resource resource;

    @PostConstruct
    public void init() {
        System.out.println("PostConstruct-> id: " + id);
        System.out.println("PostConstruct-> resource: " + resource);
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(ExternalConfigurationDependencyDemo.class);

        applicationContext.refresh();
        ExternalConfigurationDependencyDemo demo = applicationContext.getBean(ExternalConfigurationDependencyDemo.class);

        System.out.println(demo.id);
        System.out.println(demo.name);
        System.out.println(demo.resource);

        applicationContext.close();
    }
}

執行結果

PostConstruct-> id: 1
PostConstruct-> resource: class path resource [META-INF/default.properties]
1
小仙
class path resource [META-INF/default.properties]

如果漢字打印有亂碼,需要注意 default.properties 文件和 idea 的字符集,具體設置 @PropertySource(value = "META-INF/default.properties", encoding = "gbk")

調用源碼位置:DefaultListableBeanFactory#doResolveDependency 1225 行
在這裏插入圖片描述
三個字段,會依次進來三次。

9.面試題

9.1 注入和查找的依賴來源是否相同?

不完全一樣,依賴查找的來源僅限於 Spring BeanDefinition 以及單例對象,而依賴注入的來源還包括 Resolvable Dependency(非 Spring 容器管理對象或者稱遊離對象) 以及 @Value 所標註的外部化配置

9.2 單例對象能在 IOC 容器啓動後註冊碼?

可以,單例對象的註冊與 BeanDefinition 不同,BeanDefinition 會被 ConfigurableListableBeanFactory#freezeConfiguration() 方法影響從而凍結註冊,單例對象沒有這個限制。

9.3 Spring 依賴注入有哪些來源?

  • Spring BeanDefinition
  • 單體對象
  • Resolvable Dependency 遊離對象 4個
    • BeanFactory
    • ApplicationContext
    • ResourceLoader
    • ApplicationEventPublisher
  • 外部化配置@Value

10.參考

  • 極客時間-小馬哥《小馬哥講Spring核心編程思想》
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章