1.ApplicationContext ac = new ClassPathXmlApplicationContext("anComplexBeanTest.xml")
/**
* 使用給定父級建立新的ClassPathXmlApplicationContext,
* 從給定的XML檔案加載定義
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
//設定帶讀取xml配置檔案的位置
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
2.refresh()方法實作在AbstractApplicationContext類,我們先從字面意思了解各個方法,接下來再去深入分析每個方法做了什麼事情
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//準備此上下文以進行重新整理,其實無非就是設定啟動時間,是否活動狀态
prepareRefresh();
// 告訴子類重新整理内部bean工廠。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 準備bean工廠以在此上下文中使用
prepareBeanFactory(beanFactory);
try {
//TODO 擴充點 允許在上下文子類中對bean工廠進行後處理。
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 調用注冊的BeanFactoryPostProcessor的BeanFactoryPostProcessor,
// 比如設定propertyEditor到BeanFactory中
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注冊BeanPostProcessors
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 為上下文初始化messagesource
initMessageSource();
// Initialize event multicaster for this context.
// 初始化應用消息廣播
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// TODO 留給子類初始化其他bean
onRefresh();
// Check for listener beans and register them.
// 檢查并注冊listener beans
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化剩下的(非懶惰的)singletons
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成重新整理,釋出相應的事件
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.
// 銷毀已建立的單例bean避免占用資源
destroyBeans();
// Reset 'active' flag.
// 取消重新整理,重置active 标志
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
//從我們開始,重置Spring核心中的常見内省緩存
//可能再也不需要單例bean的中繼資料......
resetCommonCaches();
}
}
}
3.首先是prepareRefresh()方法
/**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
*
* 準備此上下文以進行重新整理,設定其啟動日期和活動标志以及執行屬性源的任何初始化
*/
protected void prepareRefresh() {
// Switch to active.
//設定啟動日期
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// Initialize any placeholder property sources in the context environment.
//TODO 擴充點 初始化在上下文中的任何占位符資源
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
//驗證标記為必需的所有屬性是否可解析
//請參閱ConfigurablePropertyResolver#setRequiredProperties
//TODO 擴充點留給我們去實作,比如驗證我們所需要的某一個環境是否存在
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
沒有太多的邏輯,主要是設定啟動日期,colsed,active狀态,并且預置了兩個擴充點,用于加載PropertyResources和校驗資源
4.obtainFreshBeanFactory()方法,擷取beanFactory工廠
/**
* 告訴子類重新整理内部bean工廠
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//初始化beanFactory,對XML檔案讀取,并beanFactory指派給beanFactory
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
我們來看下refreshBeanFactory()在AbstractApplicationContext子類
/**
*此實作執行此上下文的基礎bean工廠的實際重新整理,
* 關閉先前的bean工廠(如果有)并初始化上一個生命周期的下一階段的新bean工廠
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果beanFactory不為null
if (hasBeanFactory()) {
//銷毀beanFactory中的singeton
destroyBeans();
//設定beanFactory為null
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
//指定可序列化的id,可以從該id反序列化出來beanFactory對象
beanFactory.setSerializationId(getId());
//TODO 擴充點重名bean定義配置和,循環引用配置,可以根據需求自定義beanFactory設定
customizeBeanFactory(beanFactory);
//讀取bean定義到beanFactory中
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
- a.判斷工廠是否為null,不為null銷毀beans(),關閉工廠
- b.使用過構造方法建立一個工廠,new DefaultListableBeanFactory(getInternalParentBeanFactory());并且會擷取AbstractApplicationContext的内部beanFactory作為new出來工廠的父beanFactory
- c.然後Spring給我們預留了一個個性化beanFactory的擴充點customizeBeanFactory()
- d.然後加載beanDefinitions交給子類AbstractXmlApplicationContext.loadBeanDefinitions
/**
* 通過XmlBeanDefinitionReader加載bean定義。
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 為給定的BeanFactory建立一個新的XmlBeanDefinitionReader。
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 對beanDefinitonReader設定環境變量
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
// EntityResolver:用于解決從網絡上下載下傳xml驗證慢導緻啟動慢,使Spring支援從本地的查找本地的xx.dtd
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
// TODO 允許子類提供讀者的自定義初始化,
//允許子類提供讀者的自定義初始化,
然後繼續實際加載bean定義。
initBeanDefinitionReader(beanDefinitionReader);
//然後繼續實際加載bean定義
loadBeanDefinitions(beanDefinitionReader);
}
- e.對于loadBeanDefinitions方法中Spring也給我們預留了相應的擴充點initBeanDefinitionReader(beanDefinitionReader);我們可以實作該類
- 定制化字迹的beanDefinitionReader
- f.最後是loadBeanDefinitions
obtainFactory方法,幫我們建立了beanFactory,并且使用XmlBeanDefinitionReader幫我們加載讀取了beanDefintion到beanDefinitionRegistry中。
5.beanFactory已經建立了,為了實作上下文的功能,我們需要對工廠prepareBeanFactory(beanFactory);我們着重關注裡面的某些方法
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));用于解析SPEL表達式#{}
//設定beanFactory的表達式語言處理,Spring3增加了表達式 語言支援預設可以使用#{bean.xxx}
//來調用相關屬性值
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
由于SPEL表達式是為了解析某個表達式并給bean屬性指派,不難想象到StandardBeanExpressionResolver在給bean屬性指派applyPropertyValues()的時候會用到
//解析value,比如value可能為map、list、runtimeBean,SPEL字元串類型
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL1EFROJTTU5keJpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4EjN0UDNxETM4EzNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
繼續深入最終會看到 使用beanExpressionResolver解析SPEL表達式
/**
* Evaluate the given String as contained in a bean definition,
* potentially resolving it as an expression.
*
* 評估bean定義中包含的給定String,可能将其解析為表達式
* @param value the value to check
* @param beanDefinition the bean definition that the value comes from
* @return the resolved value
* @see #setBeanExpressionResolver
*/
protected Object evaluateBeanDefinitionString(String value, BeanDefinition beanDefinition) {
if (this.beanExpressionResolver == null) {
return value;
}
Scope scope = (beanDefinition != null ? getRegisteredScope(beanDefinition.getScope()) : null);
return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));用于為Spring增加預設的自定義屬性編輯器
/**
* PropertyEditorRegistrar implementation that populates a given
* {@link org.springframework.beans.PropertyEditorRegistry}
* (typically a {@link org.springframework.beans.BeanWrapper} used for bean
* creation within an {@link org.springframework.context.ApplicationContext})
* with resource editors. Used by
* {@link org.springframework.context.support.AbstractApplicationContext}.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 2.0
*/
public class ResourceEditorRegistrar implements PropertyEditorRegistrar {
private static Class<?> pathClass;
static {
try {
pathClass = ClassUtils.forName("java.nio.file.Path", ResourceEditorRegistrar.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Java 7 Path class not available
pathClass = null;
}
}
private final PropertyResolver propertyResolver;
private final ResourceLoader resourceLoader;
/**
* Create a new ResourceEditorRegistrar for the given {@link ResourceLoader}
* and {@link PropertyResolver}.
* @param resourceLoader the ResourceLoader (or ResourcePatternResolver)
* to create editors for (usually an ApplicationContext)
* @param propertyResolver the PropertyResolver (usually an Environment)
* @see org.springframework.core.env.Environment
* @see org.springframework.core.io.support.ResourcePatternResolver
* @see org.springframework.context.ApplicationContext
*/
public ResourceEditorRegistrar(ResourceLoader resourceLoader, PropertyResolver propertyResolver) {
this.resourceLoader = resourceLoader;
this.propertyResolver = propertyResolver;
}
/**
* Populate the given {@code registry} with the following resource editors:
* ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
* URIEditor, ClassEditor, ClassArrayEditor.
* <p>If this registrar has been configured with a {@link ResourcePatternResolver},
* a ResourceArrayPropertyEditor will be registered as well.
* @see org.springframework.core.io.ResourceEditor
* @see org.springframework.beans.propertyeditors.InputStreamEditor
* @see org.springframework.beans.propertyeditors.InputSourceEditor
* @see org.springframework.beans.propertyeditors.FileEditor
* @see org.springframework.beans.propertyeditors.URLEditor
* @see org.springframework.beans.propertyeditors.URIEditor
* @see org.springframework.beans.propertyeditors.ClassEditor
* @see org.springframework.beans.propertyeditors.ClassArrayEditor
* @see org.springframework.core.io.support.ResourceArrayPropertyEditor
*/
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
if (pathClass != null) {
doRegisterEditor(registry, pathClass, new PathEditor(baseEditor));
}
doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
ClassLoader classLoader = this.resourceLoader.getClassLoader();
doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
if (this.resourceLoader instanceof ResourcePatternResolver) {
doRegisterEditor(registry, Resource[].class,
new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
}
}
/**
* Override default editor, if possible (since that's what we really mean to do here);
* otherwise register as a custom editor.
*/
private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
if (registry instanceof PropertyEditorRegistrySupport) {
((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
}
else {
registry.registerCustomEditor(requiredType, editor);
}
}
}
我們可以在Spring的AbstractBeanFactory.registerCustomEditors方法中可以找到調用
new ApplicationContextAwareProcessor(this);對于我們實作EnvironmentAware/ApplicationContextAware/ResourceLoaderAware/MessageSourceAware接口的bean,ApplicationContextAwareProcessor幫轉我們将相應的Environment/ApplicationContext/ResourceLoader/MessageSource類型set注入進去,
既然可以通過實作xxxAware接口注入那麼,這些類型的接口依賴肯定不需要Spring注入了,是以有以下方法
接下裡就是支援aop實作的關鍵new LoadTimeWeaverAwareProcessor(beanFactory),這個PostProcessor實作的功能是将那些實作loadTimeWeaver的bean注入loadTimeWeaver,據說這是AOP實作的關鍵,後面在探讨
最後将系統預設的bean注冊到beanFactory
6. 擴充點:在标準初始化之後修改應用程式上下文的内部bean工廠。 将加載所有bean定義,但尚未執行個體化任何bean。這允許在某些ApplicationContext實作中注冊特殊的BeanPostProcessors等
//TODO 擴充點 允許在上下文子類中對bean工廠進行後處理。
//我們拿到了beanFactory想做什麼就做什麼了,但是不要
//先把bean建立出來
postProcessBeanFactory(beanFactory);