spring读取xml生成BeanDefinion时的扩展点

spring读取xml生成beanDefinion时的扩展点(虽然基本上用不到,但既然看到了还是记录一下(┬_┬))

通常在我们的web项目中spring的上下文实现类是XmlWebApplicationContext类。
比如org.springframework.web.context.ContextLoader#determineContextClass

protected Class<?> determineContextClass(ServletContext servletContext) {
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
    if (contextClassName != null) {
        try {
            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                    "Failed to load custom context class [" + contextClassName + "]", ex);
        }
    }
    else {
        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
        try {
            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                    "Failed to load default context class [" + contextClassName + "]", ex);
        }
    }
}

在web中配置的ContextLoaderListener启动之后会调用上面的方法确定使用什么Context实现类,如果没有额外配置的话会使用默认的XmlWebApplicationContext。

org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

@Override
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        //此处会加载BeanDefinitions
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

在AbstractRefreshableApplicationContext的refreshBeanFactory方法中会加载所有的BeanDefinitions。

然后我们看到XmlWebApplicationContext实现了AbstractRefreshableApplicationContext的loadBeanDefinitions方法。
org.springframework.web.context.support.XmlWebApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    beanDefinitionReader.setEnvironment(getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

在上面的方法中XmlWebApplicationContext创建了一个XmlBeanDefinitionReader类用于读取xml中的数据。

spring加载beanDefinion都是通过AbstractBeanDefinitionReader抽象类来加载的,AbstractBeanDefinitionReader继承了BeanDefinitionReader接口,而具体是什么介质并加载是由子类来实现BeanDefinitionReader接口的loadBeanDefinitions方法来做的。

AbstractBeanDefinitionReader的子类实现有三个分别是PropertiesBeanDefinitionReader、GroovyBeanDefinitionReader、XmlBeanDefinitionReader。

我们看到
由于XmlBeanDefinitionReader继承于AbstractBeanDefinitionReader,所以实现了BeanDefinitionReader接口的org.springframework.beans.factory.support.BeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)方法。并且在该方法中最终会走到registerBeanDefinitions方法。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    documentReader.setEnvironment(this.getEnvironment());
    int countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

如上方法会调用createBeanDefinitionDocumentReader()方法获取BeanDefinitionDocumentReader对象,并调用该对象的registerBeanDefinitions()方法。我们的扩展点也就是在这个方法中扩展的。

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    Element root = doc.getDocumentElement();
    doRegisterBeanDefinitions(root);//此处调用下面的方法
}


protected void doRegisterBeanDefinitions(Element root) {
    String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    if (StringUtils.hasText(profileSpec)) {
        Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        if (!this.environment.acceptsProfiles(specifiedProfiles)) {
            return;
        }
    }
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(this.readerContext, root, parent);

    preProcessXml(root);//在解析xml并注册BeanDefinition之前执行
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);//在解析xml并注册BeanDefinition之后执行

    this.delegate = parent;
}

spring有默认的BeanDefinitionDocumentReader接口实现类:DefaultBeanDefinitionDocumentReader。

我们看到DefaultBeanDefinitionDocumentReader类有两个没有任何实现的方法。


/**
 * Allow the XML to be extensible by processing any custom element types first,
 * before we start to process the bean definitions. This method is a natural
 * extension point for any other custom pre-processing of the XML.
 * <p>The default implementation is empty. Subclasses can override this method to
 * convert custom elements into standard Spring bean definitions, for example.
 * Implementors have access to the parser's bean definition reader and the
 * underlying XML resource, through the corresponding accessors.
 * @see #getReaderContext()
 */
protected void preProcessXml(Element root) {
}

/**
 * Allow the XML to be extensible by processing any custom element types last,
 * after we finished processing the bean definitions. This method is a natural
 * extension point for any other custom post-processing of the XML.
 * <p>The default implementation is empty. Subclasses can override this method to
 * convert custom elements into standard Spring bean definitions, for example.
 * Implementors have access to the parser's bean definition reader and the
 * underlying XML resource, through the corresponding accessors.
 * @see #getReaderContext()
 */
protected void postProcessXml(Element root) {
}

preProcessXml()方法在解析xml注册beanDefinion()之前执行,postProcessXml()在之后执行。所以,我们要想在这两个步骤中做自己的处理就必须继承DefaultBeanDefinitionDocumentReader并实现preProcessXml()方法和postProcessXml()方法。

我们看到在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions方法中会调用它的createBeanDefinitionDocumentReader()方法创建一个BeanDefinitionDocumentReader对象并返回。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();//这儿
    documentReader.setEnvironment(this.getEnvironment());
    int countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}

createBeanDefinitionDocumentReader()方法中是通过自身的documentReaderClass属性来确定了创建对象类型。所以,我们可以通过XmlBeanDefinitionReader类的setDocumentReaderClass(Class documentReaderClass)方法来改变这个属性为我们自己定义的继承了DefaultBeanDefinitionDocumentReader并实现了preProcessXml()方法和postProcessXml()方法的类的类型。

public void setDocumentReaderClass(Class<?> documentReaderClass) {
    if (documentReaderClass == null || !BeanDefinitionDocumentReader.class.isAssignableFrom(documentReaderClass)) {
        throw new IllegalArgumentException(
                "documentReaderClass must be an implementation of the BeanDefinitionDocumentReader interface");
    }
    this.documentReaderClass = documentReaderClass;
}

我们知道在初始化容器并调用org.springframework.web.context.support.XmlWebApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)这个方法的时候会创建一个XmlBeanDefinitionReader对象。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // Configure the bean definition reader with this context's
    // resource loading environment.
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

在上面的方法中提供了org.springframework.web.context.support.XmlWebApplicationContext#initBeanDefinitionReader方法来初始化XmlBeanDefinitionReader对象,所以我们可以继承XmlWebApplicationContext类来实现initBeanDefinitionReader()方法,并在该方法中调用传进去的XmlBeanDefinitionReader对象的setDocumentReaderClass()方法来设置我们自定义的继承BeanDefinitionDocumentReader并实现preProcessXml()方法和postProcessXml()方法的类类型。

最后一步就是在web.xml中显示配置contextClass变量为继承了XmlWebApplicationContext的自定义容器类类型。
比如:

<context-param>
    <param-name>contextClass</param-name>
    <param-value>com.explore.develop.MyXmlWebApplicationContext</param-value>
</context-param>

当然springMVC的容器是另外一个容器,只是在初始化的时候会把ContextLoaderListener加载的容器指定为父容器。如果想要springMVC容器在加载beanDefinion的时候也执行扩展点,也需要指定它的contextClass变量。如下:

<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/spring/spring-mvc.xml</param-value>
    </init-param>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>com.explore.develop.MyXmlWebApplicationContext</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
总结

实现扩展spring读取xml生成beanDefinion时的扩展点的步骤为:
1. 继承DefaultBeanDefinitionDocumentReader并实现preProcessXml()方法和postProcessXml()。
2. 继承XmlWebApplicationContext类并重写initBeanDefinitionReader()方法。
3. 在重写了XmlWebApplicationContext的initBeanDefinitionReader()方法中,调用XmlBeanDefinitionReader对象的setDocumentReaderClas()方法设置我们在第1步继承DefaultBeanDefinitionDocumentReader的子类类型。
4. web.xml中配置contextClass变量类型为第2步自定义的XmlWebApplicationContext类的子类

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值