spring中怎么解析xml文件
很多新手在创建Spring的xml文件的时候找不到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.xml、spring-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=...
- 苹果6app怎么更改账号
- varyrt渲染器怎么用
- 三星s5要怎么弄中文
- 3dmax怎么做沙发弧线
- 音响连接不上蓝牙怎么设置
- 怎么烧写程序
- 魅族5怎么设置隐私模式
- hdmi怎么转换usb
- h430联想怎么装不上win7
- ad怎么打开库
- 三相电路的连接方式 怎么选择
- note3阅读模式怎么样
- 路由和光猫 猫怎么连接
- 路由器登陆不了怎么办啊
- 单片机TAB怎么用
- h加号怎么回事
- 51开发板怎么用
- 苹果5s桌面壁纸怎么弄成白色
- 球机摄像头怎么手控旋转
- cc2541怎么用
- 苹果手机怎么锁屏不断网
- 怎么把qq群转为讨论组
- 苹果6黑屏电脑开机怎么回事啊
- 苹果本本怎么安装win7
- 苹果6s手机怎么解锁屏幕旋转
- 静态电流怎么测量
- mpc 1000怎么用
- 磐正bios怎么打开
- 怎么关闭烟雾报警器
- win7系统怎么设置前置声音
- ipad 暴风影音怎么打开本地视频
- 照片变黄了怎么办
- 路由器怎么重设置密码
- 夜间监控视频看不清人脸怎么办
- 家用监控摄像头怎么删除视频
- 苹果怎么安装谷歌
- 拔掉路由器猫怎么连电脑
- 淘宝客服怎么做视频教程
- wifi链接视频监控效果怎么样
- 十字绣软件怎么用
- isis怎么安装
- oppo怎么设置自拍杆
- dvrh264录像设置怎么调
- cad材质库怎么安装
- 怎么替换手机自带卓面
- 路由器设置多帐号登录不了怎么办
- h430联想怎么装不上win7
- 路由器怎么判断烧了
- 单片机pwm电路输出后怎么用
- 笔记本没有键盘灯怎么办
- 哥特英文怎么
- 苹果5c怎么锁屏幕
- 路由换地方怎么设置
- 手机响应慢怎么解决
- 万用表温度怎么测量
- ad怎么隐藏敷铜层
- 怎么让硬盘录像机故障
- 联想液晶屏怎么拆开
- usb接口无线网卡怎么用
- 怎么降低安卓版本
- 淘宝店铺怎么换背景颜色
- ad原理图库怎么快速切换
- 这个续期缴费怎么取消
- ir1838怎么接单片机
- 5s怎么换手机铃声
- 2芯网线怎么接
- 咕咚怎么修改体重
- 施耐德atv31变频器黑屏怎么办
- 飘落的玫瑰花瓣怎么画
- 怎么刷新手机数据
- fpga怎么入门
- 3dmax软件户型图怎么画
- pcm54解码怎么样
- 怎么查看单片机已经烧好的程序
- cmos反相器怎么理解
- 台式电脑怎么用微信
- 苹果5怎么查看拍照时间
- 苹果6s怎么会自动亮屏
- qt怎么在label上画线
- 怎么样画平面图
- cam350怎么导出gerber
- 千牛插件首页怎么设置
- 怎么保护home键
- arm板终端怎么打开
- 怎么下载圆方安装包
- keil 怎么添加芯片
- 老主板没有机箱怎么开机
- kk开关触点怎么看
- 在爱心里面打字怎么弄
- 监控探头接头怎么拆
- opa627ap怎么样
- bufg怎么用
- 笔记本健盘失灵怎么办
- 怎么看笔记本接口是并口
- iphone6怎么设置九宫格锁屏壁纸
- keil 怎么返回断点前几步
- 苹果手机怎么下载录音器
- 电阻100欧姆怎么读写
- 逻辑加的符号怎么打
- sd卡接口识别的挑卡现象怎么解决
- 科密a3考勤机怎么输密码
- 怎么做安卓rom
- iphone5 怎么手机定位
- 嵌入式中怎么编译文件
- 店铺搜索框怎么做
- 酷6网怎么下载 迅雷下载
- 微信买票怎么取票
- op07az有噪声怎么回事
- 笔记本蓝牙驱动安不上怎么回事
- 单片机 怎么修改波特率
- 苹果6plusapple id怎么注册
- 平板上怎么刷机
- stm32f407与dp83848怎么连
- 路由器怎么经常断网
- 手机唱吧里面的歌曲怎么下载
- 苹果手机怎么让铃声随机
- 苹果4s怎么去掉越狱
- 6官方保护壳怎么安装
- 单片机0xfe怎么来的
- 电路图中保险器怎么图
- 美图开机黑屏怎么办
- 大华怎么设置
- 千牛怎么进入分销平台
- 三星k2200复印机怎么使用
- cbb18电容怎么读
- 微信怎么发音乐文件
- 手机芒果tv怎么推送到电视
- 宝贝怎么自动上架
- pads导出pcb时怎么打散元件
- dspic单片机怎么样
- 路灯电路图怎么画
- ipad播放视频是快放是怎么回事
- 怎么格式手机内置sd卡
- 联想电脑键盘怎么锁
- cpu变慢了怎么办
- 淘宝怎么更换宝贝图片
- 怎么设置店铺标志
- 怎么举报约爱软件
- 怎么刷新手机数据
- vivoy35怎么root
- atmel ice 怎么用
- rc leading怎么玩
- 3dmax怎么将点移位
- 协方差矩阵符号怎么读
- 淘宝怎么做倒计时
- 苹果6s怎么蓝牙传软件
- 内存条怎么换
- 苹果5s桌面壁纸怎么弄成白色
- 三星note4怎么关闭快速充电
- c语言指针变量怎么定义
- 联想电脑拯救系统打不开怎么办
- 总是显示淘宝店名被使用怎么办
- 怎么新做一张图片
- 单板机上怎么修改程序
- ad713jn运放怎么样
- 473皮法电容怎么量
- 安卓机器人躺了怎么办
- 苹果9.3.2怎么设置铃声
- 淘宝收藏夹怎么设置不公开
- 苹果id怎么取消关联
- led怎么设计尺寸
- 超市卖水果的条形码是怎么生成的
- 怎么看店铺流量
- 猎豹在线收藏夹怎么用
- 单片机怎么引脚哪个是1
- 平板电脑的系统数据网络怎么打开
- 10块mp3怎么充电器
- fd怎么用
- 监控怎么重新安装
- 总买电子产品怎么办
- 怎么提高基代
- 51怎么看串口发过来的数据
- 520怎么双击唤醒
- 苹果5siBook怎么清除缓存
- 怎么设置海报背景
- 耦合变压器怎么接线图
- 没有电脑怎么改wift
- hspice 怎么实现 导通 关断
- 充电宝突然没反应了怎么办
- moc3023怎么用
- Arm外部存储器EMC 读写怎么转化
- 大对数与110配线架怎么连接
- 惠普笔记本怎么开小键盘
- 爱普生打印机 打印页上下错位 怎么解决
- 4s信号差怎么解决方法
- mc506怎么样
- 红米手机充电不稳定怎么回事
- 新机苹果ld怎么注册
- 淘宝怎么添加收藏
- 8259 怎么进入中断程序
- 怎么看戴尔笔记本性能
- gx这两个字母怎么设计简洁大方
- 苹果6plus假的怎么辨别
- 苹果手机qq怎么降级
- 苹果4的手电筒怎么关
- 淘宝手机端怎么操作
- 基本rs触发器 怎么理解
- 笔记本闪屏开不开机怎么回事
- 电路中负电荷怎么提供给负载能量
- 怎么看电脑的尺寸大小
- 导航仪怎么设置时间
- 铜丝吸锡怎么弄
- 苹果手机5 s怎么样
- 怎么用特效美图
- mac怎么连投影仪
- cad中怎么转换模式
- 主机不开机怎么办
- 电容器规格怎么看
- c51中setb怎么用
- 酷派开机显示卡没有是怎么回事
- matlab怎么从busy状态跳出
- 小米手机怎么去除静电
- 密码忘记了怎么开机
- 有信专线怎么用
- td62083怎么用
- JBLkm210怎么样
- cad家具的大小怎么调整
- 怎么写管脚配置
- 魅蓝note2怎么打开usb
- 努比亚z9怎么设置安装位置
- 图库不显示sd卡的照片怎么办
- 魅族mx6怎么样开起闪冲
- ewb中怎么选择芯片
- 外链怎么代替淘宝图片空间
- 720图片怎么制作
- 哥特英文怎么
- igzo屏怎么样
- pmos怎么分析
- 怎么接电容
- bios怎么还原
- 电位器大小怎么选
- cl31a226kahnnne怎么测量
- u盘摔了插上去没反应怎么办
- 微星647怎么进入bios
- ipod shuffle3怎么用
- ads中怎么插入输出节点vout
- 淘宝右侧悬浮导航怎么做
- 怎么把照片修成古风
- 禅道怎么删除任务
- 怎么查看cydia冲突
- 魅族2怎么把内存卡拿出来
- led点阵怎么控制速度
- vhdl怎么在fpga上运行
- 程序跑飞是怎么一回事
- 路由器扩展器怎么安装
- 淘宝助理上怎么上传图片
- 怎么做摄像头
- pcb怎么接单
- 怎么测试屏幕的好坏
- 怎么看淘宝消息记录
- 液晶显示器 怎么换灯管
- 苹果5拍照模糊怎么解决
- 联想z50怎么拆机
- 苹果6怎么按键重启手机
- iOS怎么电池校准
- 60比32怎么写出过程
- quartus cpld怎么回读
- 怎么才能把别人的淘宝网页的模块复制来
- 怎么样画平面图
- 怎么让照片墙静止
- opa37怎么样
- 路由器扩展器怎么安装不了网
- 苹果6s动态壁纸6怎么没有
- 四轴gps定点怎么实现
- 怎么安装笔记本网线
- op07cp运放怎么样
- 苹果相机怎么用法
- 电脑不能启动怎么
- 用苹果6s上qq怎么显示
- 怎么连接无线
- cad怎么画厚度
- avr 怎么使用 外部晶振
- 二维码怎么发到朋友圈
- 怎么定义字符串数组
- 无极电容怎么串联
- 苹果手机QQ怎么打开一闪就关了
- protel怎么添加lcd显示器
- 两头电线怎么接最结实
- 360手机防盗怎么收费
- 麦客怎么去掉表头
- altium designer怎么读
- psim怎么接线
- 苹果6云怎么用
- pcb怎么封装
- 4700套件怎么调
- 怎么样重装手机系统
- tplink路由器怎么念
- modelsim 怎么综合
- 路由器怎么设置调光猫
- 苹果四怎么清洗喇叭
- 网线怎么处理
- 6s玫瑰金掉漆怎么办
- 电阻100欧姆怎么读写
- 手机淘宝logo怎么安装
- iphone怎么保护电池
- 怎么盗一个淘宝网店
- 淘宝网怎么发链接
- cmd怎么进入设置
- io口输入电流缓冲一下要怎么设计
- 猫 路由器ip怎么修改密码
- 怎么判断电感的好坏
- ahua监控时间不对怎么更改
- 怎么hu
- stm8s208怎么样
- 纽扣电池怎么保存
- 手提怎么开wifi热点
- x25045怎么用
- 电脑怎么添加蓝牙音响
- 苹果6怎么改变苹果id
- rs触发器原理怎么理解啊
- 路由器扩展器怎么安装不了
- cms监控怎么录相
- jlink 怎么做串口实验
- 怎么给安卓平板做系统
- 怎么制作无限遥控器
- dvd驱动器怎么装
- irf630mf怎么测量好坏
- 平方根解决问题怎么做
- 怎么样的宾馆监控录像会云端保存
- 移动叔叔工具箱怎么刷机
- avr 中 怎么定义寄存器的某一位
- 陌陌怎么关闭更新声音
- 电路板输入信号干扰怎么办
- 手机语音控制怎么关
- 路由和光猫 猫怎么连接
- 海康威视密码忘记了怎么办
- 怎么测量距离
- 怎么把图片传到旺旺上
- ad14怎么铺铜
- op yz40d怎么换
- tft彩屏怎么显示汉字
- 苹果6怎么更换系统字体
- 168的平方是怎么算
- 淘宝开店怎么上传图片
- matlab怎么作方程的图
- fpga dds 产生波形 频率怎么算
- dac0832寄存器地址是怎么算的
- 淘宝店铺头像怎么弄
- ta8216h做功放怎么样呀
- ad7767怎么用
- ipad没有原ID怎么激活
- ad打印pcb怎么黑色的
- 魅族2怎么把内存卡拿出来
- i9300驱动怎么安装
- 磁铁怎么安装
- 配模图怎么看
- 淘宝上怎么网页复制
- 慕课网实战视频怎么看
- 电瓶短路了怎么办
- 联想怎么隐藏应用程序
- 同方笔记本怎么样
- keil4怎么下载芯片
- 怎么保存相片
- 怎么在家装两个无线路由器
- 图片怎么防盗
- 苹果6plus怎么设置桌面图标
- 淘宝助理销售属性怎么没反应
- 电路板怎么仿制
- 政府部门是怎么进行网络监控
- c语言调用函数怎么写
- pl2303怎么用
- qc怎么新建模块
- 安卓手机充不进去电怎么办
- 怎么拆散热器
- stm32f030c8 怎么样
- 十六进制数怎么移位
- 永硕e盘怎么删除目录
- at89c52怎么下载程序
- 华为荣耀6界面是英文怎么转换
- 怎么用微信充值百度云会员
- 电容怎么量多少微法
- 怎么制作背景分离区
- 没图片看怎么办
- 苹果6plus怎么注册app
- 华为应用市场怎么没有管理
- 联想预装系统怎么装
- 蒙语怎么下载安装
- 怎么才能开超清
- 苹果5日本版怎么解锁
- hp一体机怎么拆
- 平板无线怎么设置密码
- stm8s103f3p6引脚输入怎么设置
- leb怎么做
- hp打印机怎么进纸图解
- 热敏电阻怎么读数
- 怎么安装canon打印机
- 壁纸怎么还原
- 指纹考勤机怎么设置密码
- 3d怎么做房屋
- 母版ppt怎么变成普通
- 苹果5s充电器怎么插
- 36负电压怎么变220的
- 阿里旺旺员工账号怎么设置
- quartus ii ip核过期怎么办
- 图片属性怎么找不到图片地址做空间墙纸
- 华为3cSD卡怎么用
- 联想笔记本*号键怎么打
- 魅蓝5s的卡怎么剪卡吗
- 光猫自己怎么安装
- 电路板测量仪怎么用
- 怎么用单片机做呼吸灯
- pcb怎么接单
- 海康威视密码忘记了怎么办
- 电动车电压怎么降压
- 怎么进行整理
- 怎么下载淘宝模板
- ad布线时怎么批量把顶层元件放置到底层
- 保险丝怎么配
- 74ls192和74ls138怎么连
- 方波怎么放大
- ace旅行箱怎么改密码锁
- 电路的电路图怎么看
- 9308怎么root
- 58同城手机怎么登陆
- iphone5s日版怎么用4g
- 魅族分频怎么操作
- opa627ap怎么样
- 没有网络怎么安装摄像头
- cc2541怎么用
- 综合布线怎么计算
- 喷绘写真黑色怎么调
- 电视颜色怎么校正
- ?U?^怎么读拼音
- 台电c430怎么用
- 苹果6怎么换账号
- 千牛插件首页怎么设置
- 联想手机出现全部乱码怎么办
- 手机下载bt打不开怎么办
- 筋雕效果怎么样
- 电子电池怎么装图示
- 苹果6s丢失部分电话号码怎么找回
- sc7731g怎么样
- 32转化为16进制怎么算
- 网线暗线断了怎么办
- s7锁屏时间怎么设置
- 怎么找同行换单子
- 惠普的键鼠怎么样
- fft的库文件怎么使用
- 空电路板怎么写入程序
- 电路时序图怎么看
- 笔记本画面模糊怎么调
- c语言不用数组怎么排序
- 电流是怎么流的
- 三星9300手机没有信号怎么办
- 与门怎么画
- 苹果6plus怎么下载铃声到手机上
- 小米2怎么把软件安装到
- 笔记本电脑散热器扇叶怎么拆
- cad橱柜尺寸怎么标注
- 231模块开关怎么是毫安档
- 平板手机qq怎么退群
- 鲁大师怎么检测显示屏
- mac找不到蓝牙设备怎么解决
- 导航器怎么做
- 5S不能显示4G怎么设置
- proteus怎么改变导线颜色
- ev1527怎么解码
- mos管上怎么加散热片
- 32f4卡尔曼滤波怎么使用寄存器
- 光栅a b信号怎么接
- 怎么删除宝贝详情上面的模块
- mac怎么充电器怎么连接
- 苹果5s彩信怎么关闭
- 淘宝店招怎么加入链接
- 苹果6怎么设置ap热点
- 苹果电脑怎么切换投影仪
- 苹果5s怎么把升级提示取消
- 么么直播怎么得经验快
- 苹果5s打字的键盘声音不对怎么办
- i897怎么刷机
- 图片发黄怎么处理
- 家里的电脑怎么设置wifi
- 路由器多个账号登录不了怎么办
- 苹果 应用内访问不了怎么办
- 51单片机io口数据怎么显示出来
- 苹果5日版a1429怎么样
- 下载移动eHR怎么登录
- 怎么做智能系统
- 5s运营商怎么升级
- 分压式偏置放大电路怎么求B
- 红米怎么破解字体
- 路由器怎么会被劫持
- 淘宝收藏夹怎么设置不公开
- cad怎么填充不上
- 微信二维码怎么发到朋友圈
- 怎么处理暗色系列照片
- 尼尔机器纪元怎么全屏
- 我的电脑和房东家的冲突怎么设置
- 电脑一体机怎么装系统
- 第三方rom怎么解决驱动问题
- UC浏览器怎么才能连格式一起复制
- ltc2942的极限电压怎么设置
- 萤石云云存储怎么关闭
- 链家员工邮箱怎么
- 路由器怎么分帐号
- 苹果6plus怎么手电筒
- 电解电容怎么用
- 联想固态硬盘怎么安装系统安装
- 路由器信号不稳定怎么回事
- pcb怎么接单
- 肾病ad125高是怎么回事
- 程序怎么写
- dsp未使用的输入输出引脚怎么办
- 联想预装系统怎么装
- 海康计划录像怎么开
- 苹果6s手机怎么解锁屏幕旋转
- 4098继电器怎么用
- 0xcc怎么读
- 路由器的id怎么改密码
- 74ls148编码器怎么连到译码器
- ipad air2怎么看网速
- 淘宝店铺头像怎么弄
- 安卓微信恢复聊天记录怎么恢复
- 苹果6s字体怎么改大小吗
- 30pf怎么读
- 3d max 怎么做倒影
- 怎么删除手机病毒软件
- 电脑连接wifi需要验证怎么弄
- 淘宝卖家怎么设置分享
- 最简单的户型图怎么画
- 两头电线怎么接最结实
- 5656除以101怎么简便
- 淘宝ps怎么用
- satari书签怎么同步
- 收到别人无线信号怎么进入
- 6splus锁屏键怎么设置
- ipad air2下载软件太慢怎么办
- 苹果专用充电宝怎么用
- labview界面怎么插入图片
- 苹果5c插上显示3g怎么回事
- 电源网怎么发帖
- 手提电脑硬盘坏了怎么办
- ps怎么做规划图
- 淘宝助理怎么用图片搬家
- 苹果本本怎么安装win7
- 路由器扩展器怎么安装不了
- ad原理图库怎么快速切换元器件
- 视频线怎么接网络摄像头
- 苹果6plus假的怎么辨别
- oppor7s怎么创建常用语
- protel dxp怎么设置显示格子大小
- ad9怎么添加protel的元件库
- 怎么看z9支持电信卡
- stm32没有进位 标志怎么办
- e-v5怎么刷机
- 淘宝店铺怎么装修视频教程
- cam350怎么导出gerber
- 宽度怎么设计
- 大华dvr时间怎么设置方法
- 苹果ipod怎么使用教程
- 怎么上传淘宝店标
- win7怎么调耳麦音量
- 苹果 来电 怎么录音
- 怎么样重装手机系统
- 4s7点几的怎么降到6点几
- 怎么计算寸
- 苹果6plus港版的怎么样验货真假
- quartus ii 怎么编译
- iap15f2k61s2 怎么仿真
- 电脑打不上字了怎么办
- 怎么查笔记本的尺寸
- 监控密码忘记怎么办
- 苹果5怎么查看拍照时间
- 鸟笼怎么画
- 国际版qq怎么老是卡屏
- 怎么通过gsm模块发送语音提示
- 5s的怎么切换到原本的输入法
- 平板怎么关省电模式
- 大学只有51单片机的课怎么办
- 来电通怎么看哪张sim卡的短信
- 单板机上怎么修改程序
- 无极电容怎么串联
- mos管 spectre 仿真怎么看ro
- usb调试模式怎么打不开
- 电子音量mcu怎么接
- 便携式移动路由器怎么用
- 玩游戏联通宽带怎么样
- dac8541怎么用
- 美版iphone5怎么刷机
- 苹果6s缩放功能怎么用
- 平板被偷了应该怎么办
- 6s怎么不能用语音输入法呢
- proteus仿真怎么用
- 如意投计划怎么设置
- itune怎么关闭自动备份
- 三星怎么只用3g网络
- 逻辑电平输出怎么接
- 路由器设置多帐号登录不了怎么办
- xm格式手机怎么打开
- 16进制怎么转10进制数
- 2台功放怎么连
- 旺旺怎么登陆
- win8手机怎么刷机
- 怎么看keil是否破解
- 发照片怎么配文字说明
- 电话线怎么接485信号
- 怎么判断步进电机驱动器是否坏了
- 怎么向打印机发送命令
- 平面设计师怎么接单
- 风扇驱动怎么还有hall
- 怎么变占空比
- 文本文档怎么改成txt
- 外墙在cad中怎么表示
- 华硕x403主板不识别硬盘怎么办
- 5s屏幕怎么校准
- 电脑的照片怎么导入iphone6
- 怎么能将ldo前端的滤波电解烧坏
- 手机怎么用usb电脑的网络上网
- 圆螺丝怎么拆
- 监控poe分离器怎么接
- cad室内设计平面图窗怎么画
- keil怎么添加文件
- live照片怎么看
- ps怎么做精装嗯a3封面
- 共发射极放大电路rbe怎么求
- 魅族5手机怎么打开usb调试模式吗
- 苹果笔记本是怎么散热的
- 联想一体机开机就蓝屏怎么办
- 传感器和单片机怎么接
- c店全屏店招怎么上传
- 蜂鸣器的封装图怎么画
- aduc7023怎么下载程序
- macbook pro编程怎么样
- 淘宝卖家怎么设置分享
- iphone怎么看5还是6的
- 指纹打卡机怎么恢复出厂设置
- 可视栅格怎么设置
- 怎么可以关闭快门
- 飞思卡尔单片机怎么样
- 60转12v的线怎么接
- 魅族5手机怎么打开usb调试
- 电气距离怎么计算
- 芒果账号怎么免费领取
- 怎么让手机测网速
- 笔记本闪屏开不开机怎么回事
- 苹果6s怎么能消除编辑
- 联想新主机内存条坏了怎么办
- 怎么举报约爱软件
- 电路怎么检查
- 4s7点几的怎么降到6点几
- 台式机不安装宽带怎么上网
- 怎么让2g网速变快
- 怎么打开jlink软件
- 流动字幕怎么设置
- 美版1633电信3G不能上网怎么办
- 蓝牙音箱怎么连电脑
- 怎么在店铺首页显示新品
- 苹果6怎么分享软件
- 努比亚z9怎么设置安装位置
- 联想电脑怎么设置dns
- 淘宝卖家怎么修改类目
- 示波器怎么看串口数据
- 淘宝怎么没反应了
- 怎么查看手机是不是三网通
- 微博私信怎么自动回复
- 怎么传送指纹
- 位置定位图标怎么打
- 华为手机自动拨号怎么回事
- qq怎么看历史消息
- 1602怎么接
- 爱我X9手机怎么截屏
- opamp怎么变符号
- 10进制数转换为5进制数怎么转换器
- cad家具的大小怎么调整
- 耦合变压器怎么接线图
- 手机出现陌生人id怎么办
- multisim总线怎么用
- jlink怎么下载da14580
- 台式主机内存条怎么装
- 怎么删除监控的显示时间
- 淘宝怎么寄东西
- 淘宝发不了截图怎么办
- 监控硬盘录像机里硬盘满了怎么办
- c语言除法怎么用强制转换类型
- cad中窗帘怎么画
- 怎么放大鼠标
- 16进制怎么转换为10进制
- 怎么 有线
- ipad怎么把繁体字
- 笔记本怎么链接电视
- altium怎么画pcb
- hdmi延长线怎么样
- 机器人网站怎么做好
- 宝贝自定义内容怎么删除
- 6s语音播报怎么关
- 怎么查看苹果电脑配置
- 路由器扩展器怎么安装不了网
- 红米手机怎么打字
- 考勤机文件怎么打开
- proteus安装过程中怎么升级
- 智能模板怎么用
- 苹果4电子邮件怎么填
- hp1536怎么恢复出厂
- iar怎么调试
- 内存条怎么换
- 苹果6s怎么看是不是有锁
- kn hp1008怎么样
- 华汕电子怎么样
- 蓝牙串口助手怎么使用
- 命令怎么关闭tomcat
- 智能模板怎么用
- 电子怎么充电
- 淘宝店怎么设置阿里旺旺
- 壁纸广告语该怎么做
- 怎么测试可控硅好坏
- 苹果ID用不了了怎么取消定位服务
- ad敷铜后选择网络还有飞线怎么回事
- 漩涡相机怎么用
- 淘宝发不了截图怎么办
- 10的次方怎么算的
- pads怎么进入无模命令
- pc遥控器怎么用
- 淘宝上怎么用旺旺
- 手机没有流量管家怎么锁死流量
- protel封装时怎么画等长的斜线
- 苹果手机怎么连接没有密码wifi
- 电脑系统内存不足怎么解决
- 量化噪声怎么算
- 怎么接电容
- itoa怎么用
- hc573sj 单片机 c51 怎么连接
- adc打团时被js切怎么办
- lua脚本怎么写
- 欧式花纹图案怎么画
- 内网ip地址怎么设置
- 手提电脑硬盘坏了怎么办
- 图库不显示sd卡的照片怎么办
- 魅族怎么设置应用角标
- 台式主机内存条怎么装
- 图片发黄怎么处理
- 36个摄像头怎么收费
- 苹果5s越来越慢怎么办
- 电脑无线热点怎么开启
- 怎么做几何色块背景
- gbu808怎么焊接
- 苹果6所有图标都不见了怎么办
- rlc 品质因数怎么算
- 政府部门是怎么进行网络监控
- iphone5s港版a1530怎么改
- arduino 软串口怎么用
- 录屏大师手机版怎么弄
- 酷派开机显示卡没有是怎么回事
- opencv 怎么抓取屏幕
- 用苹果6s上qq怎么显示
- 怎么查看网上不清楚照片
- 店铺网址怎么改
- 魅族手机联系人怎么备份
- 怎么做个小四轴飞机
- hp打印机怎么进纸图解
- 联想怎么隐藏应用
- ad713jn运放怎么样
- 手机刷砖了怎么办
- 怎么把电子图案和实物图案一样
- 字库怎么用在单片机上
- note4的nfc怎么用
- 锁相环怎么失锁
- 路由器网络怎么设置密码
- hfss怎么看阻抗匹配
- iar在线调试 怎么窗口
- 魅族2显示内存卡已损坏怎么办
- protel99中pcb怎么转换成cad
- 苹果手机里图片怎么另外存电脑
- 电气距离怎么计算
- ksp13怎么测量好坏
- iphone怎么删除录音
- iphone怎么删除录音
- 苹果6plus激活密码忘记了怎么办
- 电信3g怎么设置
- 淘宝店铺壁纸怎么搞
- 厦门违章处理代码8055d怎么处罚
- 随手记怎么导入短信记录
- gbu1010怎么判断好坏
- 咕咚怎么修改体重
- protel上怎么画光纤收发器
- 淘宝手机号不用了怎么办
- iphone怎么看5还是6的
- 怎么在桌面添加日历
- ipad2系统文件怎么看
- 微型飞行器怎么做
- 两个sata3硬盘怎么并联
- 保险丝怎么配
- 苹果5怎么调话筒音量
- 电脑蓝牙音箱怎么用
- 怎么用特效美图
- ai怎么使用剪刀
- ad中开关怎么找
- 怎么重新下载老版本微信
- 苹果手机怎么直接进入
- pc怎么算
- 机器人网站怎么做好
- iphone5s系统8.3怎么样
- 淘宝商品怎么换链接
- 电容4700pf怎么测量
- dm365怎么访问寄存器
- verilog 怎么表示sgn
- orcad怎么打开asc文件
- 设计周边怎么做
- 怎么修改淘宝店铺属性
- 怎么修改淘宝区域
- 3dmax弧怎么做平面
- mercury无线路由器怎么看密码是多少
- cpu倍频怎么
- 三星s6锁频声音怎么换
- sd卡安装证书文件怎么获取红米
- 打印纸怎么选
- 内网ip地址怎么设置路由器
- 淘宝psd模板怎么上传
- 淘宝的验证码怎么写
- microblaze怎么用
- 美洽怎么自定义链接
- 苹果5id号怎么申请
- 苹果6怎么把图标隐藏
- 10进制怎么转成16进制
- keil怎么生成opt文件
- 艾泰ap怎么样
- bui怎么接受数据
- 网线与电话线怎么接
- 暗线网线怎么接
- 怎么做摄像头
- 日常锁头怎么撬开
- 路由器校园网怎么设置
- 活动手册32怎么写
- pcb怎么导入原理图
- 浏览器停止怎么办
- 怎么查看单片机已经烧好的程序
- ps怎么解决缝隙
- 华为手机怎么硬件检测
- 怎么样保护笔记本电脑电池
- wire型变量怎么赋值
- bf630 scan 怎么用
- miui8耗电严重怎么解决
- 录制微信视频没声音怎么办
- 斗鱼第三方登入怎么查帐号
- xm格式手机怎么打开
- 单片机定时器256ms怎么设置
- 苹果图标会动怎么关掉
- 指文考勤机管理员怎么进入
- 怎么分辨USB线
- 铜丝吸锡怎么弄
- 电压过冲怎么解决
- 怎么设置打孔流程
- 白光芯片瞬时电压怎么测
- mini2怎么连接电脑
- pads文件怎么打开
- m1卡怎么破解
- 录音备忘录怎么导出
- 电脑风扇怎么安
- 150m无线路由器怎么安装
- 越狱手机怎么用优步
- 无线网怎么没有了
- 0x01在内存单元中是怎么存储的
- 上网管家怎么不能用了
- 苹果wi-fi伴侣怎么用
- 怎么制作简易耳塞
- 条形码机怎么用
- 高频uces怎么求
- 苹果6app帐号怎么注册
- c8051f340怎么下载程序
- op491怎么用
- 千牛快捷键怎么取消
- 安全模式黑屏怎么办
- 读读写写怎么做
- pc怎么连接蓝牙音箱
- verilog数组端口怎么配置管脚
- 怎么设计图片广告
- 我保存的图片怎么找不到
- 苹果怎么暂停更新
- proteus中变压器怎么设置
- 怎么送好友空间装扮
- iphone5s港版a1530怎么改
- 机机上长水泡是怎么回事
- 怎么样画平面图
- 100以内平方数怎么记
- 怎么看有没有设置手机详情页
- simulink窗口大小怎么调节
- cortex杂志怎么样
- oppor7s怎么创建常用语
- 空间视频怎么保存
- usart hmi怎么用
- io口输入电流缓冲一下要怎么设计
- 电脑不能启动怎么
- pcb怎么布铜
- adea236b门禁怎么修改密码
- lenovo u盘保护怎么取消
- 苹果ID用不了了怎么取消定位服务
- 在电脑上怎么制作图片
- 暗线网线怎么接
- 笔记本电脑黑屏不能开机怎么回事
- 电压怎么转换
- 5s越狱后怎么出厂设置
- ads怎么导入protel
- 桌子左右晃动怎么固定
- win7怎么设置水星路由器
- 路由器限速怎么更改
- 猎豹轻桌面怎么样
- 苹果手机怎么下载微博
- 把手机的型号信息改了怎么还原
- 五四打印助手怎么打印淘宝单
- 淘宝活动页面怎么装修
- 新硒鼓怎么取胶带图解
- verilog 计数器程序怎么写
- 内置显卡坏了怎么办
- 港版苹果怎么支持电信4g
- vbo怎么播放
- 照片变黄了怎么办
- 怎么查笔记本的尺寸
- quartus cpld怎么回读
- 万象壁纸怎么用步骤
- wifi互相干扰怎么处理
- 笔记本屏幕怎么调暗
- 电路怎么检查
- 苹果5s换卡后激活不了怎么办
- 层次原理图怎么连接两张图纸
- 美洽怎么自定义链接
- pnzk文件怎么打开
- 美商海盗船驱动怎么下载
- 苹果6开锁怎么设置
- 电脑安装系统后忘记密码怎么解锁
- 内部存储卡怎么使用
- ark文件怎么打开
- 60比32怎么写出过程
- 电路图怎么看电流流向
- jlink 怎么做串口实验
- 麦库记事怎么加密
- 三星自带强效省电软件怎么提取
- 手机怎么监控路由器
- 用小键盘怎么打开无线
- ps窗帘怎么做褶皱
- 安卓6.0怎么限制开机自起
- ipda怎么样叫别人无法用
- 天线接头怎么接的
- 拆键盘按键里面没有弹簧是怎么回事
- iar怎么烧录
- 二维码怎么发到朋友圈
- 高精度怎么转化
- 关闭ssi怎么连接
- 5sa1453怎么鉴定有没有卡贴
- 怎么修改淘宝区域
- 并口线怎么设置
- 苹果平板该怎么充电
- 苹果6s怎么微笑双开
- 苹果6手机玩手游闪退怎么办
- 草图大师怎么贴地板
- 怎么算用什么空开
- 南方cass9.0怎么安装
- 10k色环电阻怎么表示
- 怎么查出手机看出型号
- 苹果5系统固件怎么设置
- iphone5s怎么看图片大小
- bnd 50 sc36怎么用
- 电脑触控板怎么没用了
- 联想笔记本g50u盘启动怎么弄
- 3d max地砖贴图怎么去除黑边
- layout 怎么画定位孔
- ipad停机怎么办
- 邻居借wifi密码怎么说
- 声光灯怎么做
- rom manager怎么用
- pads怎么出坐标文件
- 电热线怎么接图解
- iphone5s怎么看是不是4g
- 三星a5怎么设置不了时间
- 6678的emif怎么配置
- 监控录像机打雷下雨怎么
- 贴画贴在身上怎么才能时间长
- 路由器信号不稳定怎么回事
- 屏幕少一块怎么自动调整
- 收藏的淘宝店铺怎么导出
- 3dmax优化里的数值怎么调
- p10单元板想长亮怎么接
- c语言调用函数怎么写
- 淘宝怎么设计模板
- 淘宝怎么添加店铺介绍
- 苹果5话筒怎么设置
- 苹果6下账户设置怎么弄
- 反正切函数怎么算
- 怎么批量上架宝贝
- 5s升级ios9没取消密码怎么办啊
- 移动卡密码怎么查
- 浏览器 日语怎么说
- 怎么用电脑给红米刷机
- 淘宝促销活动怎么设置
- Dsp怎么和arm结合起来
- 无线ap怎么使用
- 手机QQ发出去的说说怎么修改
- op07az有噪声怎么回事
- 草图大师2014怎么渲染
- 惠普笔记本开机黑屏怎么办
- iphone怎么看5还是6的
- 苹果5s怎么没有震动了
- iPhone 怎么设置横幅大小
- 手机淘宝logo怎么安装
- stc下载器怎么和最小系统板接线
- 夜间监控看不清怎么办
- 欧式花纹图案怎么画
- 淘宝发不了截图怎么办
- stc89c52怎么烧
- 怎么编辑商品编码
- 电子制冷片效果怎么样
- 数字监控怎么搜索图像