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);
        }
    }
这些内容下次再进行整理。
posted @ 2016-04-25 20:20  假寐的我  阅读(1722)  评论(0编辑  收藏  举报