Spring 源码解析八:Xml 配置中默认的命名空间处理器

在 Spring 源码解析五:Bean 的配置、定义、注册 中,有一些 Xml 配置中默认的命名空间处理器还未解析

  • SimpleConstructorNamespaceHandler
  • SimplePropertyNamespaceHandler
  • UtilNamespaceHandler
  • ContextNamespaceHandler
  • JeeNamespaceHandler
  • LangNamespaceHandler
  • TaskNamespaceHandler
  • CacheNamespaceHandler
  • MvcNamespaceHandler
  • AopNamespaceHandler
  • JdbcNamespaceHandler
  • TxNamespaceHandler

这里只解析罕用的几个

  • SimpleConstructorNamespaceHandler
  • SimplePropertyNamespaceHandler
  • ContextNamespaceHandler
  • TaskNamespaceHandler
  • CacheNamespaceHandler
  • MvcNamespaceHandler

其余的有趣味能够自行摸索

1. SimpleConstructorNamespaceHandler

SimpleConstructorNamespaceHandler
的次要性能是在实例化如下配置的 bean

<bean id="author" class="..TestBean" c:name="Enescu" c:work-ref="compositions"/>

name="Enescu", work=bean[compositions] 作为构造函数的参数实例化 bean

public class SimpleConstructorNamespaceHandler implements NamespaceHandler {    @Override    public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {        if (node instanceof Attr) {            Attr attr = (Attr) node;            // 去除两边空白            String argName = StringUtils.trimWhitespace(parserContext.getDelegate().getLocalName(attr));            // 去除两边空白            String argValue = StringUtils.trimWhitespace(attr.getValue());            // 结构函数参数汇合            ConstructorArgumentValues cvs = definition.getBeanDefinition().getConstructorArgumentValues();            boolean ref = false;            // 如果字段名以"-ref"后果,则示意在运行时对其余bean的援用            if (argName.endsWith(REF_SUFFIX)) {                ref = true;                argName = argName.substring(0, argName.length() - REF_SUFFIX.length());            }            // 用ValueHolder封装值            ValueHolder valueHolder = new ValueHolder(ref ? new RuntimeBeanReference(argValue) : argValue);            valueHolder.setSource(parserContext.getReaderContext().extractSource(attr));            // 以"_"结尾的,示意是没有名字的参数,依照程序传入就能够了            if (argName.startsWith(DELIMITER_PREFIX)) {                String arg = argName.substring(1).trim();                // 如果没有指定程序,间接增加                if (!StringUtils.hasText(arg)) {                    cvs.addGenericArgumentValue(valueHolder);                }                // 如果有指定程序,指定程序增加                else {                    int index = -1;                    try {                        index = Integer.parseInt(arg);                    }                    catch (NumberFormatException ex) {                        // ... 代码省略                    }                    // ... 代码省略                    // 增加进cvs                    cvs.addIndexedArgumentValue(index, valueHolder);                }            }            // 有名字的参数            else {                // ... 代码省略                // 增加进cvs                valueHolder.setName(Conventions.attributeNameToPropertyName(argName));                cvs.addGenericArgumentValue(valueHolder);            }        }        return definition;    }}

2. SimplePropertyNamespaceHandler

SimplePropertyNamespaceHandler
的次要性能是在实例化如下配置的 bean

<bean id="rob" class="..TestBean" p:name="Rob Harrop" p:spouse-ref="sally"/>

name="Rob Harrop", spouse=bean[sally] 作为属性注入到 bean 中

public class SimplePropertyNamespaceHandler implements NamespaceHandler {    @Override    public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {        if (node instanceof Attr) {            Attr attr = (Attr) node;            // 属性名            String propertyName = parserContext.getDelegate().getLocalName(attr);            // 属性值            String propertyValue = attr.getValue();            // 属性汇合            MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues();            // 如果字段名以"-ref"后果,则示意在运行时对其余bean的援用            if (propertyName.endsWith(REF_SUFFIX)) {                propertyName = propertyName.substring(0, propertyName.length() - REF_SUFFIX.length());                // 把字段名从短横线-或下划线_式的,转化为驼峰式的                pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference(propertyValue));            }            else {                // 把字段名从短横线-或下划线_式的,转化为驼峰式的                pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue);            }        }        return definition;    }}

3. ContextNamespaceHandler

public class ContextNamespaceHandler extends NamespaceHandlerSupport {    @Override    public void init() {        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());    }}

3.1. context:property-placeholder

PropertyPlaceholderBeanDefinitionParser
的次要性能是解析<context:property-placeholder/>元素,在 bean 定义时,应用 ${} 援用内部起源的属性

先看看继承关系

- AbstractBeanDefinitionParser  - AbstractSingleBeanDefinitionParser    - AbstractPropertyLoadingBeanDefinitionParser      - PropertyPlaceholderBeanDefinitionParser

3.1.1. AbstractBeanDefinitionParser

AbstractBeanDefinitionParser
的次要性能是实现了 BeanDefinitionParser
接口的 parse 办法,提供了根本的由配置解析为对象的性能

public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {    @Override    public final BeanDefinition parse(Element element, ParserContext parserContext) {        // 解析元素        AbstractBeanDefinition definition = parseInternal(element, parserContext);        try {            // 获取bean的id            String id = resolveId(element, definition, parserContext);            String[] aliases = null;            // 获取name属性,用逗号分隔为多个别名            if (shouldParseNameAsAliases()) {                String name = element.getAttribute(NAME_ATTRIBUTE);                if (StringUtils.hasLength(name)) {                    aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));                }            }            // 封装为BeanDefinitionHolder            BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);            // 注册到registry中            registerBeanDefinition(holder, parserContext.getRegistry());            // ... 代码省略        }        catch (BeanDefinitionStoreException ex) {            // ... 代码省略        }        return definition;    }    // 解析元素,由子类实现    protected abstract AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext);    // 获取bean的id,先获取id属性,其次name属性,都没有,生成默认的名字    protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)            throws BeanDefinitionStoreException {        // ... 代码省略    }}

3.1.2. AbstractSingleBeanDefinitionParser

AbstractSingleBeanDefinitionParser
的次要性能是反对解析为单例的 bean

public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDefinitionParser {    @Override    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {        // 创立一个结构器        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();        // 父元素        String parentName = getParentName(element);        if (parentName != null) {            builder.getRawBeanDefinition().setParentName(parentName);        }        // beanClass        Class<?> beanClass = getBeanClass(element);        if (beanClass != null) {            builder.getRawBeanDefinition().setBeanClass(beanClass);        }        // beanClassName        else {            String beanClassName = getBeanClassName(element);            if (beanClassName != null) {                builder.getRawBeanDefinition().setBeanClassName(beanClassName);            }        }        // ... 代码省略        // 解析到结构器        doParse(element, parserContext, builder);        return builder.getBeanDefinition();    }    // 解析到结构器,由子类实现    protected void doParse(Element element, BeanDefinitionBuilder builder) {}}

3.1.3. AbstractPropertyLoadingBeanDefinitionParser

AbstractPropertyLoadingBeanDefinitionParser
的次要性能是解析context:property-...元素

abstract class AbstractPropertyLoadingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {    @Override    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {        // location属性        String location = element.getAttribute("location");        if (StringUtils.hasLength(location)) {            location = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(location);            String[] locations = StringUtils.commaDelimitedListToStringArray(location);            builder.addPropertyValue("locations", locations);        }        // properties-ref属性        String propertiesRef = element.getAttribute("properties-ref");        if (StringUtils.hasLength(propertiesRef)) {            builder.addPropertyReference("properties", propertiesRef);        }        // file-encoding属性        String fileEncoding = element.getAttribute("file-encoding");        if (StringUtils.hasLength(fileEncoding)) {            builder.addPropertyValue("fileEncoding", fileEncoding);        }        // order属性        String order = element.getAttribute("order");        if (StringUtils.hasLength(order)) {            builder.addPropertyValue("order", Integer.valueOf(order));        }        // ignore-resource-not-found属性        builder.addPropertyValue("ignoreResourceNotFound",                Boolean.valueOf(element.getAttribute("ignore-resource-not-found")));        // local-override属性        builder.addPropertyValue("localOverride",                Boolean.valueOf(element.getAttribute("local-override")));    }}

3.1.4. PropertyPlaceholderBeanDefinitionParser

PropertyPlaceholderBeanDefinitionParser
的次要性能是解析context:property-placeholder元素

class PropertyPlaceholderBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser {    @Override    protected Class<?> getBeanClass(Element element) {        // 应用这个类来实现bean        return org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.class;    }    @Override    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {        super.doParse(element, parserContext, builder);        // ignore-unresolvable属性        builder.addPropertyValue("ignoreUnresolvablePlaceholders",                Boolean.valueOf(element.getAttribute("ignore-unresolvable")));        // system-properties-mode属性        String systemPropertiesModeName = element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE);        if (StringUtils.hasLength(systemPropertiesModeName) &&                !systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {            builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_" + systemPropertiesModeName);        }        // value-separator属性        if (element.hasAttribute("value-separator")) {            builder.addPropertyValue("valueSeparator", element.getAttribute("value-separator"));        }        // trim-values属性        if (element.hasAttribute("trim-values")) {            builder.addPropertyValue("trimValues", element.getAttribute("trim-values"));        }        // null-value属性        if (element.hasAttribute("null-value")) {            builder.addPropertyValue("nullValue", element.getAttribute("null-value"));        }    }}

3.2. context:property-override

PropertyOverrideBeanDefinitionParser
的次要性能是解析<context:property-override/>元素,为 xml 配置文件中的 bean 的属性指定最终后果

class PropertyOverrideBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser {    @Override    protected Class<?> getBeanClass(Element element) {        // 应用这个类来实现bean        return PropertyOverrideConfigurer.class;    }    @Override    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {        super.doParse(element, parserContext, builder);        // ignore-unresolvable属性        builder.addPropertyValue("ignoreInvalidKeys",                Boolean.valueOf(element.getAttribute("ignore-unresolvable")));    }}

3.3. context:annotation-config

AnnotationConfigBeanDefinitionParser
的次要性能是解析<context:annotation-config/>元素,应用 @Autowired 主动拆卸 bean

public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {    @Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        Object source = parserContext.extractSource(element);        // 获取registry所有相干的bean定义        Set<BeanDefinitionHolder> processorDefinitions =                AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);        // ... 代码省略        // 注册bean定义        for (BeanDefinitionHolder processorDefinition : processorDefinitions) {            parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));        }        // ... 代码省略        return null;    }}

3.4. context:component-scan

ComponentScanBeanDefinitionParser
的次要性能是解析<context:component-scan/>元素,主动扫描指定包下的注解 bean

public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {    @Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        // base-package属性        String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);        basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);        // 分隔多个包        String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);        // 创立并配置扫描对象        ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);        // 扫描包中的组件        Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);        // 注册组件到上下文中        registerComponents(parserContext.getReaderContext(), beanDefinitions, element);        return null;    }}

对于 context: 命令空间的解析就到这里了,其余的有趣味能够自行摸索

4. TaskNamespaceHandler

public class TaskNamespaceHandler extends NamespaceHandlerSupport {    @Override    public void init() {        this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());        this.registerBeanDefinitionParser("executor", new ExecutorBeanDefinitionParser());        this.registerBeanDefinitionParser("scheduled-tasks", new ScheduledTasksBeanDefinitionParser());        this.registerBeanDefinitionParser("scheduler", new SchedulerBeanDefinitionParser());    }}

4.1. task:annotation-driven

AnnotationDrivenBeanDefinitionParser
的次要性能是解析<task:annotation-driven/>元素,开启定时器开关,主动扫描程序中带注解的定时器

public class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {    @Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        Object source = parserContext.extractSource(element);        // ... 代码省略        // 获取registry        BeanDefinitionRegistry registry = parserContext.getRegistry();        // mode属性        String mode = element.getAttribute("mode");        // 应用aspectj来执行异步工作        if ("aspectj".equals(mode)) {            // mode="aspectj"            registerAsyncExecutionAspect(element, parserContext);        }        // 应用内置的性能来执行异步工作        else {            // bean "org.springframework.context.annotation.internalAsyncAnnotationProcessor" 曾经存在            if (registry.containsBeanDefinition(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)) {                // ... 代码省略            }            else {                // 应用类 "org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor"                // 来配置bean "org.springframework.context.annotation.internalAsyncAnnotationProcessor"                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(                        "org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor");                builder.getRawBeanDefinition().setSource(source);                // executor属性                String executor = element.getAttribute("executor");                if (StringUtils.hasText(executor)) {                    builder.addPropertyReference("executor", executor);                }                // exception-handler属性                String exceptionHandler = element.getAttribute("exception-handler");                if (StringUtils.hasText(exceptionHandler)) {                    builder.addPropertyReference("exceptionHandler", exceptionHandler);                }                // proxy-target-class属性                if (Boolean.parseBoolean(element.getAttribute(AopNamespaceUtils.PROXY_TARGET_CLASS_ATTRIBUTE))) {                    builder.addPropertyValue("proxyTargetClass", true);                }                // 注册组件到registry与上下文                registerPostProcessor(parserContext, builder, TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);            }        }        // bean "org.springframework.context.annotation.internalScheduledAnnotationProcessor" 曾经存在        if (registry.containsBeanDefinition(TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)) {            // ... 代码省略        }        else {            // 应用类 "org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor"            // 来配置bean "org.springframework.context.annotation.internalScheduledAnnotationProcessor"            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(                    "org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor");            builder.getRawBeanDefinition().setSource(source);            // scheduler属性            String scheduler = element.getAttribute("scheduler");            if (StringUtils.hasText(scheduler)) {                builder.addPropertyReference("scheduler", scheduler);            }            // 注册组件到registry与上下文            registerPostProcessor(parserContext, builder, TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME);        }        // ... 代码省略        return null;    }    // 应用aspectj来执行异步工作    private void registerAsyncExecutionAspect(Element element, ParserContext parserContext) {        // bean "org.springframework.scheduling.config.internalAsyncExecutionAspect" 不存在        if (!parserContext.getRegistry().containsBeanDefinition(TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME)) {            // 应用类 "org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect"            // 来配置bean "org.springframework.scheduling.config.internalAsyncExecutionAspect"            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ASYNC_EXECUTION_ASPECT_CLASS_NAME);            // 应用FactoryMethod来初始化bean            builder.setFactoryMethod("aspectOf");            // executor属性            String executor = element.getAttribute("executor");            if (StringUtils.hasText(executor)) {                builder.addPropertyReference("executor", executor);            }            // exception-handler属性            String exceptionHandler = element.getAttribute("exception-handler");            if (StringUtils.hasText(exceptionHandler)) {                builder.addPropertyReference("exceptionHandler", exceptionHandler);            }            // 注册bean            parserContext.registerBeanComponent(new BeanComponentDefinition(builder.getBeanDefinition(),                    TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME));        }    }}

4.2. task:executor

ExecutorBeanDefinitionParser
的次要性能是解析<task:executor/>元素,用于配置工作执行器

public class ExecutorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {    @Override    protected String getBeanClassName(Element element) {        // 应用这个类来实现bean        return "org.springframework.scheduling.config.TaskExecutorFactoryBean";    }    @Override    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {        // keep-alive属性        String keepAliveSeconds = element.getAttribute("keep-alive");        if (StringUtils.hasText(keepAliveSeconds)) {            builder.addPropertyValue("keepAliveSeconds", keepAliveSeconds);        }        // queue-capacity属性        String queueCapacity = element.getAttribute("queue-capacity");        if (StringUtils.hasText(queueCapacity)) {            builder.addPropertyValue("queueCapacity", queueCapacity);        }        // 配置策略        configureRejectionPolicy(element, builder);        // pool-size属性        String poolSize = element.getAttribute("pool-size");        if (StringUtils.hasText(poolSize)) {            builder.addPropertyValue("poolSize", poolSize);        }    }    // 配置策略    private void configureRejectionPolicy(Element element, BeanDefinitionBuilder builder) {        // rejection-policy属性        String rejectionPolicy = element.getAttribute("rejection-policy");        if (!StringUtils.hasText(rejectionPolicy)) {            return;        }        String prefix = "java.util.concurrent.ThreadPoolExecutor.";        String policyClassName;        if (rejectionPolicy.equals("ABORT")) {            policyClassName = prefix + "AbortPolicy";        }        else if (rejectionPolicy.equals("CALLER_RUNS")) {            policyClassName = prefix + "CallerRunsPolicy";        }        else if (rejectionPolicy.equals("DISCARD")) {            policyClassName = prefix + "DiscardPolicy";        }        else if (rejectionPolicy.equals("DISCARD_OLDEST")) {            policyClassName = prefix + "DiscardOldestPolicy";        }        else {            policyClassName = rejectionPolicy;        }        builder.addPropertyValue("rejectedExecutionHandler", new RootBeanDefinition(policyClassName));    }}

4.3. task:scheduled-tasks

ScheduledTasksBeanDefinitionParser
的次要性能是解析<task:scheduled-tasks/>元素,执行定时器工作

public class ScheduledTasksBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {    @Override    protected String getBeanClassName(Element element) {        // 应用这个类来实现bean        return "org.springframework.scheduling.config.ContextLifecycleScheduledTaskRegistrar";    }    @Override    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {        // cron工作        ManagedList<RuntimeBeanReference> cronTaskList = new ManagedList<>();        // 固定提早工作        ManagedList<RuntimeBeanReference> fixedDelayTaskList = new ManagedList<>();        // 固定频率工作        ManagedList<RuntimeBeanReference> fixedRateTaskList = new ManagedList<>();        // 触发工作        ManagedList<RuntimeBeanReference> triggerTaskList = new ManagedList<>();        // 遍历子元素        NodeList childNodes = element.getChildNodes();        for (int i = 0; i < childNodes.getLength(); i++) {            Node child = childNodes.item(i);            Element taskElement = (Element) child;            // ref属性            String ref = taskElement.getAttribute("ref");            // method属性            String method = taskElement.getAttribute("method");            // ... 代码省略            // cron属性            String cronAttribute = taskElement.getAttribute("cron");            // fixed-delay属性            String fixedDelayAttribute = taskElement.getAttribute("fixed-delay");            // fixed-rate属性            String fixedRateAttribute = taskElement.getAttribute("fixed-rate");            // trigger属性            String triggerAttribute = taskElement.getAttribute("trigger");            // initial-delay属性            String initialDelayAttribute = taskElement.getAttribute("initial-delay");            boolean hasCronAttribute = StringUtils.hasText(cronAttribute);            boolean hasFixedDelayAttribute = StringUtils.hasText(fixedDelayAttribute);            boolean hasFixedRateAttribute = StringUtils.hasText(fixedRateAttribute);            boolean hasTriggerAttribute = StringUtils.hasText(triggerAttribute);            boolean hasInitialDelayAttribute = StringUtils.hasText(initialDelayAttribute);            // ... 代码省略            // 应用类 "org.springframework.scheduling.support.ScheduledMethodRunnable"            // 实例化定时器bean            String runnableName =                    runnableReference(ref, method, taskElement, parserContext).getBeanName();            if (hasFixedDelayAttribute) {                // 应用类 "org.springframework.scheduling.config.IntervalTask"                // 实例化固定提早执行器bean                fixedDelayTaskList.add(intervalTaskReference(runnableName,                        initialDelayAttribute, fixedDelayAttribute, taskElement, parserContext));            }            if (hasFixedRateAttribute) {                // 应用类 "org.springframework.scheduling.config.IntervalTask"                // 实例化固定频率执行器bean                fixedRateTaskList.add(intervalTaskReference(runnableName,                        initialDelayAttribute, fixedRateAttribute, taskElement, parserContext));            }            if (hasCronAttribute) {                // 应用类 "org.springframework.scheduling.config.CronTask"                // 实例化cron执行器bean                cronTaskList.add(cronTaskReference(runnableName, cronAttribute,                        taskElement, parserContext));            }            if (hasTriggerAttribute) {                // 应用类 "org.springframework.scheduling.config.TriggerTask"                // 实例化触发执行器bean                String triggerName = new RuntimeBeanReference(triggerAttribute).getBeanName();                triggerTaskList.add(triggerTaskReference(runnableName, triggerName,                        taskElement, parserContext));            }        }        // scheduler属性        String schedulerRef = element.getAttribute("scheduler");        if (StringUtils.hasText(schedulerRef)) {            builder.addPropertyReference("taskScheduler", schedulerRef);        }        builder.addPropertyValue("cronTasksList", cronTaskList);        builder.addPropertyValue("fixedDelayTasksList", fixedDelayTaskList);        builder.addPropertyValue("fixedRateTasksList", fixedRateTaskList);        builder.addPropertyValue("triggerTasksList", triggerTaskList);    }}

4.4. task:scheduler

SchedulerBeanDefinitionParser
的次要性能是解析<task:scheduler/>元素,配置调度线程池

public class SchedulerBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {    @Override    protected String getBeanClassName(Element element) {        // 应用这个类来实现bean        return "org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler";    }    @Override    protected void doParse(Element element, BeanDefinitionBuilder builder) {        // pool-size属性        String poolSize = element.getAttribute("pool-size");        if (StringUtils.hasText(poolSize)) {            builder.addPropertyValue("poolSize", poolSize);        }    }}

5. CacheNamespaceHandler

public class CacheNamespaceHandler extends NamespaceHandlerSupport {    @Override    public void init() {        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser());        registerBeanDefinitionParser("advice", new CacheAdviceParser());    }}

5.1. cache:annotation-driven

AnnotationDrivenCacheBeanDefinitionParser
的次要性能是解析<cache:annotation-driven/>元素,反对注解注 @Cacheable@CacheEvict@CacheUpdate

class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser {    @Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        // mode属性        String mode = element.getAttribute("mode");        // 应用aspectj来执行        if ("aspectj".equals(mode)) {            registerCacheAspect(element, parserContext);        }        // 应用内置的性能来执行        else {            registerCacheAdvisor(element, parserContext);        }        return null;    }    // 应用aspectj来执行    private void registerCacheAspect(Element element, ParserContext parserContext) {        SpringCachingConfigurer.registerCacheAspect(element, parserContext);    }    // 应用内置的性能来执行    private void registerCacheAdvisor(Element element, ParserContext parserContext) {        AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);        SpringCachingConfigurer.registerCacheAdvisor(element, parserContext);    }    // 解析cache-resolver属性    private static void parseCacheResolution(Element element, BeanDefinition def, boolean setBoth) {        String name = element.getAttribute("cache-resolver");        boolean hasText = StringUtils.hasText(name);        if (hasText) {            def.getPropertyValues().add("cacheResolver", new RuntimeBeanReference(name.trim()));        }        if (!hasText || setBoth) {            def.getPropertyValues().add("cacheManager",                    new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));        }    }}

外部类 JCacheCachingConfigurer

private static class JCacheCachingConfigurer {    // 应用内置的性能来执行    private static void registerCacheAdvisor(Element element, ParserContext parserContext) {        // bean "org.springframework.cache.config.internalJCacheAdvisor" 不存在        if (!parserContext.getRegistry().containsBeanDefinition(CacheManagementConfigUtils.JCACHE_ADVISOR_BEAN_NAME)) {            Object source = parserContext.extractSource(element);            // 创立一个JCache执行的bean            BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, source);            // 获取bean名字            String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);            // 应用 "org.springframework.cache.jcache.interceptor.JCacheInterceptor"            // 创立CacheInterceptor的bean定义            RootBeanDefinition interceptorDef =                    new RootBeanDefinition("org.springframework.cache.jcache.interceptor.JCacheInterceptor");            // ... 代码省略            // 获取bean名字            String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);            // 应用 "org.springframework.cache.jcache.interceptor.BeanFactoryJCacheOperationSourceAdvisor"            // 创立CacheAdvisor定义            RootBeanDefinition advisorDef = new RootBeanDefinition(                    "org.springframework.cache.jcache.interceptor.BeanFactoryJCacheOperationSourceAdvisor");            // ... 代码省略            // 注册bean "org.springframework.cache.config.internalJCacheAdvisor"            parserContext.getRegistry().registerBeanDefinition(CacheManagementConfigUtils.JCACHE_ADVISOR_BEAN_NAME, advisorDef);            // 注册复合bean sourceDef+interceptorDef+advisorDef            CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);            compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));            compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));            compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, CacheManagementConfigUtils.JCACHE_ADVISOR_BEAN_NAME));            parserContext.registerComponent(compositeDef);        }    }    // 应用aspectj来执行    private static void registerCacheAspect(Element element, ParserContext parserContext) {        // bean "org.springframework.cache.config.internalJCacheAspect" 不存在        if (!parserContext.getRegistry().containsBeanDefinition(CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME)) {            Object eleSource = parserContext.extractSource(element);            RootBeanDefinition def = new RootBeanDefinition();            // 应用 "org.springframework.cache.aspectj.JCacheCacheAspect"            // 来创立bean            def.setBeanClassName(JCACHE_ASPECT_CLASS_NAME);            // 应用FactoryMethod来初始化bean            def.setFactoryMethodName("aspectOf");            // 创立一个JCache执行的bean            BeanDefinition sourceDef = createJCacheOperationSourceBeanDefinition(element, eleSource);            String sourceName =                    parserContext.getReaderContext().registerWithGeneratedName(sourceDef);            // 注册bean "org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource"            parserContext.registerBeanComponent(new BeanComponentDefinition(sourceDef, sourceName));            // 注册bean "org.springframework.cache.config.internalJCacheAspect"            parserContext.registerBeanComponent(new BeanComponentDefinition(def, CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME));        }    }    // 创立一个JCache执行的bean    private static RootBeanDefinition createJCacheOperationSourceBeanDefinition(Element element, @Nullable Object eleSource) {        // 应用 "org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource""        // 来创立bean        RootBeanDefinition sourceDef =                new RootBeanDefinition("org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource");        // ... 代码省略        // 解析cache-resolver属性        CacheNamespaceHandler.parseKeyGenerator(element, sourceDef);        return sourceDef;    }}

5.2. cache:advice

CacheAdviceParser
的次要性能是解析<cache:advice/>元素,配置缓存

class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {    @Override    protected Class<?> getBeanClass(Element element) {        // 应用这个类来实现bean        return CacheInterceptor.class;    }    @Override    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {        // cache-manager属性        builder.addPropertyReference("cacheManager", CacheNamespaceHandler.extractCacheManager(element));        // caching元素        List<Element> cacheDefs = DomUtils.getChildElementsByTagName(element, DEFS_ELEMENT);        // 有caching元素        if (!cacheDefs.isEmpty()) {            // 把caching元素转化成bean            List<RootBeanDefinition> attributeSourceDefinitions = parseDefinitionsSources(cacheDefs, parserContext);            builder.addPropertyValue("cacheOperationSources", attributeSourceDefinitions);        }        else {            // 没有caching元素,用 "org.springframework.cache.annotation.AnnotationCacheOperationSource"            // 创立一个默认的bean            builder.addPropertyValue("cacheOperationSources",                    new RootBeanDefinition("org.springframework.cache.annotation.AnnotationCacheOperationSource"));        }    }    // 把caching元素转化成bean    private List<RootBeanDefinition> parseDefinitionsSources(List<Element> definitions, ParserContext parserContext) {        ManagedList<RootBeanDefinition> defs = new ManagedList<>(definitions.size());        // 遍历        for (Element element : definitions) {            defs.add(parseDefinitionSource(element, parserContext));        }        return defs;    }    // 把caching元素转化成bean    private RootBeanDefinition parseDefinitionSource(Element definition, ParserContext parserContext) {        // ... 代码省略    }}

6. MvcNamespaceHandler

public class MvcNamespaceHandler extends NamespaceHandlerSupport {    @Override    public void init() {        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());        registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());        registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());        registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());        registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());        registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());        registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());        registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());        registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());        registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());        registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());        registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());        registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());    }}

mvc 命名空间的处理器内容较多,这里只解析第一个 mvc:annotation-driven,其余的有趣味能够自行摸索

AnnotationDrivenBeanDefinitionParser
的次要性能是解析<mvc:annotation-driven/>元素,启用注解驱动,通过context:component-scan标签的配置,主动扫描
@Component,@Controller,@Service,@Repository等注解标记的组件注册到工厂中,来解决申请

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {    @Override    public BeanDefinition parse(Element element, ParserContext context) {        // 元素元信息        Object source = context.extractSource(element);        XmlReaderContext readerContext = context.getReaderContext();        // ... 代码省略        // 申请映射处理器bean        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);        // ... 代码省略        // enable-matrix-variables属性        if (element.hasAttribute("enable-matrix-variables")) {            boolean enableMatrixVariables = Boolean.parseBoolean(element.getAttribute("enable-matrix-variables"));            handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);        }        // 配置门路映射        configurePathMatchingProperties(handlerMappingDef, element, context);        // 注册RequestMappingHandlerMapping.class bean        readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);        // ... 代码省略        // 获取数据转换服务        RuntimeBeanReference conversionService = getConversionService(element, source, context);        // 获取验证器        RuntimeBeanReference validator = getValidator(element, source, context);        // 获取文本解析器        RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);        // 注册ConfigurableWebBindingInitializer.class bean        RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);        // ... 代码省略        // 载入conversionService、validator、messageCodesResolver        bindingDef.getPropertyValues().add("conversionService", conversionService);        bindingDef.getPropertyValues().add("validator", validator);        bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);        // 配置音讯转换器        ManagedList<?> messageConverters = getMessageConverters(element, source, context);        // argument-resolvers子元素配置的自定义参数解析器        ManagedList<?> argumentResolvers = getArgumentResolvers(element, context);        // return-value-handlers子元素配置的自定义响应解析器        ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, context);        // async-support子元素配置的异步工作超时工夫        String asyncTimeout = getAsyncTimeout(element);        // async-support子元素配置的异步工作执行器        RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);        // async-support子元素配置的callable-interceptors申请拦截器        ManagedList<?> callableInterceptors = getInterceptors(element, source, context, "callable-interceptors");        // async-support子元素配置的deferred-result-interceptors响应拦截器        ManagedList<?> deferredResultInterceptors = getInterceptors(element, source, context, "deferred-result-interceptors");        // 注册RequestMappingHandlerAdapter.class bean        RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);        // 载入contentNegotiationManager、bindingDef、messageConverters        handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);        handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);        handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);        // 载入jackson申请解决        addRequestBodyAdvice(handlerAdapterDef);        // 载入jackson响应解决        addResponseBodyAdvice(handlerAdapterDef);        // ignore-default-model-on-redirect属性        if (element.hasAttribute("ignore-default-model-on-redirect")) {            Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));            handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);        }        if (argumentResolvers != null) {            handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);        }        if (returnValueHandlers != null) {            handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);        }        if (asyncTimeout != null) {            handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);        }        if (asyncExecutor != null) {            handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);        }        handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);        handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);        // 注册RequestMappingHandlerAdapter.class bean        readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);        // 注册CompositeUriComponentsContributorFactoryBean.class bean        RootBeanDefinition uriContributorDef =                new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);        uriContributorDef.setSource(source);        uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);        uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);        // 注册mvcUriComponentsContributor bean        String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;        readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);        // 注册ConversionServiceExposingInterceptor.class bean        RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);        csInterceptorDef.setSource(source);        csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);        // 注册MappedInterceptor.class bean        RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);        // ... 代码省略        // 注册ExceptionHandlerExceptionResolver.class bean        RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);        // ... 代码省略        // 注册ResponseStatusExceptionResolver.class bean        RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);        // ... 代码省略        // 注册DefaultHandlerExceptionResolver.class bean        RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);        // ... 代码省略        // 注册一系列bean        context.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));        context.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));        context.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));        context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));        context.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));        context.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));        context.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));        // ... 代码省略        context.popAndRegisterContainingComponent();        return null;    }}
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {    // 配置门路映射    private void configurePathMatchingProperties(            RootBeanDefinition handlerMappingDef, Element element, ParserContext context) {        // 获取path-matching子元素        Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching");        if (pathMatchingElement != null) {            Object source = context.extractSource(element);            // suffix-pattern属性            if (pathMatchingElement.hasAttribute("suffix-pattern")) {                Boolean useSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern"));                handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);            }            // trailing-slash属性            if (pathMatchingElement.hasAttribute("trailing-slash")) {                Boolean useTrailingSlashMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash"));                handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useTrailingSlashMatch);            }            // registered-suffixes-only属性            if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {                Boolean useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only"));                handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);            }            RuntimeBeanReference pathHelperRef = null;            // path-helper属性            if (pathMatchingElement.hasAttribute("path-helper")) {                pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper"));            }            pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, context, source);            handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);            RuntimeBeanReference pathMatcherRef = null;            // path-matcher属性            if (pathMatchingElement.hasAttribute("path-matcher")) {                pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher"));            }            pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, context, source);            handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);        }    }    // 获取数据转换服务    private RuntimeBeanReference getConversionService(Element element, @Nullable Object source, ParserContext context) {        RuntimeBeanReference conversionServiceRef;        // 有配置conversion-service属性        if (element.hasAttribute("conversion-service")) {            conversionServiceRef = new RuntimeBeanReference(element.getAttribute("conversion-service"));        }        else {            // 没有就用FormattingConversionServiceFactoryBean创立一个默认的            RootBeanDefinition conversionDef = new RootBeanDefinition(FormattingConversionServiceFactoryBean.class);            // ... 代码省略            conversionServiceRef = new RuntimeBeanReference(conversionName);        }        return conversionServiceRef;    }    // 获取验证器    private RuntimeBeanReference getValidator(Element element, @Nullable Object source, ParserContext context) {        // 有配置validator属性        if (element.hasAttribute("validator")) {            return new RuntimeBeanReference(element.getAttribute("validator"));        }        else if (javaxValidationPresent) {            // 没有就用OptionalValidatorFactoryBean创立一个默认的            RootBeanDefinition validatorDef = new RootBeanDefinition(                    "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean");            // ... 代码省略            return new RuntimeBeanReference(validatorName);        }        else {            return null;        }    }    // 获取文本解析器    private RuntimeBeanReference getMessageCodesResolver(Element element) {        // 有配置message-codes-resolver属性        if (element.hasAttribute("message-codes-resolver")) {            return new RuntimeBeanReference(element.getAttribute("message-codes-resolver"));        }        else {            return null;        }    }}
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {    // 配置音讯转换器    private ManagedList<?> getMessageConverters(Element element, @Nullable Object source, ParserContext context) {        // message-converters子元素        Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");        // 后果集        ManagedList<Object> messageConverters = new ManagedList<>();        // 有message-converters子元素        if (convertersElement != null) {            // 把bean、ref子元素转换为bean,退出后果集            for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {                Object object = context.getDelegate().parsePropertySubElement(beanElement, null);                messageConverters.add(object);            }        }        // 有message-converters子元素,但有register-defaults属性        if (convertersElement == null || Boolean.parseBoolean(convertersElement.getAttribute("register-defaults"))) {            // 增加ByteArrayHttpMessageConverter.class bean到后果集            // 字节数组转换器            messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));            // 增加StringHttpMessageConverter.class bean到后果集            // 字符转换器            RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);            stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);            messageConverters.add(stringConverterDef);            // 增加ResourceHttpMessageConverter.class bean到后果集            // 资源转换器            messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));            // 增加ResourceRegionHttpMessageConverter.class bean到后果集            // 资源区域转换器            messageConverters.add(createConverterDefinition(ResourceRegionHttpMessageConverter.class, source));            // 增加SourceHttpMessageConverter.class bean到后果集            // javax.xml.transform.Source转换器            messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));            // 增加AllEncompassingFormHttpMessageConverter.class bean到后果集            // form-data转换器            messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));            // ... 代码省略            if (jackson2XmlPresent) {                // 增加jackson2Xml转换器            }            // ... 代码省略            if (jackson2Present) {                // 增加jackson2转换器            }            else if (gsonPresent) {                // 增加gson转换器            }            // ... 代码省略        }        return messageConverters;    }}

后续

更多博客,查看 https://github.com/senntyou/blogs

作者:深予之 (@senntyou)

版权申明:自在转载-非商用-非衍生-放弃署名(创意共享 3.0 许可证)