spring bean加载源码

首先研究spring源码需要先把源码放到开发工具中,这样调试更加的方便。可以在github上下载spring源码,然后双击里面的import-into-eclipse.bat文件,接着一直下一步,就可以将代码改为可以在eclipse上运行的项目。

然后开始写个测试的demo

实体类MyYesyBean

public class MyTestBean {
private String testStr="testStr";


public String getTestStr() {
return testStr;
}


public void setTestStr(String testStr) {
this.testStr = testStr;
}

}

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.xsd">


<bean id="myTestBean" class="com.spring.MyTestBean" />

</beans>

测试类

public class BeanFactoryTest {

@Test
public void testSimpleLoad() {
//加载
Resource resource=new ClassPathResource("spring/beans.xml");
BeanFactory bf=new XmlBeanFactory(resource);
MyTestBean bean=(MyTestBean) bf.getBean("myTestBean");
System.out.println(bean.getTestStr());
}
}

测试类结构上就可以看出,首先使用ClassPathResource加载xml文件返回Resource,然后使用XmlBeanFactory的构造方法初始化一个BeanFactory,最后调用BeanFactory的getBean()方法来获取加载的对象。

下面说实现细节,ClassPathResource类的代码相对比较简单,就是通过传入的字符串路径来获取这个资源的对象,需要注意的是传入的路径如果前面包含"/",构造方法中会自动去掉。

public ClassPathResource(String path, @Nullable 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());
}

接着是将Resource传入到XmlBeanFactory,从名字上就可看出来这个是对Xml文件的处理了

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}

先是调用父类(DefaultListableBeanFactory)构造方法,DefaultListableBeanFactory再调用父类构造方法

public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}

ignoreDependencyInterface的主要功能是忽略给定接口的自动装配功能,举个例子,spring在获取A的Bean的时候如果其属性B还没有初始化,那么spring会自动初始化B。但是如果B实现了BeanNameAware或BeanFactoryAware或BeanClassLoaderAware,那么spring就会忽略对它的自动装配,而是通过其他途径对其进行装配。

this.reader.loadBeanDefinitions(resource);才是加载真正的主入口

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}

new EncodedResource(resource)是用于对文件的编码进行处理,处理完毕后开始进入loadBeanDefinitions方法,loadBeanDefinitions方法通过传入的EncodedResource先是获取到Resource的输入流,然后通过这个输入流获取到了InputSource。最后进入逻辑的核心部分doLoadBeanDefinitions。

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<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//从encodedResource中获取已经封装的Resource对象并再次从Resource中获取其中的inputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//InputSource这个类并不来自于Spring,它的全路径是org.xml.sax.InputSource
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方法里面除开对异常的处理就是获取Document,然后再注册。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
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);
}
}

doLoadDocument中执行了很多操作,但是总结来说就是以下几点:

1、调用子类DefaultDocumentLoader类的loadDocument方法来解析xml。

2、找到DTD声明文件,用来给SAX继续读取XML文件。

3、检测验证模式,spring用来检测验证模式的办法就是判断是否包含DOCTYPE,如果包含就是DTD,如果不包含就是XSD

最后就是注册了

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录统计前BeanDefinition的加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载及注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//记录本次加载的beanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}

注册暂时还有一些坑没有理解,以后想通了再来补全把。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值