Spring源码系列 — BeanDefinition扩展点

Posted 怀瑾握瑜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码系列 — BeanDefinition扩展点相关的知识,希望对你有一定的参考价值。

前言

前文介绍了Spring Bean的生命周期,也算是XML IOC系列的完结。但是Spring的博大精深,还有很多盲点需要摸索。整合前面的系列文章,从Resource到BeanDefinition,再到容器扩展点,最后到Bean创键,这个过程中无处不存在Spring预留的扩展口。

本篇文章介绍Spring的另一种扩展点:BeanDefinition扩展点,该扩展点是为处理BeanDefinition而设计。本文主要从以下几点分析:

  • BeanDefinition扩展点的几种方式
  • BeanDefinition扩展点实战
  • BeanDefinition扩展点的原理

BeanDefinition扩展点的几种方式

Spring中针对向上下文中添加BeanDefinition、修改上下文中的BeanDefinition可谓是提供了丰富的扩展点。既有针对XML配置的,又有针对注解配置的Bean,甚至还有自定义XML标签的。这里总结了,共有以下几种方式:

  1. BeanDefinitionRegistryPostProcessor方式
  2. BeanFactoryPostProcessor方式
  3. ImportBeanDefinitionRegistrar方式
  4. BeanDefinitionParser方式
BeanDefinitionRegistryPostProcessor方式

从命名上也可以看出一些端倪,BeanDefinitionRegistryPostProcessor是BeanDefinition注册后置处理器,它本身是BeanFactoryPostProcessor的扩展,允许在BeanFactoryPostProcessor处理前向上下文中注册更多的BeanDefinition。

BeanFactoryPostProcessor方式

BeanFactoryPostProcessor是容器的扩展点,用于更进一步处理上下文中的BeanDefinition,如果对其还不甚了解,请移步至我的另一篇文章Spring源码系列 — 容器Extend Point(一)

ImportBeanDefinitionRegistrar方式

ImportBeanDefinitionRegistrar也是BeanDefinition注册器,用于向上下文注册更多的BeanDefinition。不过它是被应用在注解处理BeanDefinition的场景中,即自定义注解,然后利用ImportBeanDefinitionRegistrar其实现向上下文中注册自定义注解标注的Bean定义。

BeanDefinitionParser方式

BeanDefinitionParser是BeanDefinition解析器,它是Spring提供为扩展解析XML配置的Bean而设计。它不仅能够解析XML向上下文中注册更多BeanDefiniion,同时还支持自定义XML Tag。

BeanDefinition扩展点实战

上节整理了Spring中提供处理BeanDefinition的几种扩展方式,为了更好的理解和应用这些扩展点,本节将从实战的角度再度理解这些扩展方式。

Notes:
关于BeanFactoryPostProcessor的扩展实战本节不再做说明,在前文的容器扩张点中已经详细介绍其原理,并利用PropertySourcesPlaceholderConfigurer案例进行了分析。这里不再赘述。

基于BeanDefinitionRegistryPostProcessor扩展

首先定义BeanDefinitionRegistryPostProcessor实现类MyBdRegistryPostProcessor,实现其postProcessBeanDefinitionRegistry接口:

/**
 * 用于演示BeanDefinitionRegistryPostProcessor扩展点
 *
 * @author huaijin
 */
public class MyBdRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        try {
            // 创建自定义的BeanDefinition
            String bdClassName = MyBeanUsedBdRegistryPostProcessor.class.getName();
            AbstractBeanDefinition bd = BeanDefinitionReaderUtils
                    .createBeanDefinition(null, bdClassName, ClassUtils.getDefaultClassLoader());
            // 设置BeanDefinition属性:单例、非惰性
            bd.setScope(AbstractAutowireCapableBeanFactory.SCOPE_SINGLETON);
            bd.setLazyInit(false);
            // 设置Bean的属性值
            MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
            PropertyValue propertyValue = new PropertyValue("name", "myBeanUsedBdRegistryPostProcessor");
            mutablePropertyValues.addPropertyValue(propertyValue);
            // 将Bean的属性值添加到BeanDefinition中
            bd.setPropertyValues(mutablePropertyValues);
            // 注册该自定义的BeanDefinition,BeanName使用myBeanUsedBdRegistryPostProcessor
            registry.registerBeanDefinition("myBeanUsedBdRegistryPostProcessor", bd);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

然后编写启动类,BeanDefinitionRegistryPostProcessorDemo,载入XML配置,从上下文中获取myBeanUsedBdRegistryPostProcessor名称的Bean,并执行其printMyName方法:

public class BeanDefinitionRegistryPostProcessorDemo {

