Spring中BeanFactory解析bean詳解
在該文中來講講Spring框架中BeanFactory解析bean的過程,該文之前在小編原文中有發(fā)表過,先來看一個(gè)在Spring中一個(gè)基本的bean定義與使用。
package bean;
public class TestBean {
private String beanName = "beanName";
public String getBeanName() {
return beanName;
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
Spring配置文件root.xml定義如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"> <bean id="testBean" class="bean.TestBean"> </beans>
下面使用XmlBeanFactory來獲取該bean:
public class BeanTest {
private static final java.util.logging.Logger logger = LoggerFactory.getLogger(BeanTest.class);
@Test
public void getBeanTest() {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("root.xml"));
TestBean bean = factory.getBean("testBean");
logger.info(bean.getBeanName);
}
}
這個(gè)單元測(cè)試運(yùn)行結(jié)果就是輸出beanName,上面就是Spring最基本的bean的獲取操作,這里我用BeanFactory作為容器來獲取bean的操作并不多見,在企業(yè)開發(fā)中一般是使用功能更完善的ApplicationContext,這里先不討論這個(gè),下面重點(diǎn)講解使用BeanFactory獲取bean的過程。
現(xiàn)在就來分析下上面的測(cè)試代碼,看看Spring到底為我們做了什么工作,上面代碼完成功能的流程不外乎如此:
1. 讀取Spring配置文件root.xml;
2. 根據(jù)root.xml中的bean配置找到對(duì)應(yīng)的類的配置,并實(shí)例化;
3. 調(diào)用實(shí)例化后的對(duì)象輸出結(jié)果。
先來看看XmlBeanFactory源碼:
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
從上面可以看出XmlBeanFactory繼承了DefaultListableBeanFactory,DefaultListableBeanFactory是Spring注冊(cè)加載bean的默認(rèn)實(shí)現(xiàn),它是整個(gè)bean加載的核心部分,XmlBeanFactory與它的不同點(diǎn)就是XmlBeanFactory使用了自定義的XML讀取器XmlBeanDefinitionReader,實(shí)現(xiàn)了自己的BeanDefinitionReader讀取。
XmlBeanFactory加載bean的關(guān)鍵就在于XmlBeanDefinitionReader,下面看看XmlBeanDefinitionReader的源碼(只列出部分):
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
private ProblemReporter problemReporter = new FailFastProblemReporter();
private ReaderEventListener eventListener = new EmptyReaderEventListener();
private SourceExtractor sourceExtractor = new NullSourceExtractor();
private NamespaceHandlerResolver namespaceHandlerResolver;
private DocumentLoader documentLoader = new DefaultDocumentLoader();
private EntityResolver entityResolver;
private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);
}
XmlBeanDefinitionReader繼承自AbstractBeanDefinitionReader,下面是AbstractBeanDefinitionReader的源碼(只列出部分):
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
protected final Log logger = LogFactory.getLog(getClass());
private final BeanDefinitionRegistry registry;
private ResourceLoader resourceLoader;
private ClassLoader beanClassLoader;
private Environment environment;
private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
}
XmlBeanDefinitionReader主要通過以下三步來加載Spring配置文件中的bean:
1. 通過繼承自AbstractBeanDefinitionReader中的方法,使用ResourLoader將資源文件(root.xml)路徑轉(zhuǎn)換為對(duì)應(yīng)的Resource文件;
2. 通過DocumentLoader對(duì)Resource文件進(jìn)行轉(zhuǎn)換,將Resource文件轉(zhuǎn)換為Ducument文件;
3. 通過DefaultBeanDefinitionDocumentReader類對(duì)Document進(jìn)行解析,最后再對(duì)解析后的Element進(jìn)行解析。
了解以上基礎(chǔ)后,接下來詳細(xì)分析下一開始例子中的代碼:
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("root.xml"));
先看看下面XmlBeanFactory初始化的時(shí)序圖來進(jìn)一步了解這段代碼的執(zhí)行,

