Spring -- 加载BeanDefinition

把spring的源码拉下来,就可以开始学习之路了,为了走通一个过程。就拿org.springframework.context.support.ClassPathXmlApplicationContextTests#testGenericApplicationContextWithXmlBeanDefinitions 这个方法入手进行debug

@Test
    public void testGenericApplicationContextWithXmlBeanDefinitions() {
        // 创建context
        GenericApplicationContext ctx = new GenericApplicationContext();
        // 创建reader用于解析资源
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ctx);
        // 加载资源并解析为BeanDefinition
        reader.loadBeanDefinitions(new ClassPathResource(CONTEXT_B, getClass()));
        reader.loadBeanDefinitions(new ClassPathResource(CONTEXT_C, getClass()));
        reader.loadBeanDefinitions(new ClassPathResource(CONTEXT_A, getClass()));
        ctx.refresh();
        assertTrue(ctx.containsBean("service"));
        assertTrue(ctx.containsBean("logicOne"));
        assertTrue(ctx.containsBean("logicTwo"));
        ctx.close();
    }

方法首先创建了XMLBeanDefinitionReader用于解析资源
创建reader后调用loadBeanDefinitions()方法解析资源为BeanDefinition并且注册到factory中。

/**
     * Create a new AbstractBeanDefinitionReader for the given bean factory.
     * <p>If the passed-in bean factory does not only implement the BeanDefinitionRegistry
     * interface but also the ResourceLoader interface, it will be used as default
     * ResourceLoader as well. This will usually be the case for
     * {@link org.springframework.context.ApplicationContext} implementations.
     * <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
     * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
     * <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its
     * environment will be used by this reader.  Otherwise, the reader will initialize and
     * use a {@link StandardEnvironment}. All ApplicationContext implementations are
     * EnvironmentCapable, while normal BeanFactory implementations are not.
     * @param registry the BeanFactory to load bean definitions into,
     * in the form of a BeanDefinitionRegistry
     * @see #setResourceLoader
     * @see #setEnvironment
     */
    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();
        }
    }

我们看看创建XMLBeanDefinitionReader执行了什么,它的父类AbstractBeanDefinitionReader构造方法中,接收了一个Registry参数,并设置了这个类的registy,也就是当解析成为BeanDefinition后要用这个registry将其注册到BeanFactory中。
然后设置了resourceLoader,用于加载资源文件

/** System environment property source name: {@value}. */
    public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

    /** JVM system properties property source name: {@value}. */
    public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";


    /**
     * Customize the set of property sources with those appropriate for any standard
     * Java environment:
     * <ul>
     * <li>{@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME}
     * <li>{@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}
     * </ul>
     * <p>Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will
     * take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}.
     * @see AbstractEnvironment#customizePropertySources(MutablePropertySources)
     * @see #getSystemProperties()
     * @see #getSystemEnvironment()
     */
    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    }

最后设置了environment,这个就是spring的环境配置了,它干了两件事,一个是加载系统环境配置,一个是加载JVM系统配置。然后说是JVM系统配置的优先级比系统环境配置要高。举个例子把,就是说你在启动容器时配置JVM参数的优先级高于配置文件的优先级,也就是JVM参数会覆盖掉配置文件里的。配置文件当然是指.properties配置文件。spring boot 现在还有.yml的配置文件。当然配置文件的形式在这里不重要。


GenericApplicationContext

而我们传入的是GenericApplicationContext对象。这个对象既实现了ResourceLoader,又实现了EnvironmentCapable。所以registry、resourceLoader、environment都是GenericApplicationContext对象。当然如果没有实现这些接口的话,会创建一个默认的PathMatchingResourcePatternResolver和StandardEnvironment对象

