网上有很多spring的源码分析教程,也有很多类图,但是spring的源码特别复杂,一层嵌套一层,刚开始看,很难受,越看越懵,所以我买了本书《spring源码深度解析》。跟着书的内容来学习吧。
首先是spring读取配置文件,spring有一个专门读取配置的较为顶级的接口-Resource。假设我们使用ClassPathResource进行resource的实例化。
//得到resource的实例
ClassPathResource resource=new ClassPathResource("beans.xml");
接着,既然有了resource实例,那么就需要分析里面的东西了。我们常用spring的时候,是使用xml的形式(不考虑springBoot),那么对应的工厂应该就是xmlFactory了。
ClassPathResource resource=new ClassPathResource("beans.xml");
//这里XmlBeanFactory在4.3.4里面已经过时了
XmlBeanFactory factory=new XmlBeanFactory(resource);
//ClassPathXmlApplicationConetext用于比xmlBeanFactory更加强大的功能。
/*
*BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
*/
//由于书上是从xmlBeanFactory开始的,那么我们就用xmlBeanFactory开始分析
//到xmlBeanFactory的构造函数里。
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
//这里先不管
super(parentBeanFactory);
//由方法的名字分析,是为了加载bean吧
this.reader.loadBeanDefinitions(resource);
}
//接着进入loadBeanDefinitions(resource)方法
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));//(1)
}
//再进去,关键性代码
//getResource是将(1)中的resource重新得到,可能是resource有编码的要求
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();
}
//---------------------------------------------------------------------------------------
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//读方法,返回类型,大致猜测一下,应该是说对xml进行解析。返回解析过后的doc
Document doc = doLoadDocument(inputSource, resource);
//获取到正确的doc后,应该就是注册并加载bean了吧(重头戏)
return registerBeanDefinitions(doc, resource);
}
//省略了诸多catch语句:解析失败过后的抛错
}
接下来查看重头戏:注册并加载bean。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//读方法名:统计之前的注册次数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载并注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//(1)
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//继续进入(1)
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//root:得到节点
Element root = doc.getDocumentElement();
//哈哈,应该是核心了吧,真正的加载bean
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//profile的处理,判断是什么环境:这就达到了不同环境,不同配置文件的效果。
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.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//解析前处理:空实现
preProcessXml(root);
//还没有注册bean,真的是很深。接着看
parseBeanDefinitions(root, this.delegate);
//解析后处理:空实现
postProcessXml(root);
this.delegate = parent;
}
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)) {
//spring的标签处理bean
parseDefaultElement(ele, delegate);
}
else {
//自定义标签:处理bean
//这里我现在不想研究,等以后吧,因为目前学习的重点不在于此
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}