    public static void main(String[] args) {
        // 载入配置
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext(
                        "applicationContext-extendpoint/beans.xml");
        // get bean
        MyBeanUsedBdRegistryPostProcessor myBean = context.getBean(
                "myBeanUsedBdRegistryPostProcessor", MyBeanUsedBdRegistryPostProcessor.class);
        // 执行方法
        myBean.printMyName();
    }
}

beans.xml的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 配置自定义的BeanDefinitionRegistryPostProcessor为Bean -->
    <bean class="com.learn.ioc.extendpoint.process.MyBdRegistryPostProcessor"></bean>
</beans>

方法调用执行结果如下:

my name is:myBeanUsedBdRegistryPostProcessor

BeanDefinitionRegistryPostProcessor中自定义的Bean成功的被上下文注册为单例。当然这里只是简单的示例,对于更复杂的需要进行依赖处理。

基于ImportBeanDefinitionRegistrar扩展

上节中介绍了ImportBeanDefinitionRegistrar是基于注解的方式BeanDefinition注册器,允许应用向上下文中注册更多的BeanDefinition。这里以笔者项目中的案例作为分析,帮助理解ImportBeanDefinitionRegistrar。

笔者在spring-boot工程的项目中使用了Elastic-Job v1.1.1版本,由于该版本Elastic-Job不支持不支持注解式配置Job Bean,笔者嫌在spring-boot中再引入XML不够方便和友好,故简单自己实现了Elastic-Job对注解支持的模块。其中就使用到了Spring提供的ImportBeanDefinitionRegistrar扩展点。

原有的Elastic——Job的XML配置主要分为两大类,第一类是任务注册中心的配置,第二类是Job相关的配置。其中Job分为多种,每种Job的配置方式不一样,这里只实现了对SimpleJob的支持。

首先分析SimpleJob的配置,同Spring Bean的配置差异不大。也是代表Job的标签,然后就是属性的配置,再者就是子元素的Bean的配置。如:

<job:simple id="..." class="..."
            registry-center-ref="..."
            overwrite="..."
            cron="..."
            sharding-total-count="..."
            sharding-item-parameters="..."
            monitor-execution="..."
            monitor-port="..."
            failover="..."
            description="...."
            disabled="...">
    <job:listener class="..." started-timeout-milliseconds="..." completed-timeout-milliseconds="..."></job:listener>
</job:simple>

一个job:simple用于定义一个Job的配置,这样可以抽象一个注解来描述该Job配置,其中子元素job:listener又是属于这个Job的监听器子元素配置,同样也需要抽象出一个注解用于定义该监听器,如:

/**
 * Elastic-job的Simple类型Job对应的注解
 *
 * @author huaijin
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
@Import(ElasticJobRegistrar.class)
public @interface ElasticSimpleJob {

    String id();

    Class<?> classStr();

    boolean overwrite() default true;

    String registryCenterRef();

    String jobParameter() default "";

    String cron();

    String shardingTotalCount();

    String shardingItemParameters() default "";

    boolean jobFailover() default true;

    int monitorPort() default 9880;

    boolean monitorExecution() default false;

    String description() default "";

    String maxTimeDiffSeconds() default "";

    String misfire() default "";

    String jobShardingStrategyClass() default "";

    JobListener jobListener() default @JobListener(startedTimeoutMilliseconds = 0,
            completedTimeoutMilliseconds = 0);

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @interface JobListener {

        Class<?> classStr() default Class.class;

        long startedTimeoutMilliseconds();

        long completedTimeoutMilliseconds();
    }
}

其中该注解被@Component修饰,表示该注解标注的类是一个Spring Bean,能够被Spring的@Component注解处理检测加载该类的注解属性。使用@Import(ElasticJobRegistrar.class)该配置,表示该注解应用被哪个ImportBeanDefinitionRegistrar实现进行处理。

然后就是实现ImportBeanDefinitionRegistrar,用于处理ElasticSimpleJob注解,将其标注的类注册为Spring中特定类型的BeanDefinition。

/**
 * 解析{@link ElasticSimpleJob},注册SpringJobScheduler和SimpleJobConfiguration
 *
 * @author huaijin
 */
@Component
public class ElasticJobRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        // 获取ElasticSimpleJob注解的属性集合
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata
                .getAnnotationAttributes(ElasticSimpleJob.class.getName()));
        // 获取job id属性
        String id = annoAttrs.getString(BeanDefinitionParserDelegate.ID_ATTRIBUTE);
        // 使用Spring提供的建造者模式构造BeanDefinition,其中类型为SpringJobScheduler
        BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(SpringJobScheduler.class);
        // 设置初始化方法
        factory.setInitMethodName("init");
        // 增加该Bean的第一个构造参数引用,即对注册中心Bean的引用
        factory.addConstructorArgReference(annoAttrs.getString("registryCenterRef"));
        // 增加该Bean的第二个构造参数引用,对Job配置的引用
        factory.addConstructorArgReference(createJobConfiguration(annoAttrs, registry));
        // 增加第三个构造参数引用,是对job listener的引用
        factory.addConstructorArgValue(createJobListeners(annoAttrs.getAnnotation("jobListener")));
        // 注册该BeanDefinition
        BeanDefinitionHolder holder = new BeanDefinitionHolder(factory.getBeanDefinition(), id + "SpringJobScheduler");
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }


    private String createJobConfiguration(final AnnotationAttributes annoAttrs, final BeanDefinitionRegistry registry) {
        Class<?> simpleJobConfigurationDto;
        try {
           simpleJobConfigurationDto = Class.forName("com.dangdang.ddframe.job.spring.namespace.parser.simple." +
                   "SimpleJobConfigurationDto");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        factory.addConstructorArgValue(annoAttrs.getString(BeanDefinitionParserDelegate.ID_ATTRIBUTE));
        factory.addConstructorArgValue(annoAttrs.getClass("classStr"));
        factory.addConstructorArgValue(annoAttrs.getString("shardingTotalCount"));
        factory.addConstructorArgValue(annoAttrs.getString("cron"));

        addPropertyValueIfExists(annoAttrs, "shardingItemParameters", factory);
        addPropertyValueIfExists(annoAttrs, "jobParameter", factory);
        addPropertyValueIfExists(annoAttrs, "jobMonitorExecution", factory);
        addPropertyValueIfExists(annoAttrs, "monitorPort", factory);
        addPropertyValueIfExists(annoAttrs, "maxTimeDiffSeconds", factory);
        addPropertyValueIfExists(annoAttrs, "failover", factory);
        addPropertyValueIfExists(annoAttrs, "misfire", factory);
        addPropertyValueIfExists(annoAttrs, "jobShardingStrategyClass", factory);
        addPropertyValueIfExists(annoAttrs, "description", factory);
        String propertyName = "elastic.job.disabled";
        addPropertyValueIfExists(environment, propertyName, factory);
        addPropertyValueIfExists(annoAttrs, "overwrite", factory);
        String result = annoAttrs.getString(BeanDefinitionParserDelegate.ID_ATTRIBUTE) + "Conf";
        registry.registerBeanDefinition(result, factory.getBeanDefinition());
        return result;
    }

    public List<BeanDefinition> createJobListeners(AnnotationAttributes jobListener) {
        List<BeanDefinition> listeners = new ManagedList<>();
        Class<?> listenerClass = jobListener.getClass("classStr");
        if (listenerClass == Class.class) {
            return new ManagedList<>(0);
        }
        BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(listenerClass);
        factory.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        if (AbstractDistributeOnceElasticJobListener.class.isAssignableFrom(listenerClass)) {
            factory.addConstructorArgValue(jobListener.getNumber("startedTimeoutMilliseconds"));
            factory.addConstructorArgValue(jobListener.getNumber("completedTimeoutMilliseconds"));
        }
        listeners.add(factory.getBeanDefinition());
        return listeners;
    }

    protected final void addPropertyValueIfExists(final AnnotationAttributes annoAttrs, final String propertyName,
                                                  final BeanDefinitionBuilder factory) {
        if (annoAttrs.containsKey(propertyName)) {
            Object attributeValue = annoAttrs.get(propertyName);
            if (Objects.nonNull(attributeValue)) {
                factory.addPropertyValue(propertyName, attributeValue.toString());
            }
        }
    }

    protected final void addPropertyValueIfExists(final Environment env, final String propertyName,
                                                  final BeanDefinitionBuilder factory) {
        String propertyValue = env.getProperty(propertyName);
        if (propertyValue != null && !propertyValue.isEmpty()) {
            factory.addPropertyValue("disabled", propertyValue);
        }
    }
}

