zoukankan      html  css  js  c++  java
  • 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);
            }
        }
    这些内容下次再进行整理。
  • 相关阅读:
    Zookeeper java API
    Zookeeper 安装与配置
    Zookeeper 架构
    HBase 优化
    HBase 与 MapReduce 整合
    Hbase protobuf Java API
    HBase数据库设计
    Fork/Join并发处理框架
    并发容器Map
    Redis缓存穿透、缓存击穿和雪崩
  • 原文地址:https://www.cnblogs.com/zhao307/p/5432578.html
Copyright © 2011-2022 走看看