上期文章:
Spring源碼分析系列(一)IOC容器的設(shè)計(jì)與實(shí)現(xiàn)(1)基礎(chǔ)容器的實(shí)現(xiàn)
上一章我們講了以xmlBeanFactory的方式實(shí)現(xiàn)的基礎(chǔ)IOC容器,這回我們接著講IOC容器的高級(jí)實(shí)現(xiàn)突梦。首先先看下ApplicationContext的接口設(shè)計(jì)路線,他在繼承了BeanFactory的基礎(chǔ)上還集成了國(guó)際化接口MessageSource捞烟、資源獲取ResourceLoader卖擅、事件機(jī)制ApplicationEventPublisher等,通過他們來實(shí)現(xiàn)基礎(chǔ)容器做不到的高級(jí)特性静稻。
下面我們來看三段代碼
//通過實(shí)際路徑
String xmlPath = "WebContent/WEB-INF/config/base/applicationContext.xml";
ApplicationContext ac = new FileSystemXmlApplicationContext(xmlPath);
user = ac.getBean(test.class);
//通過根目錄默認(rèn)
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
user = ac.getBean(LsjmUserServiceImpl.class);
//通過xmlwebApplication
ServletContext servletContext =request.getSession().getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
這三種方式都可以獲取到ApplicationContext對(duì)象脚作,我們以FileSystemXmlApplicationContext舉例葫哗。
首先是初始化,執(zhí)行構(gòu)造方法,然后按照繼承鏈進(jìn)行父類初始化劣针,最后refresh啟動(dòng)桨螺。
繼承鏈上的父類包括:
AbstractXmlApplicationContext
AbstractRefreshableConfigApplicationContext
AbstractRefreshableApplicationContext
AbstractApplicationContext
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();
}
}
我們繼續(xù)跟進(jìn)refresh方法,在AbstractApplicationContext類中看到了他的實(shí)現(xiàn)酿秸,這里是容器的準(zhǔn)備階段,調(diào)用了ConfigurableListableBeanFactory接口魏烫,那誰是他的實(shí)現(xiàn)類呢辣苏?我們接著往下走。
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
}
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
return this.getBeanFactory();
}
我們通過refreshBeanFactory繼續(xù)執(zhí)行哄褒,通過AbstractApplicationContext的繼承體系稀蟋,我們?cè)贏bstractRefreshableApplicationContext類中找到了他的實(shí)現(xiàn)方法∧派模看他的邏輯退客,先容器校驗(yàn)是否創(chuàng)建,存在就銷毀链嘀。然后我們還是通過構(gòu)建實(shí)體類DefaultListableBeanFactory來實(shí)現(xiàn)容器萌狂,這個(gè)我們后續(xù)會(huì)講解如何實(shí)現(xiàn)。
通過這段代碼怀泊,我們發(fā)現(xiàn):
無論是BeanFactory體系的基礎(chǔ)容器茫藏,還是ApplicationContext體系的高級(jí)容器,我們是要通過DefaultListableBeanFactory來實(shí)現(xiàn)霹琼。
protected final void refreshBeanFactory() throws Exception {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
this.loadBeanDefinitions(beanFactory);
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
根據(jù)基礎(chǔ)容器的構(gòu)建方式可知务傲,要實(shí)現(xiàn)容器首先要將xml中的結(jié)構(gòu),解析出來裝載到CurrentHashMap<BeanDefinition>對(duì)象中枣申,所以這里的思路是如何定位xml售葡。追蹤抽象方法this.loadBeanDefinitions(beanFactory),我們找到了他的實(shí)現(xiàn)類AbstractXmlApplicationContext忠藤,這里定義了兩種資源類型的加載方式我們按照String類型繼續(xù)跟代碼挟伙。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = this.getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = this.getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
進(jìn)入到AbstractBeanDefinitionReader類中,然后獲取資源加載器ResourceLoader根據(jù)ResourceLoader的類型不同對(duì)資源有不同的獲取方法當(dāng)為ResourcePatternResolver類型時(shí)熄驼,將ResourceLoader強(qiáng)轉(zhuǎn)類型后調(diào)用getResource()方法定位資源否則像寒,直接調(diào)用DefaultResourceLoader的getResource()方法定位資源。
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = this.getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
} else {
int count;
if (resourceLoader instanceof ResourcePatternResolver) {
Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
count = this.loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
return count;
} else {
Resource resource = resourceLoader.getResource(location);
count = this.loadBeanDefinitions((Resource)resource);
if (actualResources != null) {
actualResources.add(resource);
}
}
return count;
}
}
}
進(jìn)入到DefaultResourceLoader類瓜贾,getResource里會(huì)判斷傳入的路徑類型诺祸,是根目錄標(biāo)識(shí)的定位,還是Url標(biāo)識(shí)的定位祭芦,還是是實(shí)際路徑標(biāo)識(shí)的定位筷笨。FileSystemXmlApplicationContext屬于實(shí)際路徑所以我們繼續(xù)執(zhí)行g(shù)etResourceByPath方法。
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
Iterator var2 = this.protocolResolvers.iterator();
Resource resource;
do {
if (!var2.hasNext()) {
if (location.startsWith("/")) {
return this.getResourceByPath(location);
}
if (location.startsWith("classpath:")) {
return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());
}
try {
URL url = new URL(location);
return (Resource)(ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
} catch (MalformedURLException var5) {
return this.getResourceByPath(location);
}
}
ProtocolResolver protocolResolver = (ProtocolResolver)var2.next();
resource = protocolResolver.resolve(location, this);
} while(resource == null);
return resource;
}
getResourceByPath的實(shí)現(xiàn)類就在我們最開始的入口FileSystemXmlApplicationContext類中,這個(gè)方法會(huì)返回一個(gè)FileSystemResource對(duì)象胃夏,通過這個(gè)對(duì)象Spring就可以進(jìn)行I/O操作完成BeanDefinition定位轴或。
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
經(jīng)過上述操作我們完成了配置資源的定位工作,接下就是BeanDefinition的載入和解析仰禀。
讓我們回到AbstractBeanDefinitionReader類中的loadBeanDefinitions(String , Set<Resource>)方法上,getResource使我們獲得了資源的坐標(biāo)照雁,然后我們執(zhí)行this.loadBeanDefinitions,這里開始正式開始BeanDefinition的載入和解析答恶。
Resource resource = resourceLoader.getResource(location);
count = this.loadBeanDefinitions((Resource)resource);
繼續(xù)追蹤代碼在BeanDefinitionReader接口中我們按照xml解析的方式選擇XmlBeanDefinitionReader作為實(shí)現(xiàn)類饺蚊,這里將resource對(duì)象轉(zhuǎn)換為流并通過doLoadBeanDefinitions方法進(jìn)行讀取。具體的讀取過程都在doLoadDocument中悬嗓,最后將讀取的結(jié)果放入Document對(duì)象中并開始注冊(cè)污呼。
public int loadBeanDefinitions(EncodedResource encodedResource) {
InputStream inputStream = encodedResource.getResource().getInputStream();
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
// 具體的讀取過程都在doLoadDocument中
Document doc = this.doLoadDocument(inputSource, resource);
int count = this.registerBeanDefinitions(doc, resource);
return count;
}
我們繼續(xù)跟進(jìn)代碼registerBeanDefinitions,我們發(fā)現(xiàn)registerBeanDefinitions真正的實(shí)現(xiàn)類又回到了我們?cè)谧?cè)基礎(chǔ)容器時(shí)用到的DefaultBeanDefinitionDocumentReader類包竹。后續(xù)的注冊(cè)過程就不在敘述燕酷,參考基礎(chǔ)容器的實(shí)現(xiàn),高級(jí)容器與基礎(chǔ)容器的的核心本質(zhì)上都是一套東西周瞎。
最后關(guān)于AppliicationContext的高級(jí)特性部分苗缩,我們往回找,找到AbstractApplicationContext看refresh方法堰氓。在準(zhǔn)備好基礎(chǔ)的IOC容器后挤渐,我們開始逐步注冊(cè)那些高級(jí)特性。
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 啟動(dòng)注冊(cè)ioc容器
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
// 初始化消息
this.initMessageSource();
// 初始化時(shí)間機(jī)制
this.initApplicationEventMulticaster();
this.onRefresh();
//注冊(cè)監(jiān)聽器
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
}
}