以上实现利用ImportBeanDefinitionRegistrar扩展点,获取ElasticSimpleJob注解的属性,然后将其解析填充到相应类型的BeanDefinition中,最后再将BeanDefinition注册到上下文中。这样就完成了使用ElasticSimpleJob注解配置Job,并能够让Spring正常的加载实例化Job。

基于BeanDefinitionParser扩展

BeanDefinitionParser是Spring提供的对XML解析生成BeanDefinition的扩展点,应用可以扩展该接口,提供自定义XML Tag的解析能力,并生成BeanDefinition注册至上下文中。

本节将通过定义自定义xsd,编写自定义的XML配置,编写BeanDefinitionParser扩展实现来展示基于BeanDefinitionParser扩展。主要分为以下几个步骤:

  • 定义应用自身的xsd(XML Schema)
  • 编写Spring XML配置
  • 编写BeanDefinitionParser实现
  • 编写自定义的NameSpaceHandler,其中需要注册以上实现的BeanDefinitionParser
  • 配置集成BeanDefinitionParser和xsd至Spring中

首先自定义的XML Schema,这里使用xsd方式(关于dtd,读者可以自行研究)。如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.huaijin.com/schema/MyBean"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.huaijin.com/schema/MyBean"
            elementFormDefault="qualified">

    <xsd:element name="MyBean">
        <xsd:complexType>
            <xsd:attribute name="id" type="xsd:string" use="required"></xsd:attribute>
            <xsd:attribute name="name" type="xsd:string"></xsd:attribute>
            <xsd:attribute name="class" type="xsd:string" use="required"></xsd:attribute>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

该xsd自定义了XML Tag MyBean的描述。MyBean有三个基本属性id,name,class。

然后再使用自定义的XML Tag定义Bean:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:Mybean="http://www.huaijin.com/schema/MyBean"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.huaijin.com/schema/MyBean http://www.huaijin.com/schema/MyBean/MyBean.xsd">
	 <!-- 利用自定义的XML Tag定义Bean -->
    <Mybean:MyBean id="myHelloService" class="com.learn.ioc.bean.parser.extend.MyHelloService"></Mybean:MyBean>
</beans>

再编写BeanDefinitionParser实现:

/**
 * 自定义Bean定义解析器
 *
 * @author huaijin
 */
public class MyBeanBeanDefinitionParser implements BeanDefinitionParser {

    private static final String TAG_ID = "id";
    private static final String TAG_CLASS = "class";

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 获取id属性
        String id = element.getAttribute(TAG_ID);
        // 获取class属性
        String classType = element.getAttribute(TAG_CLASS);
        // 校验id和class属性
        if (id == null || id.isEmpty()) {
            throw new BeanDefinitionParsingException(new Problem("id must be not null.",
                    new Location(parserContext.getReaderContext().getResource())));
        }
        if (classType == null || classType.isEmpty()) {
            throw new BeanDefinitionParsingException(new Problem("classType must be not null.",
                    new Location(parserContext.getReaderContext().getResource())));
        }
        // 使用class创建BeanDefintion
        BeanDefinition beanDefinition;
        try {
            beanDefinition = BeanDefinitionReaderUtils.createBeanDefinition(null, classType,
                    parserContext.getReaderContext().getBeanClassLoader());
        } catch (ClassNotFoundException e) {
            throw new BeanDefinitionParsingException(new Problem("classType can\'t exist.",
                    new Location(parserContext.getReaderContext().getResource())));
        }
        // 使用id作为BeanName注册该BeanDefinition至上下文中
        BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, id);
        BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, parserContext.getRegistry());
        return beanDefinition;
    }
}