在這里可以看出BeanTest測(cè)試類通過向ClassPathResource的構(gòu)造方法傳入spring的配置文件構(gòu)造一個(gè)Resource資源文件的實(shí)例對(duì)象,再通過這個(gè)Resource資源文件來構(gòu)造我們想要的XmlBeanFactory實(shí)例。在前面XmlBeanFactory源碼中的構(gòu)造方法可以看出,
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
this.reader.loadBeanDefinition(resource)就是資源加載真正的實(shí)現(xiàn),時(shí)序圖中XmlBeanDefinitionReader加載數(shù)據(jù)就是在這里完成的。
接下來跟進(jìn)this.reader.loadBeanDefinition(resource)方法里面,
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
}
在loadBeanDefinition(resource)方法里對(duì)資源文件resource使用EncodedResource進(jìn)行編碼處理后繼續(xù)傳入loadBeanDefinitions方法,繼續(xù)跟進(jìn)loadBeanDefinitions(new EncodedResource(resource))方法源碼:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// 通過屬性記錄已加載的資源
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 從resource中獲取對(duì)應(yīng)的InputStream,用于下面構(gòu)造InputSource
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 調(diào)用doLoadBeanDefinitions方法
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();
}
}
}
繼續(xù)跟進(jìn)doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法,這是整個(gè)bean加載過程的核心方法,在這個(gè)方法執(zhí)行bean的加載。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
/* 省略一堆catch */
}
跟進(jìn)doLoadDocument(inputSource, resource)源碼:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
在doLoadDocument(inputSource, resource)方法里就使用到了前面講的documentLoader加載Document,這里DocumentLoader是個(gè)接口,真正調(diào)用的是其實(shí)現(xiàn)類DefaultDocumentLoader的loadDocument方法,跟進(jìn)源碼:
public class DefaultDocumentLoader implements DocumentLoader {
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
}
從源碼可以看出這里先創(chuàng)建DocumentBuilderFactory,再用它創(chuàng)建DocumentBuilder,進(jìn)而解析inputSource來返回Document對(duì)象。得到Document對(duì)象后就可以準(zhǔn)備注冊(cè)我們的Bean信息了。
在上面的doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法中拿到Document對(duì)象后下面就是執(zhí)行registerBeanDefinitions(doc, resource)方法了,看源碼:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(getEnvironment());
// 還沒注冊(cè)bean前的BeanDefinition加載個(gè)數(shù)
int countBefore = getRegistry().getBeanDefinitionCount();
// 加載注冊(cè)bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 本次加載注冊(cè)的BeanDefinition個(gè)數(shù)
return getRegistry().getBeanDefinitionCount() - countBefore;
}
這里的doc就是上面的loadDocument方法加載轉(zhuǎn)換來的,從上面可以看出主要工作是交給BeanDefinitionDocumentReader的registerBeanDefinitions()方法實(shí)現(xiàn)的,這里BeanDefinitionDocumentReader是個(gè)接口,注冊(cè)bean功能在默認(rèn)實(shí)現(xiàn)類DefaultBeanDefinitionDocumentReader的該方法實(shí)現(xiàn),跟進(jìn)它的源碼:
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
到這里通過doc.getDocumentElement()獲得Element對(duì)象后,交給doRegisterBeanDefinitions()方法后就是真正執(zhí)行XML文檔的解析了,跟進(jìn)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)) {
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
到這里處理流程就很清晰了,先是對(duì)profile進(jìn)行處理,之后就通過parseBeanDefinitions()方法進(jìn)行文檔的解析操作,跟進(jìn)parseBeanDefinitions()方法源碼:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// 下面對(duì)bean進(jìn)行處理
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
上面if-else語(yǔ)句塊中的parseDefaultElement(ele, delegate)和delegate.parseCustomElement(ele)就是對(duì)Spring配置文件中的默認(rèn)命名空間和自定義命名空間進(jìn)行解析用的。在Spring的XML配置中,默認(rèn)Bean聲明就如前面定義的:
<bean id="testBean" class="bean.TestBean">
自定義的Bean聲明如:
<tx:annotation-driven />
XmlBeanFactory加載bean的整個(gè)過程基本就講解到這里了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Spring實(shí)戰(zhàn)之使用c:命名空間簡(jiǎn)化配置操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之使用c:命名空間簡(jiǎn)化配置操作,結(jié)合實(shí)例形式詳細(xì)分析了Spring使用c:命名空間簡(jiǎn)化配置的相關(guān)接口與配置操作技巧,需要的朋友可以參考下2019-12-12
在controller中如何設(shè)置接收參數(shù)的默認(rèn)值
這篇文章主要介紹了在controller中如何設(shè)置接收參數(shù)的默認(rèn)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java中的構(gòu)造方法(構(gòu)造函數(shù))與普通方法區(qū)別及說明
這篇文章主要介紹了Java中的構(gòu)造方法(構(gòu)造函數(shù))與普通方法區(qū)別及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
Java之字符串轉(zhuǎn)換成Java對(duì)象方式
這篇文章主要介紹了Java之字符串轉(zhuǎn)換成Java對(duì)象方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
詳解 Java HashMap 實(shí)現(xiàn)原理
這篇文章主要介紹了詳解 Java HashMap 實(shí)現(xiàn)原理的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下2021-03-03
SpringBoot文件訪問映射如何實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot文件訪問映射如何實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02

