Spring源码解读之XmlBeanFactory
Spring源码解读之XmlBeanFactory
首先感谢《Spring源码深度解析》郝佳。接下来的Spring源码解读系列,都是读了郝佳的书后的观后感。再次感谢他,带我走进了源码的世界。
BeanFactory factory= new XmlBeanFactory (new ClassPathResource("D:\\Project\\Eclipse\\Spring_Maven\\src\\main\\resources\\spring_beans.xml" ));
new ClassPathResource(String path) 对资源文件进行封装,这个前面已经说过了,接下来主要是过一遍XmlBeanFactory的流程。
public XmlBeanFactory (Resource resource ) throws BeansException {
this(resource , null);
}
public XmlBeanFactory (Resource resource , BeanFactory parentBeanFactory ) throws BeansException {
super(parentBeanFactory );
this.reader.loadBeanDefinitions( resource);
}
1:当我们创建XmlBeanFactory对象的时候,调用了含参数(Resource)的构造函数,在构造函数中再次调用内部构造函数,此时的参数变成了(Resource, BeanFactory)。看一下XmlBeanFactory的完整类名
public class XmlBeanFactory extends DefaultListableBeanFactory
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory
由此,我们找到抽象类AbstractAutowireCapableBeanFactory就可以了,为什么这么说,我们能看出DefaultListableBeanFactory是XmlBeanFactory的父类,现在进入DefaultListableBeanFactory看一下
public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
super(parentBeanFactory );
}
在DefaultListableBeanFactory中参数为BeanFactory的构造函数调用了其父类的构造函数,所以我们要继续追踪到其父类 AbstractAutowireCapableBeanFactory
/**
* Create a new AbstractAutowireCapableBeanFactory with the given parent.
* @param parentBeanFactory parent bean factory, or {@code null} if none
*/
public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
this();
setParentBeanFactory( parentBeanFactory);
}
第二句代码是个传递参数的过程,这个还好理解,我们又看到了this,我们还得继续找构造函数
/**
* Create a new AbstractAutowireCapableBeanFactory.
*/
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware. class);
ignoreDependencyInterface(BeanFactoryAware. class);
ignoreDependencyInterface(BeanClassLoaderAware. class);
}
又看到了一个super(),好愁人,接着找AbstractAutowireCapableBeanFactory的父类AbstractBeanFactory。
/**
* Create a new AbstractBeanFactory.
*/
public AbstractBeanFactory () {
}
还好,什么都没有。回到AbstractAutowireCapableBeanFactory 类, 有三个以ignoreDependencyInterface开头的方法,
/**
* Ignore the given dependency interface for autowiring.
* <p>This will typically be used by application contexts to register
* dependencies that are resolved in other ways, like BeanFactory through
* BeanFactoryAware or ApplicationContext through ApplicationContextAware.
* <p>By default, only the BeanFactoryAware interface is ignored.
* For further types to ignore, invoke this method for each type.
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.context.ApplicationContextAware
*/
public void ignoreDependencyInterface(Class<?> ifc) {
this.ignoredDependencyInterfaces .add(ifc);
}
看意思是说给参数ignoreDependencyInterfaces添加值,原来这个的主要功能是忽略给定接口的自动装配功能。首先我们要知道 spring是可以自动装配的,比如说A 当中有属性B,spring在获取A的bean的时候如果B还没有被初始化,那么Spring会自动初始化B。如果B实现了BeanFactory接口,那么久不会初始化B。代码上面的英文说的很清楚,以后要多看看。
总结一下刚才都记录了什么。首先是XmlBeanFactory的构造函数之间的调用,然后设置了parentBeanFactory,以及指定了哪些接口忽略自动装配功能,这些内容是通过子类实现父类的方法来实现的。所以说就讲了两个事,一个是构造函数的事,另外一个是父类和子类的事。
2:this.reader.loadBeanDefinitions(Source)这句代码才是整个资源加载的切入点。下面先看看reader是什么鬼
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
reader是XmlBeanDefinitionReader的实例,我们看到,在创建这个对象的时候,把自己给传过去了,赶紧过去看看
/**
* Create new XmlBeanDefinitionReader for the given bean factory.
* @param registry the BeanFactory to load bean definitions into,
* in the form of a BeanDefinitionRegistry
*/
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry );
}
传过去的是XmlBeanFactory,怎么变成了BeanDefinitionRegistry了,莫非是接口
public interface BeanDefinitionRegistry extends AliasRegistry
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
要知道 XmlBeanFactory是DefaultListableBeanFactory的子类,父类实现了好几个接口,其中就有BeanDefinitionRegistry。所以我们在传递参数的时候,面向接口。
解决了传递参数的问题,还有个父类方法调用呢,我们先看看父类是谁
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert. notNull(registry, "BeanDefinitionRegistry must not be null" );
this.registry = registry;
// Determine ResourceLoader to use.
if (this .registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader) this .registry ;
}
else {
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
// Inherit Environment if possible
if (this .registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable) this.registry ).getEnvironment();
}
else {
this.environment = new StandardEnvironment();
}
}
先说一下ResourceLoader 和 environment。
ResourceLoader定义资源加载器,主要应用于根据给定的资源文件地址返回对象Resource.
EnvironmentCapable定义获取Environment方法。
而AbstractBeanDefinitionRader是对EnvironmentCapable和BeanDefinitionReader类定义的功能进行实现。
可见,我们在创建reader的时候,其实做了很多的事情,并不是直接拿过来用,而是经过一系列的参数传递的过程。
Java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。显然 我们的ResourceLoader和EnvironmentCapable都是接口。然后看看我们的类定义
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader
父类实现了接口,子类也必然是这个接口的对象。所以上面那个判断都是正确的,在接口里是看不出来内容的,要想知道具体做了什么,必须到XmlBeanDefinitionReader类中要到对应的方法。写到这,我感觉好像把两个分开的东西整合到一起了。
3:不说reader对象了,说说它的方法
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource ));
}
这次不说构造函数之间的调用,而是同一个方法名之间的调用,先看看这个EncodedResource是什么东西
/**
* Create a new EncodedResource for the given Resource,
* not specifying a specific encoding.
* @param resource the Resource to hold
*/
public EncodedResource (Resource resource ) {
Assert. notNull(resource, "Resource must not be null");
this.resource = resource;
}
由此看来就是对Resource的封装,把Resource做为参数传来进来。先不看怎么用,先继续往下追踪。
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert. notNull(encodedResource, "EncodedResource must not be null" );
if (logger .isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
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();
try {
InputSource inputSource = new InputSource(inputStream );
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource .getEncoding());
}
return doLoadBeanDefinitions(inputSource , encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex ) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource );
if (currentResources .isEmpty()) {
this.resourcesCurrentlyBeingLoaded .remove();
}
}
}
写的这么热闹,看懂的就是有一个从encodedResource对象中获取inputStream过程。
/**
* Return the Resource held.
*/
public final Resource getResource() {
return this .resource ;
}
把resource传进EncodeResource,然后在返回回来,目前还没看到哪有用。
InputSource inputSource = new InputSource(inputStream );
public InputSource (InputStream byteStream )
{
setByteStream( byteStream);
}
public void setByteStream (InputStream byteStream)
{
this.byteStream = byteStream;
}
好吧,我们有把输入流给传进inputSource对象里面去了。又有一个判断,把encodedResource的encoding传进inputSource里了。
书上给我的解释是,首先对传入的resource参数做封装,目的是考虑到Resource可能存在编码要求的情况,其次SAX读取XML文件的方法来准备InputSource对象。最后将准备的数据通过参数传入真正的核心处理部分doLoadBeanDefinitions(inputSource,encodedResource.getResource()).
4:代码比较复杂,一点一点分析
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource , resource );
return registerBeanDefinitions(doc , resource);
}
catch (BeanDefinitionStoreException ex ) {
throw ex ;
}
catch (SAXParseException ex ) {
throw new XmlBeanDefinitionStoreException(resource .getDescription(),
"Line " + ex .getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex ) {
throw new XmlBeanDefinitionStoreException(resource .getDescription(),
"XML document from " + resource + " is invalid" , ex );
}
catch (ParserConfigurationException ex ) {
throw new BeanDefinitionStoreException(resource .getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex ) {
throw new BeanDefinitionStoreException(resource .getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex ) {
throw new BeanDefinitionStoreException(resource .getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
这些内容下次再进行整理。