【学习底层原理系列】重读spring源码3-加载beanDefinition的方法obtainFreshBeanFactory
xiaoyang_ 人气:0
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.创建beanFactory
2.根据web.xml中contextConfigLocation配置的路径,读取Spring配置文件,封装为Resource
3.根据Resource加载XML配置文件(bean文件)并解析为Document对象
4.遍历Document,解析为beanDefinition。
加载全部内容