spring中怎么解析xml文件

Spring_创建spring配置文件xml找不到spring Config的解决方法

很多新手在创建Springxml文件的时候找不到Spring Config,如下图 解决方法,右键你的当前模块module。选择Add Framework Support。下拉找到Spring Mvc 勾上确认即可 可以找到config文件

源码学习之Spring容器创建原理_bean_xml_testStr

限于篇幅,这里只介绍该容器创建的部分,即对于new XmlBeanFactory(new ClassPathResource(“beanFactoryTest.xml”)),Spring都干了些什么。2 Spring容器创建原理 2.1 整体实现流程 首先我们大致了解一下Spring容器创建的整体...

Spring框架第二谈:IOC,xml配置文件给属性赋值,注解实现赋值

主配置文件负责把各模块连接起来->< import resource = "classpath:di01/spring-student.xml"/>< import resource = "classpath:di01/sprinh-school.xml"/> 各模块配置文件的写法: ...

Spring认证指南-如何使用 Spring 创建超媒体驱动 RESTful Web 服|spring|应用程序|restful|xml|json_网易...

Spring认证指南-如何使用 Spring 创建超媒体驱动 RESTful Web 服,spring,应用程序,restful,xml,json

Apache Geode 的 Spring 数据教程二十五|spring|应用程序|apache|xml|bean_网易订阅

Spring Data for Apache Geode 的LuceneIndexFactoryBeanAPI 和 SDG 的 XML 命名空间也允许 org.apache.geode.cache.lucene.LuceneSerializer 在创建LuceneIndex.将LuceneSerializer允许您配置对象将转换为Lucene的文件进行...

02-Spring源码的准备工作:Spring的简单使用和原理分析

第三,最后基于demo中的代码分析了一下XmlBeanFactory的工作原理,包括加载xml文件解析xml中的bean标签生成相应的bean,并将bean注入到Spring容器中,最后通过getBean方法从容器中获取bean来使用 从下一节开始,我们会从...

剖析 SPI 在 Spring 中的应用

从 源码 中 可以 发现 : ServiceLoader 类 本身 实现 了 Iterable 接口 并 实现 了 其中 的 iterator 方法 , iterator 方法 的 实现 中 调用 了 LazyIterator 这 个 内部 类 中 的 方法 , 解析 完 服务 提供 接口 文件 后 最终 结果 放在 了 Iterator 中 返回 , 并 不 支持 服务 提供 接口 实现 类 的 直接 访问 。 所有 服务 提供 接口 的 对应 文件 都 是 放置 在 META - INF / services / 目录 下 , final 类型 决定 了 PREFIX 目录 不 可 变更 。 所以 Java 内置 的 SPI 机制 思想 是 非常 好 的 , 但 其 内置 实现 上 的 不足 也 很 明显 。 三 、 Dubbo SPI Dubbo SPI 沿用 了 Java SPI 的 设计 思想 , 但 在 实现 上 有 了 很 大 的 改进 , 不仅 可以 直接 访问 扩展 类 , 而且 在 访问 的 灵活 性 和 扩展 的 便捷 性 都 做 了 很 大 的 提升 。 3 . 1 基本 概念 ① 扩展 点 一个 Java 接口 , 等同 于 服务 提供 接口 , 需用 @ SPI 注解 修饰 。 ② 扩展 扩展 点 的 实现 类 。 ③ 扩展 类 加载 器 : ExtensionLoader 类似 于 Java SPI 的 ServiceLoader , 主要 用来 加载 并 实例 化 扩展 类 。 一个 扩展 点 对应 一个 扩展 加载 器 。 ④ Dubbo 扩展 文件 加载 路径 Dubbo 框架 支持 从 以下 三 个 路径 来 加载 扩展 类 : META - INF / dubbo / internal META - INF / dubbo META - INF / services Dubbo 框架 针对 三 个 不同 路径 下 的 扩展 配置 文件 对应 三 个 策略 类 : DubboInternalLoadingStrategy DubboLoadingStrategy ServicesLoadingStrategy 三 个 路径 下 的 扩展 配置 文件 并 没有 特殊 之 处 , 一般 情况 下 : META - INF / dubbo 对 开发 者 开放 META - INF / dubbo / internal 用来 加载 Dubbo 内部 的 扩展 点 META - INF / services 兼容 Java SPI ⑤ 扩展 配置 文件 和 Java SPI 不同 , Dubbo 的 扩展 配置 文件 中 扩展 类 都 有 一个 名称 , 便于 在 应用 中 引用 它们 。 如 : Dubbo SPI 扩展 配置 文件 3 . 2 Dubbo SPI 先 通过 代码 来 演示 下 Dubbo SPI 的 实现 。 ① 创建 扩展 点 ( 即 服务 提供 接口 ) 扩展 点 package dubbo . spi ; import org . apache . dubbo . com mon . extension . SPI ; @ SPI / / 注解 标记 当前 接口 为 扩展 点 public interface DataBaseSPI { public void dataBaseOperation ( ) ; } ② 创建 扩展 点 实现 类 MysqlDataBaseSPIImpl 扩展 类 1 package dubbo . spi . impl ; import dubbo . spi . DataBaseSPI ; public class MysqlDataBaseSPIImpl implements DataBaseSPI { @ Override public void dataBaseOperation ( ) { System . out . println ( " Dubbo SPI Operate Mysql database ! ! ! " ) ; } } OracleDataBaseSPIImpl 扩展 类 2 package dubbo . spi . impl ; import dubbo . spi . DataBaseSPI ; public class OracleDataBaseSPIImpl implements DataBaseSPI { @ Override public void dataBaseOperation ( ) { System . out . println ( " Dubbo SPI Operate Oracle database ! ! ! " ) ; } } ③ 在 项目 META - INF / dubbo / 目录 下 创建 dubbo . spi . DataBaseSPI 文件 : dubbo . spi . DataBaseSPI PS : 文件 内容 中 , 等号 左边 为 该 扩展 类 对应 的 扩展 实例 名称 , 右边 为 扩展 类 ( 内容 格式 为 一 行 一个 扩展 类 , 多 个 扩展 类 分为 多 行 ) ④ 运行 代码 : DubboSpiTest # main ( ) package dubbo . spi ; import org . apache . dubbo . com mon . extension . ExtensionLoader ; public class DubboSpiTest { public static void main ( String args [ ] ) { / / 使用 扩展 类 加载 器 加载 指定 扩展 的 实现 ExtensionLoader < DataBaseSPI > dataBaseSpis = ExtensionLoader . getExtensionLoader ( DataBaseSPI . class ) ; / / 根据 指定 的 名称 加载 扩展 实例 ( 与 dubbo . spi . DataBaseSPI 中 一致 ) DataBaseSPI spi = dataBaseSpis . getExtension ( " mysql " ) ; spi . dataBaseOperation ( ) ; DataBaseSPI spi 2 = dataBaseSpis . getExtension ( " oracle " ) ; spi 2 . dataBaseOperation ⑤ 运行 结果 : 从 上面 的 代码 实现 直观 来 看 , Dubbo SPI 在 使用 上 和 Java SPI 比较 类似 , 但 也 有 差异 。 相同 : 扩展 点 即 服务 提供 接口 、 扩展 即 服务 提供 接口 实现 类 、 扩展 配置 文件 即 services 目录 下 的 配置 文件 三 者 相同 。 都 是 先 创建 加载 器 然后 访问 具体 的 服务 实现 类 , 包括 深 层次 的 在 初始 化 加载 器 时 都 未 实时 解析 扩展 配置 文件 来 获取 扩展 点 实现 , 而是 在 使用 时 才 正式 解析 并 获取 扩展 点 实现 ( 即 懒 加载 ) 。 不同 : 扩展 点 必须 使用 @ SPI 注解 修饰 ( 源码 中 解析 会 对此 做 校验 ) 。 Dubbo 中 扩展 配置 文件 每 个 扩展 ( 服务 提供 接口 实现 类 ) 都 指定 了 一个 名称 。 Dubbo SPI 在 获取 扩展 类 实例 时 直接 通过 扩展 配置 文件 中 指定 的 名称 获取 , 而 非 Java SPI 的 循环 遍 历 , 在 使用 上 更 灵活 。 3 . 3 源码 分析 以 上述 的 代码 实现 作为 源码 分析 入口 , 了解 下 Dubbo SPI 是 如何 实现 的 。 ExtensionLoader ① 通过 ExtensionLoader . getExtensionLoader ( Classtype ) 创建 对应 扩展 类型 的 扩展 加载 器 。 ExtensionLoader # getExtensionLoader ( ) public static < T > ExtensionLoader < T > getExtensionLoader ( Class < T > type ) { if ( type = = null ) { throw new IllegalArgumentException ( " Extension type = = null " ) ; } / / 校验 当前 类型 是否 为 接口 if ( ! type . isInterface ( ) ) { throw new IllegalArgumentException ( " Extension type ( " + type + " ) is not an interface ! " ) ; } / / 接口 上 是否 使用 了 @ SPI 注解 if ( ! withExtensionAnnotation ( type ) ) { throw new IllegalArgumentException ( " Extension type ( " + type + " ) is not an extension , because it is NOT annotated with @ " + SPI . class . getSimpleName ( ) + " ! " ) ; } / / 从 内存 中 读取 该 扩展 点 的 扩展 类 加载 器 ExtensionLoader < T > loader = ( ExtensionLoader < T > ) EXTENSION _ LOADERS . get ( type ) ; / / 内存 中 不 存在 则 直接 new 一个 扩展 if ( loader = = null ) { EXTENSION _ LOADERS . putIfAbsent ( type , new ExtensionLoader < T > ( type ) ) ; loader = ( ExtensionLoader < T > ) EXTENSION _ LOADERS . get ( type ) ; } return loader getExtensionLoader ( ) 方法 中 有 三 点 比较 重要 的 逻辑 : 判断 当前 type 类型 是否 为 接口 类型 。 当前 扩展 点 是否 使用 了 @ SPI 注解 修饰 。 EXTENSION _ LOADERS 为 ConcurrentMap 类型 的 内存 缓存 , 内存 中 存在 该 类型 的 扩展 加载 器 则 直接 使用 , 不 存在 就 new 一个 并 放 入 内存 缓存 中 。 再 看 下 new ExtensionLoader ( type ) 源码 ExtensionLoader # ExtensionLoader ( ) / / 私有 构造 器 private ExtensionLoader ( Class < ? > type ) { this . type = type ; / / 创建 ExtensionFactory 自 适应 扩展 objectFactory = ( type = = ExtensionFactory . class ? null : ExtensionLoader . getExtensionLoader ( ExtensionFactory . class ) . getAdaptiveExtension 重点 : 构造 方法 为 私有 类型 , 即 外部 无法 直接 使用 构造 方法 创建 ExtensionLoader 实例 。 每次 初始 化 ExtensionLoader 实例 都会 初始 化 type 和 objectFactory , type 为 扩展 点 类型 ; objectFactory 为 ExtensionFactory 类型 。 ② 使用 getExtension ( ) 获取 指定 名称 的 扩展 类 实例 getExtension 为 重载 方法 , 分别 为 getExtension ( String name ) 和 getExtension ( String name , boolean wrap ) , getExtension ( String name ) 方法 最终 调用 的 还是 getExtension ( String name , boolean wrap ) 方法 。 ExtensionLoader # getExtension ( ) public T getExtension ( String name ) { / / 调用 两 个 参数 的 getExtension 方法 , 默认 true 表示 需要 对 扩展 实例 做 包装 return getExtension ( name , true ) ; } public T getExtension ( String name , boolean wrap ) { if ( StringUtils . isEmpty ( name ) ) { throw new IllegalArgumentException ( " Extension name = = null " ) ; } if ( " true " . equals ( name ) ) { return getDefaultExtension ( ) ; } / / 获取 Holder 实例 , 先 从 ConcurrentMap 类型 的 内存 缓存 中 取 , 没 值 会 new 一个 并 存放 到 内存 缓存 中 / / Holder 用来 存放 一个 类型 的 值 , 这里 用于 存放 扩展 实例 final Holder < Object > holder = getOrCreateHolder ( name ) ; / / 从 Holder 读取 该 name 对应 的 实例 Object instance = holder . get ( ) ; if ( instance = = null ) { / / 同步 控制 synchronized ( holder ) { instance = holder . get ( ) ; / / double check if ( instance = = null ) { / / 不 存在 扩展 实例 则 解析 扩展 配置 文件 , 实时 创建 instance = createExtension ( name , wrap ) ; holder . set ( instance ) ; } } } return ( T ) instance ; } Holder 类 : 这里 用来 存放 指定 扩展 实例 ③ 使用 createExtension ( ) 创建 扩展 实例 ExtensionLoader # createExtension ( ) / / 部分 createExtension 代码 private T createExtension ( String name , boolean wrap ) { / / 先 调用 getExtensionClasses ( ) 解析 扩展 配置 文件 , 并 生成 内存 缓存 , / / 然后 根据 扩展 实例 名称 获取 对应 的 扩展 类 Class < ? > clazz = getExtensionClasses ( ) . get ( name ) ; if ( clazz = = null ) { throw findException ( name ) ; } try { / / 根据 扩展 类 生成 实例 并 对 实例 做 包装 ( 主要 是 进行 依赖 注入 和 初始 化 ) / / 优先 从 内存 中 获取 该 class 类型 的 实例 T instance = ( T ) EXTENSION _ INSTANCES . get ( clazz ) ; if ( instance = = null ) { / / 内存 中 不 存在 则 直接 初始 化 然后 放 到 内存 中 EXTENSION _ INSTANCES . putIfAbsent ( clazz , clazz . newInstance ( ) ) ; instance = ( T ) EXTENSION _ INSTANCES . get ( clazz ) ; } / / 主要 是 注入 instance 中 的 依赖 injectExtension createExtension ( ) 方法 : 创建 扩展 实例 , 方法 中 EXTENSION _ INSTANCES 为 ConcurrentMap 类型 的 内存 缓存 , 先 从 内存 中 取 , 内存 中 不 存在 重新 创建 ; 其中 一个 核心 方法 是 getExtensionClasses ( ) : ExtensionLoader # getExtensionClasses ( ) private Map < String , Class < ? > > getExtensionClasses ( ) { / / 优先 从 内存 缓存 中 读 Map < String , Class < ? > > classes = cachedClasses . get ( ) ; if ( classes = = null ) { / / 采用 同步 手段 解析 配置 文件 synchronized ( cachedClasses ) { / / double check classes = cachedClasses . get ( ) ; if ( classes = = null ) { / / 正式 开始 解析 配置 文件 classes = loadExtensionClasses ( ) ; cachedClasses . set ( classes ) ; } } } return classes ; } cachedClasses 为 Holder < map < string , class > > 类型 的 内存 缓存 , getExtensionClasses 中 会 优先 读 内存 缓存 , 内存 中 不 存在 则 采用 同步 的 方式 解析 配置 文件 , 最终 在 loadExtensionClasses 方法 中 解析 配置 文件 , 完成 从 扩展 配置 文件 中 读出 扩展 类 : ExtensionLoader # loadExtensionClasses ( ) / / 在 getExtensionClasses 方法 中 是 以 同步 的 方式 调用 , 是 线程 安全 private Map < String , Class < ? > > loadExtensionClasses ( ) { / / 缓存 默认 扩展 名称 cacheDefaultExtensionName ( ) ; Map < String , Class < ? > > extensionClasses = new HashMap < > ( ) ; / / strategies 策略 类 集合 , 分别 对应 dubbo 的 三 个 配置 文件 目录 for ( LoadingStrategy strategy : strategies ) { loadDirectory ( extensionClasses , strategy . directory ( ) , type . getName ( ) , strategy . preferExtensionClassLoader ( ) , strategy . overridden ( ) , strategy . excludedPackages ( ) ) ; loadDirectory ( extensionClasses , strategy . directory ( ) , type . getName ( ) . replace ( " org . apache " , " com . alibaba " ) , strategy . preferExtensionClassLoader ( ) , strategy . overridden ( ) , strategy . excludedPackages ( ) ) ; } return extensionClasses 源码 中 的 strategies 即 static volatile LoadingStrategy [ ] strategies 数 组 , 通过 Java SPI 从 META - INF / services / 目录 下 加载 配置 文件 完成 初始 化 , 默认 包含 三 个 类 : DubboInternalLoadingStrategy DubboLoadingStrategy ServicesLoadingStrategy 分别 对应 dubbo 的 三 个 目录 : META - INF / dubbo / internal META - INF / dubbo META - INF / services 上述 的 源码 分析 只是 对 Dubbo SPI 做 了 简要 的 介绍 , Dubbo 中 对 SPI 的 应用 很 广泛 , 如 : 序列 化 组件 、 负载 均衡 等 都 应用 了 SPI 技术 , 还有 很 多 SPI 功能 未 做 分析 , 比如 : 自 适应 扩展 、 Activate 活性 扩展 等 等 , 感 兴趣 的 同学 可以 更 深入 的 研究 。 四 、 Spring SPI Spring SPI 沿用 了 Java SPI 的 设计 思想 , 但 在 实现 上 和 Java SPI 及 Dubbo SPI 也 存在 差异 , Spring 通过 spring . handlers 和 spring . factories 两 种 方式 实现 SPI 机制 , 可以 在 不 修改 Spring 源码 的 前提 下 , 做到 对 Spring 框架 的 扩展 开发 。 4 . 1 基本 概念 DefaultNamespaceHandlerResolver 类似 于 Java SPI 的 ServiceLoader , 负责 解析 spring . handlers 配置 文件 , 生成 namespaceUri 和 NamespaceHandler 名称 的 映射 , 并 实例 化 NamespaceHandler 。 spring . handlers 自 定义 标签 配置 文件 ; Spring 在 2 . 0 时 便 引入 了 spring . handlers , 通过 配置 spring . handlers 文件 实现 自 定义 标签 并 使用 自 定义 标签 解析 类 进行 解析 实现 动态 扩 , 内容 配置 如 : / / 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 spring . handlers 实现 的 SPI 是 以 namespaceUri 作为 key , NamespaceHandler 作为 value , 建立 映射 关系 , 在 解析 标签 时 通过 namespaceUri 获取 相应 的 NamespaceHandler 来 解析 SpringFactoriesLoader 类似 于 Java SPI 的 ServiceLoader , 负责 解析 spring . factories , 并 将 指定 接口 的 所有 实现 类 实例 化 后 返回 。 spring . factories Spring 在 3 . 2 时 引入 spring . factories , 加强 版 的 SPI 配置 文件 , 为 Spring 的 SPI 机制 的 实现 提供 支撑 , 内容 配置 如 : PropertySource Loaders org . springframework . boot . env . PropertySourceLoader = \ org . springframework . boot . env . PropertiesPropertySourceLoader , \ org . springframework . boot . env . YamlPropertySourceLoader # Run Listeners org . springframework . boot . SpringApplicationRunListener = \ org . springframework . boot . context . event . EventPublishingRunListener spring . factories 实现 的 SPI 是 以 接口 的 全 限定 名 作为 key , 接口 实现 类 作为 value , 多 个 实现 类 用 逗号 隔 开 , 最终 返回 的 结果 是 该 接口 所有 实现 类 的 实例 集合 加载 路径 Java SPI 从 / META - INF / services 目录 加载 服务 提供 接口 配置 , 而 Spring 默认 从 META - INF / spring . handlers 和 META - INF / spring . factories 目录 加载 配置 , 其中 META - INF / spring . handlers 的 路径 可以 通过 创建 实例 时 重新 指定 , 而 META - INF / spring . factories 固定 不 可 变 。 4 . 2 spring . handlers 首先 通过 代码 初步 介绍 下 spring . handlers 实现 。 4 . 2 . 1 spring . handlers SPI ① 创建 NameSpaceHandler MysqlDataBaseHandler package spring . spi . handlers ; import org . springframework . beans . factory . config . BeanDefinition ; import org . springframework . beans . factory . xml . NamespaceHandlerSupport ; import org . springframework . beans . factory . xml . ParserContext ; import org . w3 c . dom . Element ; / / 继承 抽象 类 public class MysqlDataBaseHandler extends NamespaceHandlerSupport { @ Override public void init ( ) { } @ Override public BeanDefinition parse ( Element element , ParserContext parserContext ) { System . out . println ( " MysqlDataBaseHandler ! ! ! " ) ; return null ; } } OracleDataBaseHandler package spring . spi . handlers ; import org . springframework . beans . factory . config . BeanDefinition ; import org . springframework . beans . factory . xml . NamespaceHandlerSupport ; import org . springframework . beans . factory . xml . ParserContext ; import org . w3 c . dom . Element ; public class OracleDataBaseHandler extends NamespaceHandlerSupport { @ Override public void init ( ) { } @ Override public BeanDefinition parse ( Element element , ParserContext parserContext ) { System . out . println ( " OracleDataBaseHandler ! ! ! " ) ; return null ; } } ② 在 项目 META - INF / 目录 下 创建 spring . handlers 文件 : 文件 内容 : spring . handlers # 一个 namespaceUri 对应 一个 handler http \ : / / www . mysql . org / schema / mysql = spring . spi . handlers . MysqlDataBaseHandler http \ : / / www . oracle . org / schema / oracle = spring . spi . handlers . OracleDataBaseHandler ③ 运行 代码 : SpringSpiTest # main ( ) package spring . spi ; import org . springframework . beans . factory . xml . DefaultNamespaceHandlerResolver ; import org . springframework . beans . factory . xml . NamespaceHandler ; public class SpringSpiTest { public static void main ( String args [ ] ) { / / spring 中 提供 的 默认 namespace URI 解析 器 DefaultNamespaceHandlerResolver resolver = new DefaultNamespaceHandlerResolver ( ) ; / / 此处 假设 nameSpaceUri 已 从 xml 文件 中 解析 出来 , 正常 流程 是 在 项目 启动 的 时候 会 解析 xml 文件 , 获取 到 对应 的 自 定义 标签 / / 然后 根据 自 定义 标签 取得 对应 的 nameSpaceUri String mysqlNameSpaceUri = " http : / / www . mysql . org / schema / mysql " ; NamespaceHandler handler = resolver . resolve ( mysqlNameSpaceUri ) ; / / 验证 自 定义 NamespaceHandler , 这里 参数 传 null , 实际 使用 中传 具体 的 Element handler . parse ( null , null ) ; String oracleNameSpaceUri = " http : / / www . oracle . org / schema / oracle " ; handler = resolver . resolve ( oracleNameSpaceUri ) ; handler . parse ( null , null ) ; } } ④ 运行 结果 : 上述 代码 通过 解析 spring . handlers 实现 对 自 定义 标签 的 动态 解析 , 以 NameSpaceURI 作为 key 获取 具体 的 NameSpaceHandler 实现 类 , 这里 有 别 于 Java SPI , 其中 : DefaultNamespaceHandlerResolver 是 NamespaceHandlerResolver 接口 的 默认 实现 类 , 用于 解析 自 定义 标签 。 DefaultNamespaceHandlerResolver . resolve ( String namespaceUri ) 方法 以 namespaceUri 作为 参数 , 默认 加载 各 jar 包 中 的 META - INF / spring . handlers 配置 文件 , 通过 解析 spring . handlers 文件 建立 NameSpaceURI 和 NameSpaceHandler 的 映射 。 加载 配置 文件 的 默认 路径 是 META - INF / spring . handlers , 但 可以 使用 DefaultNamespaceHandlerResolver ( ClassLoader , String ) 构造 方法 修改 , DefaultNamespaceHandlerResolver 有 多 个 重载 方法 。 DefaultNamespaceHandlerResolver . resolve ( String namespaceUri ) 方法 主要 被 BeanDefinitionParserDelegate 的 parseCustomElement ( ) 和 decorateIfRequired ( ) 方法 中 调用 , 所以 spring . handlers SPI 机制 主要 用 在 bean 的 扫描 和 解析 过程 中 。 4 . 2 . 2 源码 分析 下面 从 上述 代码 开始 深入 源码 了解 spring handlers 方式 实现 的 SPI 是 如何 工作 的 。 DefaultNamespaceHandlerResolver ① DefaultNamespaceHandlerResolver . resolve ( ) 方法 本身 是 根据 namespaceUri 获取 对应 的 namespaceHandler 对 标签 进行 解析 , 核心 源码 : DefaultNamespaceHandlerResolver # resolve ( ) public NamespaceHandler resolve ( String namespaceUri ) { / / 1 、 核心 逻辑 之一 : 获取 namespaceUri 和 namespaceHandler 映射 关系 Map < String , Object > handlerMappings = getHandlerMappings ( ) ; / / 根据 namespaceUri 参数 取 对应 的 namespaceHandler 全 限定 类 名 or NamespaceHandler 实例 Object handlerOrClassName = handlerMappings . get ( namespaceUri ) ; if ( handlerOrClassName = = null ) { return null ; } / / 2 、 handlerOrClassName 是 已 初始 化 过 的 实例 则 直接 返回 else if ( handlerOrClassName instanceof NamespaceHandler ) { return ( NamespaceHandler ) handlerOrClassName ; } else { String className = ( String ) handlerOrClassName ; try { / / / 3 、 使用 反射 根据 namespaceHandler 全 限定 类 名 加载 实现 类 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 " ) ; } / / 3 . 1 、 初始 化 namespaceHandler 实例 NamespaceHandler namespaceHandler = ( NamespaceHandler ) BeanUtils . instantiateClass ( handlerClass ) ; / / 3 . 2 、 初始 化 , 不同 的 namespaceHandler 实现 类 初始 化 方法 逻辑 有 差异 namespaceHandler . init ( ) ; / / 4 、 将 初始 化 好 的 实例 放 入 内存 缓存 中 , 下次 解析 到 相同 namespaceUri 标签 时 直接 返回 , 避免 再次 初始 化 handlerMappings . put ( namespaceUri , 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 " 第 1 步 : 源码 中 getHandlerMappings ( ) 是 比较 核心 的 一个 方法 , 通过 懒 加载 的 方式 解析 spring . handlers 并 返回 namespaceUri 和 NamespaceHandler 的 映射 关系 。 第 2 步 : 根据 namespaceUri 返回 对应 的 NamespaceHandler 全 限定 名 或者 具体 的 实例 ( 是 名称 还是 实例 取 决 于 是否 被 初始 化 过 , 若是 初始 化 过 的 实例 会 直接 返回 ) 第 3 步 : 是 NamespaceHandler 实现 类 的 全 限定 名 , 通过 上述 源码 中 的 第 3 步 , 使用 反射 进行 初始 化 。 第 4 步 : 将 初始 化 后 的 实例 放 到 handlerMappings 内存 缓存 中 , 这 也 是 第 2 步 为 什么 可能 是 NamespaceHandler 类型 的 原因 。 看 完 resolve 方法 的 源码 , 再 看 下 resolve 方法 在 Spring 中 调用 场景 , 大致 可以 了解 spring . handlers 的 使用 场景 : 可以 看到 resolve ( ) 主要 用 在 标签 解析 过程 中 , 主要 被 在 BeanDefinitionParserDelegate 的 parseCustomElement 和 decorateIfRequired 方法 中 调用 。 ② resolve ( ) 源码 中 核心 逻辑 之一 便 是 调用 的 getHandlerMappings ( ) , 在 getHandlerMappings ( ) 中 实现 对 各个 jar 包 中 的 META - INF / spring . handlers 文件 的 解析 , 如 : DefaultNamespaceHandlerResolver # getHandlerMappings ( ) getHandlerMappings ( ) { Map < String , Object > handlerMappings = this . handlerMappings ; / / 使用 线程 安全 的 解析 逻辑 , 避免 在 并发 场景 下 重复 的 解析 , 没 必要 重复 解析 / / 这里 在 同步 代码 块 的 内外 对 handlerMappings = = null 作 两 次 判断 很 有 必要 , 采用 懒汉 式 初始 化 if ( handlerMappings = = null ) { synchronized ( this ) { handlerMappings = this . handlerMappings ; / / duble check if ( handlerMappings = = null ) { if ( logger . isDebugEnabled ( ) ) { logger . debug ( " Loading NamespaceHandler mappings from [ " + this . handlerMappingsLocation + " ] " ) ; } try { / / 加载 handlerMappingsLocation 目录 文件 , handlerMappingsLocation 路径 值 可 变 , 默认 是 META - INF / spring . handlers Properties mappings = PropertiesLoaderUtils . loadAllProperties ( this . handlerMappingsLocation , this . classLoader ) ; if ( logger . isDebugEnabled ( ) ) { logger . debug ( " Loaded NamespaceHandler mappings : " + mappings ) ; } / / 初始 化 内存 缓存 handlerMappings = new ConcurrentHashMap < String , Object > ( mappings . size ( ) ) ; / / 将 加载 到 的 属性 合并 到 handlerMappings 中 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 源码 中 this . handlerMappings 是 一个 Map 类型 的 内存 缓存 , 存放 解析 到 的 namespaceUri 以及 NameSpaceHandler 实例 。 getHandlerMappings ( ) 方法 体 中 的 实现 使用 了 线程 安全 方式 , 增加 了 同步 逻辑 。 通过 阅读 源码 可以 了解 到 Spring 基于 spring . handlers 实现 SPI 逻辑 相对 比较 简单 , 但 应用 却 比较 灵活 , 对 自 定义 标签 的 支持 很 方便 , 在 不 修改 Spring 源码 的 前提 下 轻松 实现 接 入 , 如 Dubbo 中 定义 的 各种 Dubbo 标签 便 是 很 好 的 利用 了 spring . handlers 。 Spring 提供 如此 灵活 的 功能 , 那 是 如何 应用 的 呢 ? 下面 简单 了解 下 parseCustomElement ( ) 。 BeanDefinitionParserDelegate . parseCustomElement ( ) resolve 作为 工具 类型 的 方法 , 被 使用 的 地方 比较 多 , 这里 仅 简单 介绍 在 BeanDefinitionParserDelegate . parseCustomElement ( ) 中 的 应用 。 BeanDefinitionParserDelegate # parseCustomElement ( ) public BeanDefinition parseCustomElement ( Element ele , BeanDefinition containingBd ) { / / 获取 标签 的 namespaceUri String namespaceUri = getNamespaceURI ( ele ) ; / / 首先 获得 DefaultNamespaceHandlerResolver 实例 在 再 以 namespaceUri 作为 参数 调用 resolve 方法 解析 取得 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 中 的 parse 方法 开始 解析 标签 return handler . parse ( ele , new ParserContext ( this . readerContext , this , containingBd ) ) ; } parseCustomElement 作为 解析 标签 的 中间 方法 , 再 看 下 parseCustomElement 的 调用 情况 : 在 parseBeanDefinitions ( ) 中 被 调用 , 再 看 下 parseBeanDefinitions 的 源码 DefaultBeanDefinitionDocumentReader # parseBeanDefinitions ( ) parseBeanDefinitions ( Element root , BeanDefinitionParserDelegate delegate ) { / / spring 内部 定义 的 标签 为 默认 标签 , 即 非 spring 内部 定义 的 标签 都 不是 默认 的 namespace 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 ; / / root 子 标签 也 做 此 判断 if ( delegate . isDefaultNamespace ( ele ) ) { parseDefaultElement ( ele , delegate ) ; } else { / / 子 标签 非 spring 默认 标签 ( 即 自 定义 标签 ) 也 走 parseCustomElement 来 解析 delegate . parseCustomElement ( ele ) ; } } } } else { / / 非 spring 的 默认 标签 ( 即 自 定义 的 标签 ) 走 parseCustomElement 来 解析 delegate . parseCustomElement 到 此 就 很 清晰 了 , 调用 前 判断 是否 为 Spring 默认 标签 , 不是 默认 标签 调用 parseCustomElement 来 解析 , 最后 调用 resolve 方法 。 4 . 2 . 3 小节 Spring 自 2 . 0 引入 spring . handlers 以后 , 为 Spring 的 动态 扩展 提供 更 多 的 入口 和 手段 , 为 自 定义 标签 的 实现 提供 了 强力 支撑 。 很 多 文章 在 介绍 Spring SPI 时 都 重点 介绍 spring . factories 实现 , 很 少 提及 很 早就 引入 的 spring . handlers , 但 通过 个人 的 分析 及 与 Java SPI 的 对比 , spring . handlers 也 是 一 种 SPI 的 实现 , 只是 基于 xml 实现 。 相比 于 Java SPI , 基于 spring . handlers 实现 的 SPI 更加 的 灵活 , 无需 遍 历 , 直接 映射 , 更 类似 于 Dubbo SPI 的 实现 思想 , 每 个 类 指定 一个 名称 ( 只是 spring . handlers 中 是 以 namespaceUri 作为 key , Dubbo 配置 中 是 指定 的 名称 作为 key ) 。 4 . 3 spring . factories 同样 先 以 测试 代码 来 介绍 spring . factories 实现 SPI 的 逻辑 。 4 . 3 . 1 spring . factories SPI ① 创建 DataBaseSPI 接口 接口 ② 创建 DataBaseSPI 接口 的 实现 类 MysqlDataBaseImpl # 实现 类 1 package spring . spi . factories . impl ; import spring . spi . factories . DataBaseSPI ; public class MysqlDataBaseImpl implements DataBaseSPI { @ Override public void dataBaseOperation ( ) { System . out . println ( " Mysql database test ! ! ! ! " ) ; } } MysqlDataBaseImpl # 实现 类 2 package spring . spi . factories . impl ; import spring . spi . factories . DataBaseSPI ; public class OracleDataBaseImpl implements DataBaseSPI { @ Override public void dataBaseOperation ( ) { System . out . println ( " Oracle database test ! ! ! ! " ) ; } } ③ 在 项目 META - INF / 目录 下 创建 spring . factories 文件 : 文件 内容 spring . factories key 是 接口 的 全 限定 名 , value 是 接口 的 实现 类 spring . spi . factories . DataBaseSPI = spring . spi . factories . impl . MysqlDataBaseImpl , spring . spi . factories . impl . OracleDataBaseImpl ④ 运行 代码 SpringSpiTest # main ( ) package spring . spi . factories ; import java . util . List ; import org . springframework . core . io . support . SpringFactoriesLoader ; public class SpringSpiTest { public static void main ( String args [ ] ) { / / 调用 SpringFactoriesLoader . loadFactories 方法 加载 DataBaseSPI 接口 所有 实现 类 的 实例 List < DataBaseSPI > spis = SpringFactoriesLoader . loadFactories ( DataBaseSPI . class , Thread . currentThread ( ) . getContextClassLoader ( ) ) ; / / 遍 历 DataBaseSPI 接口 实现 类 实例 for ( DataBaseSPI spi : spis ) { spi . dataBaseOperation ⑤ 运行 结果 从 上述 的 示例 代码 中 可以 看出 spring . facotries 方式 实现 的 SPI 和 Java SPI 很 相似 , 都 是 先 获取 指定 接口 类型 的 实现 类 , 然后 遍 历 访问 所有 的 实现 。 但 也 存在 一定 的 差异 : ( 1 ) 配置 上 : Java SPI 是 一个 服务 提供 接口 对应 一个 配置 文件 , 配置 文件 中 存放 当前 接口 的 所有 实现 类 , 多 个 服务 提供 接口 对应 多 个 配置 文件 , 所有 配置 都 在 services 目录 下 ; Spring factories SPI 是 一个 spring . factories 配置 文件 存放 多 个 接口 及 对应 的 实现 类 , 以 接口 全 限定 名 作为 key , 实现 类 作为 value 来 配置 , 多 个 实现 类 用 逗号 隔 开 , 仅 spring . factories 一个 配置 文件 。 ( 2 ) 实现 上 Java SPI 使用 了 懒 加载 模式 , 即 在 调用 ServiceLoader . load ( ) 时 仅 是 返回 了 ServiceLoader 实例 , 尚未 解析 接口 对应 的 配置 文件 , 在 使用 时 即 循环 遍 历时 才 正式 解析 返回 服务 提供 接口 的 实现 类 实例 ; Spring factories SPI 在 调用 SpringFactoriesLoader . loadFactories ( ) 时 便 已 解析 spring . facotries 文件 返回 接口 实现 类 的 实例 ( 实现 细节 在 源码 分析 中 详解 ) 。 4 . 3 . 2 源码 分析 我们 还是 从 测试 代码 开始 , 了解 下 spring . factories 的 SPI 实现 源码 , 细 品 spring . factories 的 实现 方式 。 SpringFactoriesLoader 测试 代码 入口 直接 调用 SpringFactoriesLoader . loadFactories ( ) 静态 方法 开始 解析 spring . factories 文件 , 并 返回 方法 参数 中 指定 的 接口 类型 , 如 测试 代码 里 的 DataBaseSPI 接口 的 实现 类 实例 。 SpringFactoriesLoader # loadFactories ( ) public static < T > List < T > loadFactories ( Class < T > factoryClass , ClassLoader classLoader ) { Assert . notNull ( factoryClass , " ' factoryClass ' must not be null " ) ; ClassLoader classLoaderToUse = classLoader ; / / 1 . 确定 类 加载 器 if ( classLoaderToUse = = null ) { classLoaderToUse = SpringFactoriesLoader . class . getClassLoader ( ) ; } / / 2 . 核心 逻辑 之一 : 解析 各 jar 包 中 META - INF / spring . factories 文件 中 factoryClass 的 实现 类 全 限定 名 List < String > factoryNames = loadFactoryNames ( factoryClass , classLoaderToUse ) ; if ( logger . isTraceEnabled ( ) ) { logger . trace ( " Loaded [ " + factoryClass . getName ( ) + " ] names : " + factoryNames ) ; } List < T > result = new ArrayList < T > ( factoryNames . size ( ) ) ; / / 3 . 遍 历 实现 类 的 全 限定 名 并 进行 实例 化 for ( String factoryName : factoryNames ) { result . add ( instantiateFactory ( factoryName , factoryClass , classLoaderToUse ) ) ; } / / 排序 AnnotationAwareOrderComparator . sort ( result ) ; / / 4 . 返回 实例 化 后 的 结果 集 return result 源码 中 loadFactoryNames ( ) 是 另外 一个 比较 核心 的 方法 , 解析 spring . factories 文件 中 指定 接口 的 实现 类 的 全 限定 名 , 实现 逻辑 见 后续 的 源码 。 经过 源码 中 第 2 步 解析 得到 实现 类 的 全 限定 名 后 , 在 第 3 步 通过 instantiateFactory ( ) 方法 逐个 实例 化 实现 类 。 再 看 loadFactoryNames ( ) 源码 是 如何 解析 得到 实现 类 全 限定 名 的 : SpringFactoriesLoader # loadFactoryNames ( ) public static List < String > loadFactoryNames ( Class < ? > factoryClass , ClassLoader classLoader ) { / / 1 . 接口 全 限定 名 String factoryClassName = factoryClass . getName ( ) ; try { / / 2 . 加载 META - INF / spring . factories 文件 路径 ( 分布 在 各个 不同 jar 包 里 , 所以 这里 会 是 多 个 文件 路径 , 枚举 返回 ) Enumeration < URL > urls = ( classLoader ! = null ? classLoader . getResources ( FACTORIES _ RESOURCE _ LOCATION ) : ClassLoader . getSystemResources ( FACTORIES _ RESOURCE _ LOCATION ) ) ; List < String > result = new ArrayList < String > ( ) ; / / 3 . 遍 历 枚举 集合 , 逐个 解析 spring . factories 文件 while ( urls . hasMoreElements ( ) ) { URL url = urls . nextElement ( ) ; Properties properties = PropertiesLoaderUtils . loadProperties ( new UrlResource ( url ) ) ; String propertyValue = properties . getProperty ( factoryClassName ) ; / / 4 . spring . factories 文件 中 一个 接口 的 实现 类 有 多 个 时 会 用 逗号 隔 开 , 这里 拆开 获取 实现 类 全 限定 名 for ( String factoryName : StringUtils . commaDelimitedListToStringArray ( propertyValue ) ) { result . add ( factoryName . trim ( ) ) ; } } return result ; } catch ( IOException ex ) { throw new IllegalArgumentException ( " Unable to load factories from location [ " + FACTORIES _ RESOURCE _ LOCATION 源码 中 第 2 步 获取 所有 jar 包 中 META - INF / spring . factories 文件 路径 , 以 枚举 值 返回 。 源码 中 第 3 步 开始 遍 历 spring . factories 文件 路径 , 逐个 加载 解析 , 整合 factoryClass 类型 的 实现 类 名称 。 获取 到 实现 类 的 全 限定 名 集合 后 , 便 根据 实现 类 的 名称 逐个 实例 化 , 继续 看 下 instantiateFactory ( ) 方法 的 源码 : SpringFactoriesLoader # instantiateFactory ( ) private static < T > T instantiateFactory ( String instanceClassName , Class < T > factoryClass , ClassLoader classLoader ) { try { / / 1 . 使用 classLoader 类 加载 器 加载 instanceClassName 类 Class < ? > instanceClass = ClassUtils . forName ( instanceClassName , classLoader ) ; if ( ! factoryClass . isAssignableFrom ( instanceClass ) ) { throw new IllegalArgumentException ( " Class [ " + instanceClassName + " ] is not assignable to [ " + factoryClass . getName ( ) + " ] " ) ; } / / 2 . instanceClassName 类 中 的 构造 方法 Constructor < ? > constructor = instanceClass . getDeclaredConstructor ( ) ; ReflectionUtils . makeAccessible ( constructor ) ; / / 3 . 实例 化 return ( T ) constructor . newInstance ( ) ; } catch ( Throwable ex ) { throw new IllegalArgumentException ( " Unable to instantiate factory class : " 实例 化 方法 是 私有 型 ( private ) 静态 方法 , 这 个 有 别 于 loadFactories 和 loadFactoryNames 。 实例 化 逻辑 整体 使用 了 反射 实现 , 比较 通用 的 实现 方式 。 通过 对 源码 的 分析 , Spring factories 方式 实现 的 SPI 逻辑 不是 很 复杂 , 整体 上 的 实现 容易 理解 。 Spring 在 3 . 2 便 已 引入 spring . factories , 那 spring . factories 在 Spring 框架 中 又 是 如何 使用 的 呢 ? 先 看 下 loadFactories 方法 的 调用 情况 : 从 调用 情况 看 Spring 自 3 . 2 引入 spring . factories SPI 后 并 没有 真正 的 利用 起来 , 使用 的 地方 比较 少 , 然而 真正 把 spring . factories 发扬光大 的 , 是 在 Spring Boot 中 , 简单 了解 下 SpringBoot 中 的 调用 。 getSpringFactoriesInstances ( ) getSpringFactoriesInstances ( ) 并 不是 Spring 框架 中 的 方法 , 而是 SpringBoot 中 SpringApplication 类 里 定义 的 私有 型 ( private ) 方法 , 很 多 地方 都 有 调用 , 源码 如下 : SpringApplication # getSpringFactoriesInstance ( ) / / 单个 参数 getSpringFactoriesInstances 方法 private < T > Collection < T > getSpringFactoriesInstances ( Class < T > type ) { / / 默认 调用 多 参 的 重载 方法 return getSpringFactoriesInstances ( type , new Class < ? > [ ] { } ) ; } / / 多 个 参数 的 getSpringFactoriesInstances 方法 private < T > Collection < T > getSpringFactoriesInstances ( Class < T > type , Class < ? > [ ] parameterTypes , Object . . . args ) { ClassLoader classLoader = getClassLoader ( ) ; / / 调用 SpringFactoriesLoader 中 的 loadFactoryNames 方法 加载 接口 实现 类 的 全 限定 名 Set < String > names = new LinkedHashSet < > ( SpringFactoriesLoader . loadFactoryNames ( type , classLoader ) ) ; / / 实例 化 List < T > instances = createSpringFactoriesInstances ( type , parameterTypes , classLoader , args , names ) ; AnnotationAwareOrderComparator . sort ( instances ) ; return instances 在 getSpringFactoriesInstances ( ) 中 调用 了 SpringFactoriesLoader . loadFactoryNames ( ) 来 加载 接口 实现 类 的 全 限定 名 集合 , 然后 进行 初始 化 。 SpringBoot 中 除了 getSpringFactoriesInstances ( ) 方法 有 调用 , 在 其他 逻辑 中 也 广泛 运用 着 SpringFactoriesLoader 中 的 方法 来 实现 动态 扩展 , 这里 就 不 在 一一 列举 了 , 有 兴趣 的 同学 可以 自己 去 发掘 。 4 . 3 . 3 小节 Spring 框架 在 3 . 2 引入 spring . factories 后 并 没有 有效 的 利用 起来 , 但 给 框架 的 使用 者 提供 了 又 一个 动态 扩展 的 能力 和 入口 , 为 开发 人员 提供 了 很 大 的 自由 发挥 的 空间 , 尤其 是 在 SpringBoot 中 广泛 运用 就 足以 证明 spring . factories 的 地位 。 spring . factories 引入 在 提升 Spring 框架 能力 的 同时 也 暴露 出 其中 的 不足 : 首先 , spring . factories 的 实现 类似 Java SPI , 在 加载 到 服务 提供 接口 的 实现 类 后 需要 循环 遍 历 才能 访问 , 不是 很 方便 。 其次 , Spring 在 5 . 0 . x 版本 以前 SpringFactoriesLoader 类 定义 为 抽象 类 , 但 在 5 . 1 . 0 版本 之后 Sping 官方 将 SpringFactoriesLoader 改 为 final 类 , 类型 变化 对 前后 版本 的 兼容 不 友好 。 五 、 应用 实践 介绍 完 Spring 中 SPI 机制 相关 的 核心 源码 , 再 来 看 看 项目 中 自己 开发 的 轻 量 版 的 分 库 分表 SDK 是 如何 利用 Spring 的 SPI 机制 实现 分 库 分表 策略 动态 扩展 的 。 基于 项目 的 特殊 性 并 没有 使用 目前 行业 中 成熟 的 分 库 分表 组件 , 而是 基于 Mybatis 的 插件 原理 自己 开发 的 一 套 轻 量 版 分 库 分表 组件 。 为 满足 不同 场景 分 库 分表 要求 , 将 其中 分 库 分表 的 相关 逻辑 以 策略 模式 进行 抽取 分离 , 每 种 分 库 分表 的 实现 对应 一 条 策略 , 支持 使用 方 对 分 库 分表 策略 的 动态 扩展 , 而 这里 的 动态 扩展 就 利用 了 spring . factories 。 首先 给 出 轻 量 版 分 库 分表 组件 流程 图 , 然后 我们 针对 流程 图 中 使用 到 Spring SPI 的 地方 进行 详细 分析 。 说明 : 上述 流程 图 中 项目 启动 过程 中 生成 数据 源 和 分 库 分表 策略 的 初始 化 , 策略 初始 化 完成 后 缓存 到 内存 中 。 发起 数据 库 操作 指令 时 , 解析 是否 需要 分 库 分表 ( 流程 中 只 给 出 了 需要 分 库 分表 的 流程 ) , 需要 则 通过 提取 到 的 策略 key 获取 对应 的 分 库 分表 策略 并 进行 分 库 分表 , 完成 数据 库 操作 。 通过 上述 的 流程 图 可以 看到 , 分 库 分表 SDK 通过 spring . factories 支持 动态 加载 分 库 分表 策略 以 兼容 不同 项目 的 不同 使用 场景 。 其中 分 库 分表 部分 的 策略 类 图 : 其中 : ShardingStrategy 和 DBTableShardingStrategy 为 接口 ; BaseShardingStrategy 为 默认 实现 类 ; DefaultStrategy 和 CountryDbSwitchStrategy 为 SDK 中 基于 不同 场景 默认 实现 的 分 库 分表 策略 。 在 项目 实际 使用 时 , 动态 扩展 的 分 库 分表 策略 只 需要 继承 BaseShardingStrategy 即可 , SDK 中 初始 化 分 库 分表 策略 时 通过 SpringFactoriesLoader . loadFactories ( ) 实现 动态 加载 。 六 、 总结 SPI 技术 将 服务 接口 与 服务 实现 分离 以 达到 解耦 , 极 大 的 提升 程序 的 可 扩展 性 。 本文 重点 介绍 了 Java 内置 SPI 和 Dubbo SPI 以及 Spring SPI 三 者 的 原理 和 相关 源码 ; 首先 演示 了 三 种 SPI 技术 的 实现 , 然后 通过 演示 代码 深入 阅读 了 三 种 SPI 的 实现 源码 ; 其中 重点 介绍 了 Spring SPI 的 两 种 实现 方式 : spring . handlers 和 spring . factories , 以及 使用 spring . factories 实现 的 分 库 分表 策略 加载 。 希望 通过 阅读 本文 可以 让 读者 对 SPI 有 更 深入 的 了解 。

优秀!阿里甩出内部Spring优化性能全解笔记,遭网友疯狂转载|spring|sql|service|bean|xml_

Spring 创建对象 Spirng 注入属性 Bean 管理操作有两种方式 基于 xml 配置文件方式实现 基于注解方式实现 1.5 IOC 操作 Bean 管理(基于xml 方式) 1、通过ioc容器获取bean 2、使用property注入参数(set方法注入) 参数赋值...

applicationContext.xml、web.xmlspring-mvc.xml 配置深入理解

在web.xml文件中,将spring-mvc.xml和applicationContext.xml一起引入。xml version="1.0"encoding="UTF-8?xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation=...