
- 前言
- BeanDefinition
- BeanFactory 簡介
- Web 容器啟動程序
- bean 的加載
- FactoryBean
- 回圈依賴
- bean 生命周期
- 公眾號
前言
許多文章都是分析的 xml 配置,但是現在 Spring Boot 開發多基于注解,本文從注解的角度分析 Spring IOC 容器原始碼,
版本:
- Spring Boot:2.1.6.RELEASE
- Spring FrameWork:5.1.8.RELEASE
- Java 8
文章部分內容參考自:https://www.javadoop.com/post/spring-ioc
BeanDefinition
BeanDefinition 介面定義了一個包含屬性、構造器引數、其他具體資訊的 bean 實體,
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// ConfigurableBeanFactory 中只有 2 種:singleton 和 prototype,
// request, session 等是基于 Web 的擴展
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 不重要
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// 設定父 Bean 的資訊(繼承父 Bean 的配置資訊)
void setParentName(@Nullable String parentName);
@Nullable
String getParentName();
// 設定 Bean 的類名稱,要通過反射來生成實體
void setBeanClassName(@Nullable String beanClassName);
// 回傳當前 Bean 的 class name
String getBeanClassName();
void setScope(@Nullable String scope);
@Nullable
String getScope();
// 是否延遲初始化
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
// 設定該 Bean 依賴的所有的 Bean,并非 @Autowire 標記的
void setDependsOn(@Nullable String... dependsOn);
@Nullable
String[] getDependsOn();
// 設定該 Bean 是否可以注入到其他 Bean 中,只對根據型別注入有效,
// 如果根據名稱注入,即使這邊設定了 false,也是可以的
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
// 同一介面的多個實作,如果不指定名字,Spring 會優先選擇設定 primary 為 true 的 bean
void setPrimary(boolean primary);
boolean isPrimary();
// 如果該 Bean 采用工廠方法生成,指定工廠名稱;否則用反射生成
void setFactoryBeanName(@Nullable String factoryBeanName);
@Nullable
String getFactoryBeanName();
// 指定工廠類中的 工廠方法名稱
void setFactoryMethodName(@Nullable String factoryMethodName);
@Nullable
String getFactoryMethodName();
// 回傳該 bean 的構造器引數
ConstructorArgumentValues getConstructorArgumentValues();
default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}
// Bean 中的屬性值,回傳的實體在 bean factory post-processing 期間會被更改
MutablePropertyValues getPropertyValues();
default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}
void setInitMethodName(@Nullable String initMethodName);
@Nullable
String getInitMethodName();
void setDestroyMethodName(@Nullable String destroyMethodName);
@Nullable
String getDestroyMethodName();
void setRole(int role);
int getRole();
void setDescription(@Nullable String description);
@Nullable
String getDescription();
// Read-only attributes
boolean isSingleton();
boolean isPrototype();
boolean isAbstract();
@Nullable
String getResourceDescription();
@Nullable
BeanDefinition getOriginatingBeanDefinition();
}
AnnotationConfigUtils#processCommonDefinitionAnnotations(...)
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
else if (abd.getMetadata() != metadata) {
lazy = attributesFor(abd.getMetadata(), Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
}
if (metadata.isAnnotated(Primary.class.getName())) {
abd.setPrimary(true);
}
AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
if (dependsOn != null) {
abd.setDependsOn(dependsOn.getStringArray("value"));
}
AnnotationAttributes role = attributesFor(metadata, Role.class);
if (role != null) {
abd.setRole(role.getNumber("value").intValue());
}
AnnotationAttributes description = attributesFor(metadata, Description.class);
if (description != null) {
abd.setDescription(description.getString("value"));
}
}
可以看到,processCommonDefinitionAnnotations 方法會根據注解來填充 AnnotatedBeanDefinition,這些注解有:
- Lazy
- Primary
- DependsOn
- Role
- Description
向上查看呼叫,發現會在 ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass 將其注冊為一個 bean definition,
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
AnnotationMetadata metadata = https://www.cnblogs.com/510602159-Yano/archive/2020/12/10/configClass.getMetadata();
AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
configBeanDef.setScope(scopeMetadata.getScopeName());
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
// 1. 通過注解填充 configBeanDef
AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 2. 將 bean definition 注冊到 registry 中
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
configClass.setBeanName(configBeanName);
if (logger.isTraceEnabled()) {
logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
}
}
最侄訓被 AbstractApplicationContext#refresh 的 invokeBeanFactoryPostProcessors(beanFactory) 方法呼叫,
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
...
}
}
BeanFactory 簡介
BeanFactory 是生產 bean 的工廠,它負責生產和管理各個 bean 實體,從下圖可以看到,ApplicationContext 也是一個 BeanFactory,如果說 BeanFactory 是 Spring 的心臟,那么 ApplicationContext 就是完整的身軀,

