白开水时光源码首页投稿(暂停使用,暂停投稿)

spring-beans深入源码之Bean Definitio

2016-09-05  本文已影响901人  holly_wang_王小飞

我们在使用一个bean得时候,在不用任何框架的情况下都是需要自己new的,spring框架既然为我们提供bean容器使得我们把bean得管理权交给它,那么我们看看spring的bean是怎么装载的,先说xml,注解的方式其实是一样的。
   接着上篇的test例子<b>CollectionMergingTests</b>其中最开始有个<b>setUp</b>方法,这是junit方法运行前的装配方法

public class CollectionMergingTests extends TestCase {

    private DefaultListableBeanFactory beanFactory;
        @Override
    protected void setUp() throws Exception {
        this.beanFactory = new DefaultListableBeanFactory();
        BeanDefinitionReader reader = new XmlBeanDefinitionReader(this.beanFactory);
        reader.loadBeanDefinitions(new ClassPathResource("collectionMerging.xml", getClass()));
    }
………………
}

这个方法就是在装配bean到容器中。
  <b>DefaultListableBeanFactory </b>是spring 真正可以独立使用IOC容器的<b>BeanFactory</b>。继承和实现的传递性可知<b>DefaultListableBeanFactory </b>默认实现了<b>BeanFactory</b>和<b>BeanDefinitionRegistry</b>,<b>DefaultListableBeanFactory </b>类继承图(引用自software-architecture-design

DefaultListableBeanFactory继承关系

直接debug看下其初始化过程便基本知道继承关系。

DefaultListableBeanFactory初始化第一步

可见先是到static初始化块

static {
        try {
            javaUtilOptionalClass =
                    ClassUtils.forName("java.util.Optional", DefaultListableBeanFactory.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            // Java 8 not available - Optional references simply not supported then.
        }
        try {
            javaxInjectProviderClass =
                    ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            // JSR-330 API not available - Provider interface simply not supported then.
        }
    }

加载class java.util.Optional(JAVA8新加入的)和interface javax.inject.Provider
<b>java.util.Optional</b>是JAVA8新加入的类,这个类专门用来解决空引用的问题,同时这个类和lambda表达式和函数式编程也可以比较好的整合在一起使用
java.util.Optional doc
<b>javax.inject.Provider</b>提供了一个 T的实例。 通常作为一个依赖注入容器器的父接口. 可以注入任何类型的 T, 当然也可以入 Provider<T>
相对于直接注入 T ,注入 Provider<T> 有如下作用(doc的英文大概翻译):

在debug该类初始化的过程中我在类的属性初始化上打断点无意间发现了eclipse的断点类型

breakpoint

第一处的是<b>Watchpoint</b>这个主要是关注变量的值得变化过程,第二处是<b>Line Breakpoint</b>最简单的断点,即执行到此处就断点,还有。eclipse 还有条件断点,方法断点 异常断点 远程调试等 这个完了再细化记录博客吧。
这里想说的是为什么 第一行是<b>Watchpoint</b>二处是<b>Line Breakpoint</b> 同样是属性是为什么? 其实是final关键字的作用,<b>Watchpoint</b>是观察值的变化情况 final的变量是不可变的 所以final修饰的参数断点都是<b>Line Breakpoint</b> 类似于方法断点,所以有时候要分清你可以在属性上设置的断点类型。

接下来到了属性的初始化
属性初始化

继续往下走的时候可以看到 类初始化的关系 先是<b>DefaultListableBeanFactory</b>static静态初始化块和static属性,然后是SimpleAliasRegistry的属性


SimpleAliasRegistry初始化

再是SimpleAliasRegistry的子类<b>DefaultSingletonBeanRegistry</b>


DefaultSingletonBeanRegistry初始化
接着是<b>DefaultSingletonBeanRegistry</b>的子类<b>FactoryBeanRegistrySupport</b> Paste_Image.png

接着<b>FactoryBeanRegistrySupport</b>子类<b>AbstractBeanFactory</b>


AbstractBeanFactory初始化

接着是<b>AbstractBeanFactory</b>子类<b>AbstractAutowireCapableBeanFactory</b>

AbstractAutowireCapableBeanFactory初始化

最后到<b>DefaultListableBeanFactory</b>自己

DefaultListableBeanFactory初始化
由此我们基本可以得出类初始化的顺序(为什么要说这个 是因为我看到网上好多说法和我运行的得到的结果不一致):

先找到根类,根类static静态块和static属性的初始化 接着根类的子类static静态块和static属性初始化 最后回到类本身的类static静态块和static属性初始化然后再到根类 其他属性的初始化,接着根类子类的其他属性初始化 最后回到自己的其他属性初始化。
<b>DefaultListableBeanFactory</b>初始化完毕以后接着是<b>BeanDefinitionReader</b>的初始化 选择初始化的类实例是<b>XmlBeanDefinitionReader</b>初始化完毕后调用

reader.loadBeanDefinitions(new ClassPathResource("collectionMerging.xml", getClass()));

方法去加载collectionMerging.xml配置文件。
ClassPathResource类是Resource接口实现类的子类,如果没有指定相对的类名,该类将从类的根路径开始寻找某个resource,如果指定了相对的类名,则根据指定类的相对路径来查找某个resource。上面的还可以写成:

reader.loadBeanDefinitions(new ClassPathResource(
"org/springframework/beans/factory/xml/collectionMerging.xml"));

<b>XmlBeanDefinitionReader</b>初始化的时候先找到根类<b>AbstractBeanDefinitionReader</b>
其构造方法

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();
        }
    }

