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

来源:互联网 发布:2016年鞍山中考大数据 编辑:程序博客网 时间:2024/05/29 06:51

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

@Overrideprotected 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()方法。我们的扩展点也就是在这个方法中扩展的。

@Overridepublic 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类的子类

1 0