<?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:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <import resource="import1.xml"/>

    <import resource="classpath:org/springframework/context/support/test/*/import2.xml"/>

    <context:property-override location="org/springframework/context/support/override.properties"/>

    <bean id="messageSource" class="org.springframework.context.support.StaticMessageSource"/>

    <bean class="org.springframework.context.support.FactoryBeanAndApplicationListener"/>

    <bean name="service" class="org.springframework.context.support.Service">
        <property name="resources" value="/org/springframework/context/support/test/context*.xml"/>
    </bean>

    <bean name="service2" class="org.springframework.context.support.Service" autowire="byName" depends-on="service">
        <property name="resources" value="/org/springframework/context/support/test/context*.xml"/>
    </bean>

    <bean name="autowiredService" class="org.springframework.context.support.AutowiredService" autowire="byName"/>

    <bean name="autowiredService2" class="org.springframework.context.support.AutowiredService" autowire="byType"/>

    <bean name="wrappedAssemblerOne" class="org.springframework.context.support.TestProxyFactoryBean">
        <property name="target" ref="assemblerOne"/>
    </bean>

    <bean name="wrappedAssemblerTwo" class="org.springframework.context.support.TestProxyFactoryBean">
        <property name="target" ref="assemblerTwo"/>
    </bean>
</beans>
/**
     * 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.isTraceEnabled()) {
            logger.trace("Loading XML bean definitions from " + encodedResource);
        }
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(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();
            }
        }
    }

然后调用 loadBeanDefinitions() 方法加载为BeanDefinition对象。具体的工作是在XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource)中进行的。
在XmlBeanDefinitionReader的首先做的是将当前资源保存到资源集合中,解析完成后再移除。然后做解析的准备工作。在最后调用doLoadBeanDefinitions()方法进行解析。


doLoadBeanDefinitions

doLoadBeanDefinitions()主要干了两件事,一个是将资源解析为Document文档对象,然后注册BeanDefinition。


image.png
/**
     * Register the bean definitions contained in the given DOM document.
     * Called by {@code loadBeanDefinitions}.
     * <p>Creates a new instance of the parser class and invokes
     * {@code registerBeanDefinitions} on it.
     * @param doc the DOM document
     * @param resource the resource descriptor (for context information)
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of parsing errors
     * @see #loadBeanDefinitions
     * @see #setDocumentReaderClass
     * @see BeanDefinitionDocumentReader#registerBeanDefinitions
     */
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 创建reader
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // 保存已经注册的BeanDefinition数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        // 解析Document对象为BeanDefinition
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

在registerBeanDefinitions()方法中首先创建了一个BeanDefinitionDocumentReader对象,实际上就是DefaultBeanDefinitionDocumentReader对象。然后在第524行调用DefaultBeanDefinitionDocumentReader#registerBeanDefinitions注册


解析调用过程

上图的调用过程在此就不详细解析了。直接上图,以后填坑。


DefaultListableBeanFactory

注册BeanDefinition调用的是DefaultListableBeanFactory#registerBeanDefinition方法。DefaultListableBeanFactory可以说是默认的BeanFactory实现类了。
下图是DefaultListableBeanFactory实现的接口和继承的类,大概能知道这个BeanFactory有什么功能了。
@Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                // 打日志
            }
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isDebugEnabled()) {
                    // 打日志
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    // 打日志
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

DefaultListableBeanFactory#registerBeanDefinition方法的大概调用过程就是这样了。被注释为打日志的那一部分的代码被我删掉了。因为占位置。
这段代码挺简单易懂的。大概就是先检查是否存在这个BeanDefinition,如果存在又允许重写的话,就重新覆盖之前的BeanDefinition。不然的话就抛异常了。
不存在的话就先检查是否有bean正在实例化。有的话就先对beanDefinitionMap加锁,防止bean实例化过程出错,然后放进beanDefinitionMap,更新beanDefinitionNames和manualSingletonNames,至于为什么要先重新拷贝一份呢,我想应该是为了防止并发导致的一些问题吧。没有的话直接操作。
最后,如果已经存在了BeanDefinition或者存在这个单例,就要重置一下里面的缓存。因为这个BeanDefinition有可能是beanDefinitionMap中其他BeanDefinition的BeanDefinition,所以都要刷新一次。


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容