然后便是编写NameSpaceHandler,注册以上的BeanDefinitionParser:

/**
 * 自定义扩张的命名空间解析器
 *
 * @author huaijin
 */
public class MyBeanNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
    	// 注册BeanDefinitionParser
        registerBeanDefinitionParser("MyBean", new MyBeanBeanDefinitionParser());
    }
}

最后再配置xsd和自定义的BeanDefinitionParser至Spring中。这个过程需要在resource目录下配置两个文件/META-INF/spring.handlers和/META-INF/spring.schemas。
其中spring.handlers中定义命名空间和xsd文件位置的映射,使得Spring能够根据命名空间找xsd文件方便对XML配置进行格式校验;
spring.schemas中定义命名空间和NameSpaceHandler的映射,使得Spring在处理XML命名空间时能够获取具体的NameSpaceHandler,通过其获得注册的BeanDefinitionParser针对性处理该命名空间的XML配置。

spring.handlers中配置如下:

http\\://www.huaijin.com/schema/MyBean=com.learn.ioc.bean.parser.extend.MyBeanNamespaceHandler

spring.schemas中配置如下:

http\\://www.huaijin.com/schema/MyBean/MyBean.xsd=com/learn/ioc/bean/parser/extend/MyBean.xsd

最后再编写测试主类,从上下文中后去该自定义的配置的Bean,并调用方法执行验证

/**
 * 自定义扩展解析器Demo
 *
 * @author huaijin
 */
public class ExtendBeanDefinitionParserDemo {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/bean.parser-extend/extend-parser.xml");
        MyHelloService myHelloService = context.getBean("myHelloService", MyHelloService.class);
        myHelloService.sayMyHello();
    }
}

执行结果如下:

hello, you!

到这里,关于BeanDefinition的扩展点实战基本都详细介绍结束,其中关于各种方式都详细编码,如果需要了解更多详情,可以参考Spring官网对各种方式的描述。下节将从源码实现的角度分析这几种方式的扩展原理。

BeanDefinition扩展点的原理

本节针对以上的四种方式的扩展点原理展开介绍,关于BeanFactoryPostProcessor的原理在前文中已经介绍,这里不再赘述。关于BeanDefinitionRegistryPostProcessor的原理在BeanFactoryPostProcessor一文的源码分析中也有涉猎,即在Spring上下文创建完内部的BeanFactory,载入BeanDefinition后,在实例化和唤醒BeanFactoryPostProcessor的逻辑前,预留了BeanDefinitionRegistryPostProcessor的扩展,允许应用向BeanFactory中注册更多BeanDefinition,以背后续的BeanFactoryPostProcessor进行后置处理。

同时需要注意的是BeanDefinitionRegistryPostProcessor本身也是BeanFactoryProcessor的扩展抽象:

// 继承BeanFactoryProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    // 该扩展点提供了BeanDefinitionRegistry,利用其可以向上下文中注册BeanDefinition
    // 同时也能修改同时也能修改BeanDefinitionRegistry的属性
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

关于ImportBeanDefinitionRegistrar的原理,其中ImportBeanDefinitionRegistrar主要是Spring在处理@Configurer注解时的扩展点,需要了解Spring注解配置处理原理的基础,才能够清晰的理解,故本文中不做详细介绍,待后续文章中介绍Spring注解配置原理中再细说ImportBeanDefinitionRegistrar的原理。

本节主要针对BeanDefinitionParser的原理实现做详细介绍。

为了更好的讲解BeanDefinitionParser,这里先总结下几个与其相关的重要组件:

  • DefaultBeanDefinitionDocumentReader
  • BeanDefinitionParserDelegate
  • DefaultNamespaceHandlerResolver
  • NameSpaceHandler

DefaultBeanDefinitionDocumentReader和BeanDefinitionParserDelegate在前面的Spring源码系列 — BeanDefinition文章有过源码程度的分析。前者主要负责读取Document文档中的BeanDefinition配置,后者负责解析配置并负责委托处理其他的命名空间配置的解析。

DefaultNamespaceHandlerResolver是用于解析命名空间处理器,它主要提供根据XML命名空间解析NameSpaceHandler的能力。

NameSpaceHandler提供两个能力,其一是能够注册BeanDefinitionParser和XML Tag的映射关系;其二提供根据XML Tag寻找BeanDefinitionParser。

总结下,即DefaultNamespaceHandlerResolver包含XML命名空间和NameSpaceHandler的映射关系,NameSpaceHandler中包含XML Tag和BeanDefinitionParser的映射关系。

触发BeanDefinitionParser XML Tag的流程如下:

接下来就从源码的角度分析下这个流程。仍然回到DefaultBeanDefinitionDocumentReader中parseBeanDefinitions方法:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 判断XML根元素是否为默认Beans命名空间,如果是则按照默认方式解析
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                // 判断子元素是否为默认的Beans命名空间,如果是则解析Beans
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    // 如果不是,则认为是自定义的
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        // 如果不是,则认为是自定义的
        delegate.parseCustomElement(root);
    }
}

对于非Beans命名空间而言,主要进入delegate.parseCustomElement分支,解析自定义的XML Tag。

再来详细看parseCustomElement实现:

 // 解析BeanDefinition
 public BeanDefinition parseCustomElement(Element ele) {
     return parseCustomElement(ele, null);
 }
 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
     // 获取该Element对应的命名空间,利用了Java XML提供的接口
     String namespaceUri = getNamespaceURI(ele);
     // 根据命名空间获取NameSpaceHandler
     NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
     if (handler == null) {
         error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
         return null;
     }
     // 利用NameSpaceHandler解析Element
     return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
 }

其中主要就是根据命名空间获取NameSpaceHandler,然后利用handler解析XML ELemnent为BeanDefinition。主要关注NameSpaceHandler的获取过程:

@Override
public NamespaceHandler resolve(String namespaceUri) {
    // 获取命名空间和NameSpaceHandler的映射关系
    Map<String, Object> handlerMappings = getHandlerMappings();
    // 根据命名空间获取NameSpaceHandler
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    // 如果为空,则返回null
    if (handlerOrClassName == null) {
        return null;
    }
    // 如果直接是NameSpaceHandler的实例,则直接返回
    else if (handlerOrClassName instanceof NamespaceHandler) {
        return (NamespaceHandler) handlerOrClassName;
    }
    // 否则认为是NameSpaceHandler实现的类名
    else {
        // 转化为类名
        String className = (String) handlerOrClassName;
        try {
            // 获取对应的Class对象
            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                        "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
            }
            // 根据Class对象,创建NameSpaceHandler实例
            NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
            // 执行初始化方法
            namespaceHandler.init();
            // 覆盖原有的映射关系,缓存作用
            handlerMappings.put(namespaceUri, namespaceHandler);
            // 返回NameSpaceHandler
            return namespaceHandler;
        }
        catch (ClassNotFoundException ex) {
            throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
                    namespaceUri + "] not found", ex);
        }
        catch (LinkageError err) {
            throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
                    namespaceUri + "]: problem with handler class file or dependent class", err);
        }
    }
}

以上的逻辑也非常简单,首先获取命名空间和NameSpaceHandler的映射关系,然后根据命名空间获取相应的NameSpaceHandler。这里主要需要关注的是如何获取命名空间和NameSpaceHandler的映射关系:

private Map<String, Object> getHandlerMappings() {
    Map<String, Object> handlerMappings = this.handlerMappings;
    // 如果handlerMappings不为空,则直接返回,否则加载handlerMappings
    if (handlerMappings == null) {
        // 对handlerMappings的修改有数据竞态,同步
        synchronized (this) {
            // 双重锁定检查,如果仍然为空,则加载handlerMappings
            handlerMappings = this.handlerMappings;
            if (handlerMappings == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
                }
                try {
                    // 根据handlerMappingsLocation指定的文章,使用工具加载properties
                    Properties mappings =
                            PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                    }
                    // 将properties转为ConcurrentHashMap
                    handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
                    CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                    this.handlerMappings = handlerMappings;
                }
                catch (IOException ex) {
                    throw new IllegalStateException(
                            "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
                }
            }
        }
    }
    return handlerMappings;
}

