本文以spring框架的XmlBeanFactory为入手点进行分析,只讲解了如何加载bean定义这块,希望能够以尽量简洁明了的方式给予有需要的朋友一定的帮助。
首先来打开该类的代码,我们将看到如下代码:
以下内容为程序代码: public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { } |
这个类的代码很简单,一个成员对象加两个构造函数,从这里我们可以看出,最重要的地方在于最后一个构造函数:
以下内容为程序代码: super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); |
第一句就是将父亲工厂交给父类的构造函数,实际上最后也就是把父工厂保存到类的parentBeanFactory成员对象中,这个对象是在AbstractBeanFactory抽象类中定义的,而这个父工厂也会一直传递到该抽象类进行保存。第二句就是整个类中最重要的地方了,顾名思义,它的目的是通过XmlBeanDefinitionReader这个XML的Reader从资源resource中(也就是你的配置文件)读取bean的定义。接下来我们打开XmlBeanDefinitionReader的loadBeanDefinitions方法,我们可看到在这个方法里代码就一行,调用了一个同名不同参的方法,而参数是EncodedResource的一个实例,这个类实际上是Resource的一个包装类,用来保存资源的Encode的,那接下来我们再看被调用的loadBeanDefinitions方法,这个方法里最主要的部分就是:
以下内容为程序代码: InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); |
这里的目的是将资源包装成一个InputSource,连同Resource作为参数传递到doLoadBeanDefinitions方法
DocumentBuilderFactory factory = createDocumentBuilderFactory();
if (logger.isDebugEnabled()) {
logger.debug(“Using JAXP implementation [” + factory + “]”);
}
DocumentBuilder builder = createDocumentBuilder(factory);
Document doc = builder.parse(inputSource);
return registerBeanDefinitions(doc, resource);
这个方法的目的一目了然,就是为了将资源解释成为Document对象,然后调用registerBeanDefinitions方法,这里不做详细解释,不了解的话请去看看关于JAXP的介绍。接下来我们打开registerBeanDefinitions方法:
以下内容为程序代码: public int registerBeanDefinitions(Document doc, Resource resource) throws BeansException { |
这里创建了一个XmlBeanDefinitionParser接口的实现,这个接口的具体类是DefaultXmlBeanDefinitionParser,这个接口很简单,只有registerBeanDefinitions一个方法,这个方法的作用也很明了,就是用来注册Bean的定义的,所以说类和方法的名字一定要起得有意义,这样可以让人一看就大概了解其作用,减少了很多阅读代码的痛苦。废话不多说,我们打开DefaultXmlBeanDefinitionParser的registerBeanDefinitions方法,这个类就是解释XML配置文件的核心类了,打开registerBeanDefinitions方法后我们看到如下代码:
以下内容为程序代码: public int registerBeanDefinitions(BeanDefinitionReader reader, Document doc, Resource resource) this.beanDefinitionReader = reader; logger.debug(“Loading bean definitions”); preProcessXml(root);//一个空方法用于扩展 return beanDefinitionCount; |
在这个方法当中,主要用于解释定义的有两个方法,一个是initDefaults,一个是parseBeanDefinitions,第一个方法是用来解释根元素的属性的,例如lazy-init, autowire等,而parseBeanDefinitions就是用来解释具体的bean定义了,方法代码如下:
以下内容为程序代码: protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException { |
其他标签具体如何被解释这里就不多说,相信大家也能看得懂,这里主要讲一下解释bean的的处理,我们注意以下代码:
以下内容为程序代码: else if (BEAN_ELEMENT.equals(node.getNodeName())) { beanDefinitionCount++; BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory()); } |
这里是当碰到一个bean标签的时候所进行的处理,也既是对bean的定义进行解释,可以看到parseBeanDefinitionElement方法的第一个参数就是bean则个元素,第二个参数表示该bean是否为内置的bean,从这里进行解释的bean都不可能是内置的,所以这里直接以false为参数,打开parseBeanDefinitionElement方法,就可以看到这个方法里就是对bean的内部的解释,也很简单,也不多讲了,呵呵(下班时间已经到了,所以就写这么多了,基本的流程也就这样,没什么特别难的地方。),对了,最后还有一点就是解释完后,bean的定义将会被保存到beanFactory中,这个beanFactory的实现就是XmlBeanFactory了,该beanFactory是在new的时候被传递到reader中的,就是该类中以下这行代码:
以下内容为程序代码: private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); |
补充:
我们看到XmlBeanFactory使用了DefaultListableBeanFactory作为它持有的IOC容器实现,在这个基础上,添加了XML形式的Bean定义信息的读取功能。从这个角度看,这个
DefaultListableBeanFactory是很重要的一个Spring IOC实现。下面我们可以看到上下文也和XmlBeanFactory一样,通过持有这个DefaultListableBeanFactory来获得基本的IOC容器的功能。通过编程式的使用DefaultListableBeanFactory我们可以看到IOC容器使用的一些基本过程:
ClassPathResource res = new ClassPathResource(“beans.xml”);
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
这些代码演示了以下几个步骤:
1. 创建IOC配置文件的抽象资源
2. 创建一个BeanFactory,这里我们使用DefaultListableBeanFactory
3. 创建一个载入bean定义信息的读取器,这里使用XmlBeanDefinitionReader来载入XML形式的bean定义信息,配置给BeanFactory
4. 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成,这样完成整个载入和注册bean定义的过程。我们的IoC容器就建立起来
这个基本过程我们可以看到,IOC容器建立的基本步骤,这些我们可以编程式的完成这些配置,但在Spring中,它提供的上下文已经为我们作了这些事情,所以从这个角度说上下文是一个高级形态上的IOC容器。更方便了用户的使用,相比于那些基本的IOC容器的BeanFactory实现,上下文除了提供基本的上面看到的容器的基本功能外,还为用户提供了以下的附加服务更方便的让客户使用容器:
• 可以支持不同的信息源,我们看到ApplicationContext扩展了MessageSource
• 访问资源, 体现在对ResourceLoader和Resource的支持上面,这样我们可以从不同地方得到bean定义资源,这样用户程序可以灵活的定义Bean定义信息
• 支持应用事件,继承了接口ApplicationEventPublisher,这样在上下文中引入了事件机制而
BeanFactory没有在上下文环境中,这些上下文提供的基础服务更丰富了基本IOC容器的功能。所以一般我们建议客户使用上下文作为IOC容器来使用 – 和XmlBeanFactory一样,上下文是通过持有DefaultListableBeanFactory这个基本的IOC容器实现来提供IOC容器的基本功能的,这一点可以在下面我们分析IOC容器的初始化过程中看得很清楚。
我们看到XmlBeanFactory使用了DefaultListableBeanFactory作为它持有的IOC容器实现,在这个基础上,添加了XML形式的Bean定义信息的读取功能。从这个角度看,这个
DefaultListableBeanFactory是很重要的一个Spring IOC实现。下面我们可以看到上下文也和XmlBeanFactory一样,通过持有这个DefaultListableBeanFactory来获得基本的IOC容器的功能。通过编程式的使用DefaultListableBeanFactory我们可以看到IOC容器使用的一些基本过程:
ClassPathResource res = new ClassPathResource(“beans.xml”);
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
这些代码演示了以下几个步骤:
1. 创建IOC配置文件的抽象资源
2. 创建一个BeanFactory,这里我们使用DefaultListableBeanFactory
3. 创建一个载入bean定义信息的读取器,这里使用XmlBeanDefinitionReader来载入XML形式的bean定义信息,配置给BeanFactory
4. 从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成,这样完成整个载入和注册bean定义的过程。我们的IoC容器就建立起来
这个基本过程我们可以看到,IOC容器建立的基本步骤,这些我们可以编程式的完成这些配置,但在Spring中,它提供的上下文已经为我们作了这些事情,所以从这个角度说上下文是一个高级形态上的IOC容器。更方便了用户的使用,相比于那些基本的IOC容器的BeanFactory实现,上下文除了提供基本的上面看到的容器的基本功能外,还为用户提供了以下的附加服务更方便的让客户使用容器:
• 可以支持不同的信息源,我们看到ApplicationContext扩展了MessageSource
• 访问资源, 体现在对ResourceLoader和Resource的支持上面,这样我们可以从不同地方得到bean定义资源,这样用户程序可以灵活的定义Bean定义信息
• 支持应用事件,继承了接口ApplicationEventPublisher,这样在上下文中引入了事件机制而
BeanFactory没有在上下文环境中,这些上下文提供的基础服务更丰富了基本IOC容器的功能。所以一般我们建议客户使用上下文作为IOC容器来使用 – 和XmlBeanFactory一样,上下文是通过持有DefaultListableBeanFactory这个基本的IOC容器实现来提供IOC容器的基本功能的,这一点可以在下面我们分析IOC容器的初始化过程中看得很清楚。