有道無術,術可求; 有術無道,止於術; 讀源碼是一個很枯燥的過程,但是Spring源碼裡面有很多值得學習的地方 加油~!!!!! 前言 使用SpringMVC的時候,通常使用下麵這行代碼來載入Spring的配置文件 ApplicationContext application = new Class ...
有道無術,術可求;
有術無道,止於術;
讀源碼是一個很枯燥的過程,但是Spring源碼裡面有很多值得學習的地方
加油~!!!!!
前言
使用SpringMVC的時候,通常使用下麵這行代碼來載入Spring的配置文件
ApplicationContext application = new ClassPathXmlApplicationContext("webmvc.xml")
,那麼這行代碼到底進行了怎麼的操作,接下來就一探究境,看看是如何載入配置文件的
Spring的配置文件
這個配置文件對於已經學會使用SpringMVC的你來說已經再熟悉不過了
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="study" class="com.xiaobai.Student">
</bean>
</beans>
那麼Spring是如何進行Bean的註冊的呢?經過這幾天的源碼查看我寫下了這篇文章來作為筆記,
因為我剛開始看Spring的源碼,裡面有些內容可能理解的不是很到位,有錯誤請指出
源碼查看
再此之前我先bb幾句,為了方便查看源碼,可以去GitHub上下載Spring的源碼導入到Idea或者是eclipse中這樣查看起來更方便些,同時還可以在上面寫一些註釋
既然使用的是ClassPathXmlApplicationContext("webmvc.xml")
那就找到這個類的單參構造器查看跟蹤下源碼
/**
* Create a new ClassPathXmlApplicationContext, loading the definitions
* from the given XML file and automatically refreshing the context.
* @param configLocation resource location
* @throws BeansException if context creation failed
* 這個是創建 了 一個 ClassPathXmlApplicationContext,用來從給的XMl文件中載入規定
*/
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
這裡調用的是本類中的另外一個三個參數的構造方法,便進入到了下麵這些代碼中
/**
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @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, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//設置配置文件的路徑
setConfigLocations(configLocations);
if (refresh) {
//重要的方法,需要進入查看
refresh();
}
}
這裡來說下這個方法的參數的意思:
- configLocations:這個裡面保存的是配置文件的路徑
- Refresh:是否自動刷新上下文
- parent:父上下文
設置資源載入器
要跟蹤下super(parent)
這行代碼,在它的父類中(AbstractApplicationContext類裡面),有下麵的代碼,這段代碼的作 用是獲取一個SpringResource的載入器用來載入資源文件(這裡你可以理解為是為了載入webmvc.xml配置文件做前期的準備)
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
//下麵的方法在PathMatchingResourcePatternResolver類中,為了查看方便我將這兩個方法寫在了一起
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
在PathMatchingResourcePatternResolver構造方法中就設置了一個資源載入器
設置Bean信息位置
這個裡面有一個setConfigLocations
方法,這個裡面會設置Bean配置信息的位置,這個方法的所在的類是AbstractRefreshableConfigApplicationContext
,它和CLassPathXmlApplicationContext
之間是繼承的關係
@Nullable
private String[] configLocations;
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
這裡面的configLocations的是一個數組,setConfigLocations
方法的參數是一個可變參數,這個方法的作用是將多個路徑放到configLocations數組中
閱讀refresh
這個方法可以說是一個非常重要的一個方法,這在個方法裡面規定了容器的啟動流程,具體的邏輯通過ConfigurableApplicationContext
介面的子類實現
@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 {
//這裡面的代碼我刪除掉了,因為我們本文是看的解析XML創建 Bean的文章,這裡的代碼暫時用不到,我就刪除了,要不然代碼太多了
}
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.
destroyBeans();
// Reset 'active' flag.
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...
resetCommonCaches();
}
}
}
Bean的配置文件是在這個方法裡面的refreshBeanFactory方法來處理的,這個方法是在AbstractRefreshableApplicationContext
類中實現的
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(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);
}
}
這裡有一個方法是loadBeanDefinitions(beanFactory)
在這個方法裡面就開始解析配置文件了,進入這個方法
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
//Bean讀取器實現載入的方法
loadBeanDefinitions(beanDefinitionReader);
}
進入到loadBeanDefinitions(XmlBeanDefinitionReader reader)
方法
XML Bean讀取器載入Bean配置資源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//獲娶Bean配置資源的位置
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
但是本文的教程是通過ClassPathXmlApplicationContext來舉的例子,getConfigResources()方法返回的是空的,就執行下麵的分支
說點和本文有關也有可能沒有關係的話
當代碼看到這裡,學習過設計模式的同鞋可能會發現我們看過的這些代碼里也涉及到了委派模式
和策略模式
因為Spring框架中使用到了很多的設計模式,所以說在看一些框架源碼的時候,我們儘可能的先學習下設計模式,不管是對於看源碼來說或者是對於在公司中工作都是啟到了很重要的作用,在工作中使用了設計模式對於以後系統的擴展或者是維護來說都是比較方便的。當然學習設計模式也是沒有那麼的簡單,或許你看了關於設計模式的視頻或者是一些書籍,但是在工作中如果是想很好的運用出來,還是要寫很多的代碼和常用設計模式的。
學習設計模式也是投入精力的,Scott Mayer在《Effective C++》也說過:C++新手和老手的區別就是前者手背上有很多的傷疤。
未完....