为了更好的理解Spring中bean的注册原理,我们通过一个示例进行分析,代码如下:
public static void main(String[] args) {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-bean-test.xml"));
MyBean bean = (MyBean) beanFactory.getBean("myBean");
System.out.println("My name is : " + bean.getName());
}
其中配置文件如下(核心部分):
<beans>
<bean id="myBean" class="com.bean.MyBean" />
</beans>
XmlBeanFactory简介
- XmlBeanFactory继承自DefaultLisableBeanFactory,DefaultLisableBeanFactory是整个bean加载的核心部分,XmlBeanFactory通过继承DefaultLisableBeanFactory的方式,实现了个性化的XML读取器BeanDefinitionReader,用来从XML配置中读取Bean的定义。
- 因为Spring的大部分功能是以配置作为切入点的,因此XML配置文件的读取是Spring源码中的重要一环。
XmlBeanFactory详解
首先来看第一段代码:
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(“spring-bean-test.xml”));
这个里面ClassPathResource这里就不说了,主要来看new XmlBeanFactory,这个类中有两个构造函数:
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
通过源码可知,我们代码中的spring-bean-test.xml 作为参数传给了XmlBeanDefinitionReader 类,通过这个类的loadBeanDefinitions 方法进行读取,我们接着看loadBeanDefinitions ,这个方法是整个xml资源加载的重要方法,这个方法里面的核心逻辑如下:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
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();
}
}
从上面可以看到,核心的方法其实就是doLoadBeanDefinitions 方法,这个方法如下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
try中的语句很简单,主要就做了3件事:
- 获取对xml文件的验证模式
- 加载xml文件得到document对象
- 根据得到的document对象进行bean注册
验证模式的获取这里暂不介绍,下面我们来看获取document的方法loadDocument ,这里的documentLoader是一个接口类,真正调用的是它的实现类DefaultDocumentLoader,实现类中通过创建DocumentBuilderFactory来创建DocumentBuilder,进而解析inputSource来返回Document对象。
然后就是根据document来解析和注册bean的方法registerBeanDefinitions ,源码如下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
这个方法核心的逻辑就是通过创建BeanDefinitionDocumentReader 来解析xml文件并进行bean的注册,这个BeanDefinitionDocumentReader 也是一个接口类,实现类是DefaultBeanDefinitionDocumentReader ,源码如下:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
preProcessXml(root);
parseBeanDefinitions(root, delegate);
postProcessXml(root);
}
这个方法就是读取xml的root节点,通过parseBeanDefinitions 来循环读取下面的子节点进行Bean的注册,其中preProcessXml ,postProcessXml 两个方法是预留的一个拓展点,可以在子类中实现这两个方法在注册bean之前和之后进行一些自定义的处理。
总结
通过parseBeanDefinitions 将bean注册到容器中之后,通过下面的语句即可获得bean,并进行调用。
beanFactory.getBean(“myBean”) System.out.println("My name is : " + bean.getName());
到此,bean的注册过程就全部完成了,这个是spring中默认的配置解析流程,对于parseBeanDefinitions 中是如何详细进行xml各节点解析的,将在下篇文章中进行分享。
最后,欢迎大家指出文章中描述不详细的地方,后面我会持续根据大家的指正进行更新。
|