為什么需要BeanDefinition
我們在使用spring的時(shí)候旋膳,首先都會在xml或通過注解去定義一個(gè)bean,之后在IOC容器(ApplicationContext)創(chuàng)建一個(gè)bean的時(shí)础淤,都會去尋找這些bean定義的配置內(nèi)容,那這些配置內(nèi)容的封裝就是通過BeanDefinition來實(shí)現(xiàn)的继榆。
BeanDefinition是在IOC容器初始化的時(shí)候創(chuàng)建的亮元,也就是調(diào)用AbstractApplicationContext.refresh()方法觸發(fā),創(chuàng)建的過程一般分下面三個(gè)步驟:
- Bean配置資源的定位循头。這一步主要是依賴DefaultResourceLoader接口绵估,通過此接口IOC容器可以從不同的位置以特定的方式去加載bean配置。DefaultResourceLoader接口是有許多不同實(shí)現(xiàn)的卡骂,像熟知的
ClassPathXmlApplicationContext国裳,是從工程的根目錄去尋找配置;FileSystemXmlApplicationContext全跨,是從文件系統(tǒng)中去尋找配置缝左。完成這一步,就相當(dāng)于著我們想喝水就要先把井找到螟蒸。 - BeanDefinition的載入盒使。這一步是將配置資源轉(zhuǎn)化成IOC容器可以理解的BeanDefinition,也就是bean的抽象七嫌,可以讓IOC容器很方便的管理對象的定義和生成少办。這一步,就是用桶把水打上來诵原。
- BeanDefinition的注冊英妓。這一步是將BeanDefinition注冊到IOC容器(實(shí)際上就是把它放到容器中的一個(gè)Map里),之后在獲取bean時(shí)绍赛,IOC容器可以查找到BeanDefinition并進(jìn)行處理蔓纠。這一步,就是把桶里的水裝到碗里吗蚌,為以后各個(gè)來想喝水的人提供資源腿倚。
Resource定位
我們以ClassPathXmlApplicationContext為例,來分析一下整個(gè)BeanDefinition的創(chuàng)建流程蚯妇。
//一切開始的地方
public ClassPathXmlApplicationContext(
String[] configLocations/*配置文件位置*/, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
//IOC容器初始化敷燎,我們進(jìn)入到這個(gè)方法
refresh();
}
}
refresh()方法是在AbstractApplicationContext中實(shí)現(xiàn)的暂筝,它定義了IOC容器初始化的一些流程,其中調(diào)用了obtainFreshBeanFactory()這個(gè)方法硬贯,這個(gè)方法里又調(diào)用refreshBeanFactory()方法焕襟,此方法內(nèi)部才是BeanDefinition真正創(chuàng)建的地方。
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);
}
}
在refreshBeanFactory()這個(gè)方法當(dāng)中饭豹,它構(gòu)建了一個(gè)DefaultListableBeanFactory容器鸵赖,這個(gè)容器對BeanDefinition的載入和注冊有很大關(guān)系,之后我們在說拄衰。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 為給定的BeanFactory創(chuàng)建新的XmlBeanDefinitionReader它褪。
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 對環(huán)境信息和ResourceLoader注入,ResourceLoader的具體實(shí)現(xiàn)還是要看
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
關(guān)鍵是這個(gè)loadBeanDefinitions(beanDefinitionReader)翘悉,一步步斷點(diǎn)打下去回來到另外一個(gè)loadBeanDefinitions(EncodedResource encodedResource)方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
// 獲取Resource集合列赎,EncodedResource是增強(qiáng)了編解碼功能的一個(gè)封裝。
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
在這里通過流完成了資源文件讀入的過程镐确,之后我們進(jìn)入doLoadBeanDefinitions看看BeanDefinitions的加載過程。
BeanDefinition的載入
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
//在這里通過XML解析器獲取Document 對象
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//得到BeanDefinitionDocumentReader 來對XML的BeanDefinition進(jìn)行解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//具體解析過程在這里完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
這個(gè)registerBeanDefinitions()的實(shí)現(xiàn)是在DefaultBeanDefinitionDocumentReader類中饼煞,具體方法是doRegisterBeanDefinitions()源葫。
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
具體的BeanDefinition解析就是在這個(gè)BeanDefinitionParserDelegate完成的,他會把值從XML中取出來砖瞧,放到生成的BeanDefinitionHolder中息堂,BeanDefinitionHolder中持有生成的BeanDefinition,至此BeanDefinition理論上已經(jīng)生成完成块促。
BeanDefinition的注冊
在parseBeanDefinitions()中荣堰,解析完成的BeanDefinitionHolder會調(diào)用此方法來完成BeanDefinition到IOC容器的注冊,這個(gè)注冊實(shí)際上就是把BeanDefinition放到DefaultListableBeanFactory容器中的beanDefinitionMap中竭翠。完成注冊之后振坚,當(dāng)創(chuàng)建新Bean的時(shí)候,就可以拿BeanDefinition來作為藍(lán)本創(chuàng)建斋扰。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}