概覽
要了解spring的bean加載祖凫,可以從下面這段代碼入手
public static void main(String[] args) {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
MyBeanTest beanTest = (MyBeanTest) beanFactory.getBean("myBeanTest");
System.out.println(beanTest.getTestString());
}
代碼的邏輯很清楚
1.讀取beanFactoryTest.xml的內(nèi)容并創(chuàng)建bean實例存入beanFactory泥畅。
2.從beanFactory中獲取到myBeanTest侥袜。
3.調(diào)用myBeanTest的方法并打印參數(shù)卧须。
下面根據(jù)代碼逐步討論詳細過程
一淮阐、ClassPathResource
···
public ClassPathResource(String path) {
this(path, (ClassLoader)null);
}
public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader();
}
···
進入new ClassPathResource方法可以看到是這樣的辣垒,ClassPathResource類中包含很多構(gòu)造方法灌砖,不過這里用到的是這兩個璧函。首先我們傳入了一個path參數(shù)傀蚌,調(diào)用了第一個構(gòu)造方法,而第一個構(gòu)造方法會設(shè)置classLoader參數(shù)為null蘸吓,進而調(diào)用第二個構(gòu)造方法善炫。第二個構(gòu)造方法實現(xiàn)的邏輯大致是,處理path參數(shù)库继,使其標準化箩艺;驗證是否有自定義的classLoader,如果有則使用自定義classLoader宪萄,如果沒有艺谆,則使用默認的classLoader。這一步封裝了一個resource對象拜英,提供給XmlBeanFactory進行解析静汤。(關(guān)于類加載器可以參考:java classLoader學(xué)習(xí)筆記)
二、XmlBeanFactory
...
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}
...
核心是XmlBeanDefinitionReader的loadBeanDefinitions方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
這里會對resource進行再封裝居凶,封裝為EncodedResource對象撒妈,EncodedResource對象主要是用于對資源文件的編碼進行處理。設(shè)置了編碼屬性的時候排监,Spring會使用相應(yīng)的編碼作為輸入流的編碼。封裝之后再次轉(zhuǎn)入了loadBeanDefinitions方法杰捂。
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 {
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();
}
}
}
以上代碼太多舆床,核心的加載方法推測是doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
上面的代碼,在try代碼塊里只有三個步驟:
1.獲取對XML文件的驗證模式(驗證模式是為了保證XML文件的正確性)
2.加載XML文件嫁佳,得到對應(yīng)的Docunment
3.根據(jù)返回的Document注冊Bean信息
三挨队、Bean的解析與注冊
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
在registerBeanDefinitions方法中,傳入了轉(zhuǎn)化得到的Document和Resource蒿往,然后通過documentReader去解析得到的document盛垦,這里的documentReader.registerBeanDefinitions方法,實際使用的是DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法(createBeanDefinitionDocumentReader()這個方法返回的對象為DefaultBeanDefinitionDocumentReader)
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
在這里提取root瓤漏,提取到的root再次傳入doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "environment property must not be null");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
return;
}
}
// any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createHelper(readerContext, root, parent);
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
進入真正的解析方法 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;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
大致是循環(huán)取到每一個標簽元素腾夯,并進行解析。進入parseDefaultElement方法
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
再進入對bean標簽的解析
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
可以看到蔬充,解析完成后會調(diào)用BeanDefinitionReaderUtils.registerBeanDefinition方法進行bean的注冊蝶俱。
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 aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
載入Bean
當main方法在getBean時,首先會去獲取bean饥漫,獲取不到再調(diào)用creatrBean()榨呆,核心方法在AbstractAutowireCapableBeanFactory中,創(chuàng)建bean時庸队,先創(chuàng)建bean實例积蜻,再根據(jù)從beanFactory拿到的beanDefination填充bean的屬性闯割,在填充時,如果有依賴其他bean竿拆,則先創(chuàng)建被依賴的bean宙拉。依次創(chuàng)建完成,最后返回需要的bean實例如输。