spring源码分析之基于xml配置文件的bean初始化分析
spring初始化bean有xml与注解两种方式,虽然注解可以减少很多xml配置,但还是要在xml配置文件里面配置好标签之后才能生效,里面涉及到很多对xml命名空间的解析,对后面模块的分析也有帮助,所以先从xml解析初始化分析走,注解方式的注入就分析个大概了。随便吐槽下,分析开源框架最忌讳的就是陷入到分析解析xml去了,现在流行的框架差不多都会涉及到解析xml,每研究一个开源框架,分析xml都会花很多时间,其实不想写出来,因为没有技术含量,但是有时候跟同事交流又会问道怎么解析xml的,真的好蛋疼。由于spring有很多自定义标签而初始化了很多内嵌对象,所以还是写下初始化流程吧。
这里先声明下,下面的分析只针对单个bean的初始化,并没有涉及到其他复杂场景的bean初始化,配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd"
default-autowire="default">
<bean id="simple" class="mytest.bean.simpleBean.SimpleBean"/>
</beans>
就如此简单。
初始化流程
从最简单的初始化一个bean进行分析,以ClassPathXmlApplicationContext为例,由于该类继承了AbstractApplicationContext,所以初始化的时候都是从refresh方法开始的。前面分析了,容器里面最核心的一个类是DefaultListableBeanFactory,在初始化解析器之前就要先创建该类。解析xml为BeanDefinition在obtainFreshBeanFactory完成,下面就详细分析改方法。
初始化xml解析器
参考前面章节的加载bean的入口在AbstractRefreshableApplicationContext的loadBeanDefinitions方法,由于XmlWebApplicationContext与一般的应用加载bean的配置文件有些不一样,所以loadBeanDefinitions是单独由实现的,其实内部还是由XmlBeanDefinitionReader完成bean的解析。ClassPathXmlApplicationContext、FileSystemXmlApplicationContext都由AbstractXmlApplicationContext的loadBeanDefinitions实现了初始化xml解析器操作。参考代码:
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
在实例化ResourceEntityResolver解析器时,在父类DelegatingEntityResolver初始化了BeansDtdResolver、PluggableSchemaResolver,这两种解析器分别解析dtd与schema两种方式的xml。
解析配置文件为Document对象
实例化XmlBeanDefinitionReader的时候,内部的DefaultDocumentLoader就创建好了,此类完成创建Document对象。参考loadDocument方法:
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
由于实例化的时候已经设置了解析器为ResourceEntityResolver,所以直接看resolveEntity方法,有父类DelegatingEntityResolver去执行解析:
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (systemId != null) {
if (systemId.endsWith(DTD_SUFFIX)) {
return this.dtdResolver.resolveEntity(publicId, systemId);
}
else if (systemId.endsWith(XSD_SUFFIX)) {
return this.schemaResolver.resolveEntity(publicId, systemId);
}
}
return null;
}
由于spring都是使用的schema方式的定义的xml,所有使用PluggableSchemaResolver去解析xml,从这个类中就可以看到spring定义的xml标签约束了,在没个模块下面有个spring.schema文件,也就是以后的标签必须按照定义的schema规范来写标签。
解析DOM节点
上面分析了已经创建好Docment对象了,接下来就解析DOM节点了。spring有一般的标签(如
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;
}
需要注解的就是createReaderContext创建XmlReaderContext的时候,就有getNamespaceHandlerResolver方法把spring.handlers文件里面的内置对象加载进来了,用于解析命名空间类型的标签。DefaultBeanDefinitionDocumentReader将xml解析最后生成BeanDefinitionHolder,参考方法processBeanDefinition:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//注册bean到容器(DefaultListableBeanFactory)
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//触发注册监听器
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
这里比较重要的一个对象就是BeanDefinitionHolder的内部对象BeanDefinition,它保存了bean的所有信息,包括各种依赖关系,随便画了一个简单的类图:
至此解析XML就完成了。这里虽然没有介绍解析命名空间,但是后面分析注解的时候会顺带分析了,分析完自己也画了个时序图,包含了对命名空间类型节点的解析流程:
实例化bean
由上面的分析就只之,解析xml保存到DefaultListableBeanFactory中的只是BeanDefinition,并没有实例化。上面都初始化都发生在AbstractApplicationContext的obtainFreshBeanFactory方法,在refresh方法中可以看到,有个finishBeanFactoryInitialization,只要是singleton的bean,这里就是实例化的入口了,最终会走到DefaultListableBeanFactory.preInstantiateSingletons(),遍历解析xml时保存的beanDefinitionNames,并调用getBean方法,参考AbstractBeanFactory的方法:
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
......
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
//开始创建bean,由AbstractAutowireCapableBeanFactory实现
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
......
return (T) bean;
}
注意获取bean的时候有个createBean方法,参考《spring源码分析之Ioc容器分析》,AbstractBeanFactory的子类就是AbstractAutowireCapableBeanFactory,所以这个类就是创建bean入口。由于不涉及到代理bean的创建,所以直接参考doCreateBean方法:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//实例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
}
将实例化后的bean设置到BeanWrapper (转换属性类型)
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
return getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
}, getAccessControlContext());
}
else {
//bean必须有空构造函数
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
需要注意的是实例化的bean必须有空的构造函数,参考SimpleInstantiationStrategy类:
@Override
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (bd.getMethodOverrides().isEmpty()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
@Override
public Constructor<?> run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
}
});
}
else {
//必须有空构造函数,不然反射就抛异常
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Exception ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
然后通过反射实例化的bean添加到BeanWrapperImpl,看配置文件是否注册了转换器,最终BeanWrapperImpl通过getWrappedInstance就获取到实例化后的bean了。实例化bean也画了一个时序图,便于以后复习: