文章目錄
夯實Spring系列|第十章:IoC 依賴來源
本章說明
本系列之前 7 ~ 9 章的主要講解 Spring IoC 依賴查找和依賴注入相關特性和使用方法,本章主要討論 依賴查找 和 依賴注入 的依賴來源,大概分爲 7 個議題,之前章節中的一些遺留的問題也會在本章中進行說明。
本章源碼分析相對之前章節較多,如果看過之前的章節,會比較容易理解
1.項目環境
- jdk 1.8
- spring 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
- 本章模塊:dependency-source
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 依賴,這個方法在很多地方被調用
比如我們在上下文 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
,所以是同一個對象。
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);
- 這個原因也比較好理解,DefaultListableBeanFactory 中分別用兩個對象來進行存儲
可以猜測通過 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核心編程思想》