在构造方法中有instanceof ResourceLoader 判断 显然传入的registry 是<b>DefaultListableBeanFactory</b>的实例 没有实现ResourceLoader接口 所以走else 逻辑创建<b>PathMatchingResourcePatternResolver</b>类,在<b>PathMatchingResourcePatternResolver</b>初始化的过程中

public PathMatchingResourcePatternResolver() {
        this.resourceLoader = new DefaultResourceLoader();
    }

其创建的是<b>DefaultResourceLoader</b>类 该类实现了<b>ResourceLoader</b>接口。所以最后还是创建的是<b>ResourceLoader</b>的实例

public DefaultResourceLoader() {
        this.classLoader = ClassUtils.getDefaultClassLoader();
    }

这个构造方法就是为了初始化private ClassLoader classLoader;属性用于load xml资源。
好了<b>Resource</b>参数构造完毕后进入

@Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

方法,这里将<b>Resource</b>包装成了<b>EncodedResource</b> 其实EncodedResource是对Resource的包装 增加了encoding和charset属性而已 这里都为null 相当于默认的null。接着进入方法

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();
            }
        }
    }

这里会有一个比较经典的同步缓存的方式

    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

this.resourcesCurrentlyBeingLoaded是

private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
            new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded");

可以看到采用了<b>ThreadLocal</b>的存贮方式。在JDK1.2的时候就提供了<b>java.lang.ThreadLocal</b><b>ThreadLocal</b>为解决多线程问题提供了非常非常大帮助,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。这样就完美的解决了多线程的问题,不过对于计数的情况不适用 建议使用ATMOIC的变量。
在没有取到的情况下 将新的<b>EncodedResource</b>加入ThreadLocal变量的缓存中。
接着从<b>EncodedResource</b>获取输入流<b>InputStream</b>构造 <b>InputSource</b>,<b>InputSource</b>比较简单的一个类,封装了一下<b>InputStream</b>最后调用 doLoadBeanDefinitions方法。部分源码:

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();
            }
        }

这里的<b>finally</b>方法最后将<b>ThreadLocal</b>变量<b>resourcesCurrentlyBeingLoaded</b> remove掉了。看了好半天才看懂其中的奇妙之处:<b>这个方法本身存在多线程问题,最简单的做法就是同步语句块,但是大家知道这会影响性能所以借助<b>ThreadLocal</b>变量做了一个过程同步 在使用完即remove掉</b>。
<b>doLoadBeanDefinitions</b>是java dom加载xml的过程,大家都知道java解析xml的几种方式,基本的解析方式有两种,一种叫SAX,另一种叫DOM,再细一点有

这里spring用java dom解析xml 我想原因就是需要加载的xml配置文件都不是大型的xml文件,都是比较小的 所以使用java dom反而效果会好一些。
这里定义了<b>DocumentLoader</b>接口,提供了默认的实现类<b>DefaultDocumentLoader</b>其中的方法:

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);
    }

解析过程可以看oracle jom的api文档
最后返回<b>Document</b>对象。

Documen return

进入很重要的方法<b>registerBeanDefinitions</b>该方法将读取的dom转换成bean definition先进入方法

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 documentReader = createBeanDefinitionDocumentReader(); 创建了<b>BeanDefinitionDocumentReader</b>对象 该对象是将document中包含的所偶bean定义解析出来。

protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
    }

第二行int countBefore = getRegistry().getBeanDefinitionCount(); 获取到解析前的bean的数量
第三行documentReader.registerBeanDefinitions(doc, createReaderContext(resource));这里先根据传入的<b>Resource</b>参数创建<b>XmlReaderContext</b>对象,官方给出的解释是

Extension of {@link org.springframework.beans.factory.parsing.ReaderContext},
 * specific to use with an {@link XmlBeanDefinitionReader}. Provides access to the
 * {@link NamespaceHandlerResolver} configured in the {@link XmlBeanDefinitionReader}.

大概意思就是配合XmlBeanDefinitionReader使用的类。继续往下走到:

@Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

到方法<b>doRegisterBeanDefinitions(root)</b> 进入:

protected void doRegisterBeanDefinitions(Element root) {
        // Any nested <beans> elements will cause recursion in this method. In
        // order to propagate and preserve <beans> default-* attributes correctly,
        // keep track of the current (parent) delegate, which may be null. Create
        // the new (child) delegate with a reference to the parent for fallback purposes,
        // then ultimately reset this.delegate back to its original (parent) reference.
        // this behavior emulates a stack of delegates without actually necessitating one.
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }

        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

先创建<b>BeanDefinitionParserDelegate</b>

BeanDefinitionParserDelegate create

开始解析的三部曲

解析三部曲

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
```
分了好几种情况去解析 看开头是import、alias、bean、beans这四种开头的 标签去解析 ,我们就找个bean开头的跟踪下情况 其它的标签也是一样的道理。


bean开头标签的解析

进入方法

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.
                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));
        }
    }

其中<b>parseBeanDefinitionElement</b>解析后获得<b>BeanDefinitionHolder</b>其中过程中比较中要的一个类<b>GenericBeanDefinition</b>每个bean标签都是解析成这样的beandefinition了。获得这个bean后对应解析各种

GenericBeanDefinition 各个部分的解析

对应的bean部件解析parseMetaElements、parseLookupOverrideSubElements、parseReplacedMethodSubElements、parseConstructorArgElements、parsePropertyElements、parseQualifierElements 见名知意的方法没有多大难度就是对应的解析xml即可,解析完成 bean definition也就构造完成了。

BeanDefinition装配过程基本完成 最后是装配到了最先初始化的<b>DefaultListableBeanFactory</b>的各个map属性中了。 好多东西很粗率的过了 后续补进。
不早了 go home!

上一篇下一篇

猜你喜欢

热点阅读