需要注意的是,这里Spring使用了约定配置的做法,对于获取映射关系配置,是由Spring框架内置和应用扩展的。在spring中定义了默认的配置文件位置:

public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

即在类路径下的META-INF/spring.handlers中配置。这是spring约定。所以上节的案例中也配置该文件。同时在spring的其他模块,如:beans、context、aop中都有该配置文件。

beans模块中配置如下:

http\\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

context模块中配置如下:

http\\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler

aop模块配置如下:

http\\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

DefaultNameSpaceHandlerResovler中中持有命名空间和命名空间处理器的映射关系。在获取到相应的命名空间处理器后,需要进行初始化。初始化的过程就是注册BeanDefinitionParser的过程,该过程主要是建立XML Tag与BeanDefinitionParser的之间的映射关系。如上节的案例中,建立了"MyBean"的Tag和MyBeanDefinitionParser之间的关系。这里以ContextNamespaceHandler为例,讲解其init方法的细节:

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

    // 初始化,注册BeanDefinitionParser,建立XML Tag与BeanDefinitionParser之间的关系
    @Override
    public void init() {
        // 注册PropertyPlaceholderBeanDefinitionParser,让其解析Tag:"property-placeholder"
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        // 注册PropertyOverrideBeanDefinitionParser,让其解析Tag:"property-override"
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        // 注册AnnotationConfigBeanDefinitionParser,让其解析Tag:"annotation-config"
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        // 注册ComponentScanBeanDefinitionParser,让其解析Tag:"ComponentScanBeanDefinitionParser"
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        // 注册LoadTimeWeaverBeanDefinitionParser,让其解析Tag:"load-time-weaver"
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        // 注册SpringConfiguredBeanDefinitionParser,让其解析Tag:"spring-configured"
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        // 注册MBeanExportBeanDefinitionParser,让其解析Tag:"mbean-export"
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        // 注册MBeanServerBeanDefinitionParser,让其解析Tag:"mbean-server"
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }
}

从以上ContextNameSpaceHandelr中可以看出Context命名空间下的各个XML Tag所对应的BeanDefinitionParser是什么。比如常用的component:scan标签由ComponentScanBeanDefinitionParser负责解析。关于这些BeanDefinitonParser的实现细节,将在下篇Spring中注解处理中挑一些详细介绍,这里不再详述。

再继续看handler.parse的实现,其中主要是根据元素的Tag寻找对应的BeanDefinitionParser,然后解析XML Element为对应的BeanDefinition。仍然以ContextNameSpaceHandler为例介绍:

@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 查找BeanDefinitionParser,然后解析Element
    return findParserForElement(element, parserContext).parse(element, parserContext);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    // 获取Element的Tag
    String localName = parserContext.getDelegate().getLocalName(element);
    // 根据Tag获取BeanDefinitionParser
    BeanDefinitionParser parser = this.parsers.get(localName);
    if (parser == null) {
        parserContext.getReaderContext().fatal(
                "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }
    // 返回Parser
    return parser;
}

在NameSpaceHandler中是利用Map存储Tag与BeanDefinitionParser之间的映射关系的。

到这里,应该能从头至尾非常清楚的了解了BeanDefinitionParser支撑应用自定义扩展XML Tag解析BeanDefintion的原理了。

总结

本文主要介绍了Spring中BeanDefition中处理的扩展点。主要从扩展点的方式、实战案例、原理三个方面层层深入介绍。

参考

Extensible XML authoring

以上是关于Spring源码系列 — BeanDefinition扩展点的主要内容,如果未能解决你的问题,请参考以下文章

AOP执行增强-Spring 源码系列

spring源码解析系列

原Spring源码浅析系列-导入源码到Eclipse

Spring读源码系列02----默认标签解析过程

0源码基础学习Spring源码系列——Spring如何解决循环依赖

Spring读源码系列01---Spring核心类及关联性介绍