大佬教程收集整理的这篇文章主要介绍了【学习底层原理系列】重读spring源码3-加载beanDefinition的方法obtainFreshBeanFactory,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
定义BeanFactory,并加载以下两种bean的定义,装配到BeanFactory:
1.配置文件中定义的bean
2.通过<context:component-scan base-package="..." />配置的路径下的,且经过相应注解标注的所有类,注解包括:@Controller、@service、@Component、@Repository
主要流程总结:
1.创建BeanFactory:DefaultListableBeanFactory
2.解析web.xml配置,读取spring配置文件,封装为resource对象
3.把resource对象封装为Document对象
4.开始层层遍历Document的节点。
以下是细节:
先来看该方法的实现,注:这里会把无关代码删掉,以方便阅读。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//刷新bean工厂
this.refreshBeanFactory();
//创建bean工厂
return this.getBeanFactory();
}
重点看刷新bean工厂部分:
protected final void refreshBeanFactory() throws BeansException { //创建bean工厂 DefaultListableBeanFactory beanFactory = createBeanFactory(); //这里加载beanDefinition,并赋给bean工厂 loadBeanDefinitions(beanFactory); }
createBeanFactory()好理解,就是new了个工厂对象。有了工厂对象后,就需要往里面装载东西,装什么呢?这里是
接下来看loadBeanDefinitions(beanFactory)方法的具体实现:创建xml文件读取器
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 以下这一堆内容就是为了准备一个xml文件读取器,仅作了解
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setresourceLoader(this);
beanDefinitionReader.setEntityResolver(new resourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
//这里才是核心,加载beanDefinition的工作还没开始
loadBeanDefinitions(beanDefinitionReader);
}
继续跟进去,这里依然“没干正事”:加载spring配置文件
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
resource[] configresources = getConfigresources();
if (configresources != null) {
reader.loadBeanDefinitions(configresources);
}
String[] configLOCATIOns = getConfigLOCATIOns();
if (configLOCATIOns != null) {
//核心代码
reader.loadBeanDefinitions(configLOCATIOns);
}
}
接着看核心代码
public int loadBeanDefinitions(String LOCATIOn, Set<resource> actualresources) throws BeanDefinitionStoreException { resourceLoader resourceLoader = getresourceLoader(); if (resourceLoader instanceof resourcePatternResolver) { // 通配符模式匹配资源,转换为resource对象。spring提供了多种resourceLoader,根据通配符匹配,生成对应类型的resource resource[] resources = ((resourcePatternResolver) resourceLoader).getresources(LOCATIOn); //【继续把加载工作往后放】 int loadCount = loadBeanDefinitions(resources);return loadCount; } } else { // 以绝对路径加载单个资源文件,转换为resource对象 resource resource = resourceLoader.getresource(LOCATIOn); //【继续把加载工作往后放】 int loadCount = loadBeanDefinitions(resourcE);return loadCount; } }
通过上面一步,把配置资源转化为resource对象,然后作为参数传入loadxxx方法里进行解析。
进入下面的实现发现,依然在做准备工作:将resource读取为流
public int loadBeanDefinitions(Encodedresource encodedresourcE) throws BeanDefinitionStoreException { // 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(); Inputsource inputsource = new Inputsource(inputStream); if (encodedresource.getEncoding() != null) { inputsource.setEncoding(encodedresource.getEncoding()); } //终于到do...是不是这里就开始真正的执行加载了? return doLoadBeanDefinitions(inputsource, encodedresource.getresource()); } catch (IOException eX) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedresource.getresource(), eX); } finally { currentresources.remove(encodedresourcE); if (currentresources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
来看下,删除非核心代码,就做了两件事,先读取资源对象resource,封装成Document对象;再“注册”beanDefinition。
protected int doLoadBeanDefinitions(Inputsource inputsource, resource resourcE)
throws BeanDefinitionStoreException {
//生成Document对象
Document doc = doLoadDocument(inputsource, resourcE);
//注册BeanDefinition
return registerBeanDefinitions(doc, resourcE);
}
中间又经历了n个准备环境,最终进入方法parseBeanDefinitions,拿到了Document对象的根节点,开始调用解析方法解析节点:
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);
}
}
具体的解析逻辑,可以参考以下文章:https://blog.csdn.net/v123411739/article/details/86669952
BeanDefinition包含的主要内容:
@todo
解析完成后,依然是注入到BeanFactory中缓存起来,供后续使用,主要的内容是两部分:
1.beanDefinitionNames
2.beanDefinitionMap
总结:
obtainFreshBeanFactory()方法的主要作用:1.创建beanFactory2.根据web.xml中contextConfigLOCATIOn配置的路径,读取Spring配置文件,封装为resource3.根据resource加载XML配置文件(bean文件)并解析为Document对象4.遍历Document,解析为beanDefinition。
以上是大佬教程为你收集整理的【学习底层原理系列】重读spring源码3-加载beanDefinition的方法obtainFreshBeanFactory全部内容,希望文章能够帮你解决【学习底层原理系列】重读spring源码3-加载beanDefinition的方法obtainFreshBeanFactory所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。