ApplicationContext 是應用程式運行時提供配置資訊的通用介面,ApplicationContext 在程式運行時是不可更改的,但是實作類可以重新再入配置資訊,
ApplicationContext 的實作類有很多,比如 AnnotationConfigApplicationContext, AnnotationConfigWebApplicationContext, ClassPathXmlApplicationContext, FileSystemXmlApplicationContext, XmlWebApplicationContext 等,我們上面分析的就是 AnnotationConfigApplicationContext,其采用注解的方式提供配置資訊,這樣我們就不用寫 XML 組態檔了,非常簡潔,

Web 容器啟動程序
本文使用 Spring Boot 開發,其啟動的代碼是:
@SpringBootApplication
@EnableScheduling
@EnableAspectJAutoProxy
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
核心的點是這一句:
SpringApplication.run(AppApplication.class, args);
SpringApplication 的代碼就不分析了,明確本次看原始碼的目的是分析容器原始碼,Spring Boot 的啟動程序和其他資訊都忽略了,因為 Spring 代碼實在是龐雜,分析上面的 run 方法,最侄訓追蹤到 SpringApplication#run(...) 方法,
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
跟 context 相關的,是下面這 3 句代碼:
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
refreshContext 方法就是重繪給定的 context:
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
會發現最終呼叫到了 AbstractApplicationContext#refresh 方法,注釋參考自:https://www.javadoop.com/post/spring-ioc
@Override
public void refresh() throws BeansException, IllegalStateException {
// 來個鎖,不然 refresh() 還沒結束,你又來個啟動或銷毀容器的操作,那不就亂套了嘛
synchronized (this.startupShutdownMonitor) {
// 準備作業,記錄下容器的啟動時間、標記“已啟動”狀態、處理組態檔中的占位符
prepareRefresh();
// 這步比較關鍵,這步完成后,組態檔就會決議成一個個 Bean 定義,注冊到 BeanFactory 中,
// 當然,這里說的 Bean 還沒有初始化,只是配置資訊都提取出來了,
// 注冊也只是將這些資訊都保存到了注冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 設定 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動注冊幾個特殊的 bean
prepareBeanFactory(beanFactory);
try {
// 【這里需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實作了此介面,
// 那么在容器初始化以后,Spring 會負責呼叫里面的 postProcessBeanFactory 方法,】
// 這里是提供給子類的擴展點,到這里的時候,所有的 Bean 都加載、注冊完成了,但是都還沒有初始化
// 具體的子類可以在這步的時候添加一些特殊的 BeanFactoryPostProcessor 的實作類或做點什么事
postProcessBeanFactory(beanFactory);
// 呼叫 BeanFactoryPostProcessor 各個實作類的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊 BeanPostProcessor 的實作類,注意看和 BeanFactoryPostProcessor 的區別
// 此介面兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 兩個方法分別在 Bean 初始化之前和初始化之后得到執行,注意,到這里 Bean 還沒初始化
registerBeanPostProcessors(beanFactory);
// 初始化當前 ApplicationContext 的 MessageSource,國際化這里就不展開說了,不然沒完沒了了
initMessageSource();
// 初始化當前 ApplicationContext 的事件廣播器,這里也不展開了
initApplicationEventMulticaster();
// 從方法名就可以知道,典型的模板方法(鉤子方法),
// 具體的子類可以在這里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
onRefresh();
// 注冊事件監聽器,監聽器需要實作 ApplicationListener 介面,這也不是我們的重點,過
registerListeners();
// 重點,重點,重點
// 初始化所有的 singleton beans
//(lazy-init 的除外)
finishBeanFactoryInitialization(beanFactory);
// 最后,廣播事件,ApplicationContext 初始化完成
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 銷毀已經初始化的 singleton 的 Beans,以免有些 bean 會一直占用資源
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
核心流程就是 try 代碼塊里的內容,我們應該了解整體原理,本篇文章并不能逐行逐句分析,如果那樣做,完全就變成一部字典了……
bean 的加載
bean 加載的呼叫函式:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 提取對應 bean 的名字
final String beanName = transformedBeanName(name);
Object bean;
// 1. 重要,重要,重要!
// 創建單例 bean 避免回圈依賴,嘗試從快取中獲取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 存在回圈依賴
if (isPrototypeCurrentlyInCreation(beanName)) {
// 原型模式直接拋出例外(回圈依賴僅能在單例模式下解決)
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// 如果不是僅僅做型別檢查,則是創建 bean,需要做記錄
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 獲取 RootBeanDefinition,如果指定 beanName 是子 bean 的話,需要合并父類屬性
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 若存在依賴,需要遞回實體化依賴的 bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 創建 bean 實體
// Singleton 模式的創建
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// Prototype 模式的創建
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 檢測 requiredType 是否為 bean 的實際型別,不是則轉換,不成功則拋出例外
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
可以看到 bean 的加載是相當復雜的,加載的步驟大致如下:
- 轉換對應 beanName
- 嘗試從快取中加載單例
- bean 的實體化
- 原型模式的依賴檢查
- 檢測 parentBeanFactory
- 將組態檔轉換為 RootBeanDefinition
- 尋找依賴
- 針對不同的 scope 進行 bean 的創建
- 型別轉換
FactoryBean
前面提到了 BeanFactory,這里又來了個 FactoryBean …… 據說 Spring 提供了 70 多個 FactoryBean 的實作,可見其在 Spring 框架中的地位,它們隱藏了實體化復雜 bean 的細節,給上層應用帶來便捷,
public interface FactoryBean<T> {
// 回傳 FactoryBean 創建的 bean 實體,如果 isSingleton() 回傳 true,則該實體會放到 Spring 容器的單例快取池中
@Nullable
T getObject() throws Exception;
// 回傳 FactoryBean 創建的 bean 型別
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
回圈依賴
回圈依賴就是回圈參考,兩個或多個 bean 相互之間持有對方,那么 Spring 是如何解決回圈依賴的?
在 Spring 中回圈依賴一共有 3 種情況:
- 構造器回圈依賴
- setter 回圈依賴
- prototype 范圍的依賴處理
其中構造器回圈依賴是無法解決的,因為一個 bean 創建時首先要經過構造器,但是構造器相互依賴,就相當于 Java 中多執行緒死鎖,
setter 注入造成的依賴是通過 Spring 容器提前暴露剛完成構造器注入但未完成其他步驟(如 setter 注入)的 bean 來完成的,而且只能解決單例作用域的 bean 回圈依賴,通過提前暴露一個單例工廠方法,從而使其他 bean 能參考到該 bean,代碼如下:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
其中 earlySingletonObjects 的定義為:
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
對于 prototype 作用域的 bean,Spring 容器無法完成依賴注入,因為 Spring 容器不快取 prototype 作用域的 bean,
bean 生命周期
面試的話,Spring 的核心就在這里了,不過只要記住大體流程就行,

公眾號
coding 筆記、點滴記錄,以后的文章也會同步到公眾號(Coding Insight)中,希望大家關注_
代碼和思維導圖在 GitHub 專案中,歡迎大家 star!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/232908.html
標籤:其他
上一篇:常用的DOS命令
下一篇:9 一對多和多對一處理
