首先研究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;
}
注册暂时还有一些坑没有理解,以后想通了再来补全把。