当前位置: 首页 > news >正文

新宾区网站建设学校网站开发必要性与意义

新宾区网站建设,学校网站开发必要性与意义,wordpress怎么设置跳站外链接,石家庄权威发布在此前的内容中#xff0c;已经详细介绍了 SkyWalking Agent 用到的多种基础技术#xff0c;例如#xff0c;Byte Buddy、Java Agent 以及 OpenTracing 中的核心概念。本课时将深入介绍 SkyWalking Agent 以及 OAP 中都会使用到的 SPI 技术。 JDK SPI 机制 SPI#xff08…在此前的内容中已经详细介绍了 SkyWalking Agent 用到的多种基础技术例如Byte Buddy、Java Agent 以及 OpenTracing 中的核心概念。本课时将深入介绍 SkyWalking Agent 以及 OAP 中都会使用到的 SPI 技术。 JDK SPI 机制 SPIService Provider Interface主要是被框架开发人员使用的一种技术。例如使用 Java 语言访问数据库时我们会使用到 java.sql.Driver 接口每个数据库厂商使用的协议不同提供的 java.sql.Driver 实现也不同在开发 java.sql.Driver 接口时开发人员并不清楚用户最终会使用哪个数据库在这种情况下就可以使用 Java SPI 机制为 java.sql.Driver 接口寻找具体的实现。 当服务的提供者提供了一种接口的实现之后需要在 Classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件此文件记录了该 jar 包提供的服务接口的具体实现类。当某个应用引入了该 jar 包且需要使用该服务时JDK SPI 机制就可以通过查找这个 jar 包的 META-INF/services/ 中的配置文件来获得具体的实现类名进行实现类的加载和实例化最终使用该实现类完成业务功能。 下面通过一个简单的示例演示 JDK SPI 的基本使用方式示例如下 首先我们需要创建一个 Log 接口来模拟日志打印的功能 public interface Log {void log(String info); }接下来提供两个实现 —— Logback 和 Log4j分别代表两个不同日志框架的实现如下所示 public class Logback implements Log {Overridepublic void log(String info) {System.out.println(Logback:  info);} }public class Log4j implements Log {     Override     public void log(String info) {         System.out.println(“Log4j:”  info);     } } 在项目的 resources/META-INF/services 目录下添加一个名为 com.xxx.Log 的文件这是 JDK SPI 需要读取的配置文件具体内容如下 com.xxx.impl.Log4j com.xxx.impl.Logback最后创建 main() 方法其中会加载上述配置文件创建全部 Log 接口实现的实例并执行其 log() 方法如下所示 public class Main {public static void main(String[] args) {ServiceLoaderLog serviceLoader  ServiceLoader.load(Log.class);IteratorLog iterator  serviceLoader.iterator();while (iterator.hasNext()) {Log log  iterator.next();log.log(JDK SPI); }} } // 输出如下: // Log4j:JDK SPI // Logback:JDK SPIJDK SPI源码分析 通过上述示例我们可以看到 JDK SPI 的入口方法是 ServiceLoader.load()  方法接下来我将对其具体实现进行深入分析。 在 ServiceLoader.load() 方法中首先会尝试获取当前使用的 ClassLoader获取当前线程绑定的 ClassLoader查找失败后使用 SystemClassLoader然后调用 reload() 方法调用关系如下图所示 在 reload() 方法中首先会清理 providers 缓存LinkedHashMap 类型的集合该缓存用来记录 ServiceLoader 创建的实现对象其中 Key 为实现类的完整类名Value 为实现类的对象。之后创建 LazyIterator 迭代器该迭代器用于读取 SPI 配置文件并实例化实现类对象。 ServiceLoader.reload() 方法的具体实现如下所示 // 缓存用来缓存 ServiceLoader创建的实现对象 private LinkedHashMapString,S providers  new LinkedHashMap();public void reload() {     providers.clear(); // 清空缓存     lookupIterator  new LazyIterator(service, loader); // 迭代器 } 在前面的示例中main() 方法中使用的迭代器底层就是调用了 ServiceLoader.LazyIterator  实现的。Iterator 接口有两个关键方法hasNext() 方法和 next() 方法这里的 LazyIterator 中的next() 方法最终调用的是其 nextService() 方法hasNext() 方法最终调用的是 hasNextService() 方法调用关系如下图所示 首先来看 LazyIterator.hasNextService() 方法该方法主要负责查找 META-INF/services 目录下的 SPI 配置文件并进行遍历大致实现如下所示 private static final String PREFIX  META-INF/services/; EnumerationURL configs  null; IteratorString pending  null; String nextName  null;private boolean hasNextService() {     if (nextName ! null) {         return true;     }     if (configs  null) {         // PREFIX前缀与服务接口的名称拼接起来就是META-INF目录下定义的SPI配         // 置文件(即示例中的META-INF/services/com.xxx.Log)         String fullName  PREFIX  service.getName();         // 加载配置文件         if (loader  null)             configs  ClassLoader.getSystemResources(fullName);         else             configs  loader.getResources(fullName);     }     // 按行SPI遍历配置文件的内容     while ((pending  null) || !pending.hasNext()) {          if (!configs.hasMoreElements()) {             return false;         }         // 解析配置文件         pending  parse(service, configs.nextElement());      }     nextName  pending.next(); // 更新 nextName字段     return true; } 接下来在 hasNextService() 方法中完成 SPI 配置文件的解析之后再来看 LazyIterator.nextService() 方法该方法负责实例化 hasNextService() 方法读取到的实现类其中会将实例化的对象放到 providers 集合中缓存起来核心实现如下所示 private S nextService() {String cn  nextName;nextName  null;// 加载 nextName字段指定的类Class? c  Class.forName(cn, false, loader);if (!service.isAssignableFrom(c)) { // 检测类型fail(service, Provider   cn    not a subtype);}S p  service.cast(c.newInstance()); // 创建实现类的对象providers.put(cn, p); // 将实现类名称以及相应实例对象添加到缓存return p; }在 main() 方法中使用的迭代器的底层实现介绍完了我们再来看一下其使用的真正迭代器核心实现如下 public IteratorS iterator() {return new IteratorS() {// knownProviders用来迭代 providers缓存IteratorMap.EntryString,S knownProviders providers.entrySet().iterator();public boolean hasNext() {             // 先走查询缓存缓存查询失败再通过 LazyIterator加载             if (knownProviders.hasNext())                  return true;             return lookupIterator.hasNext();         } public S next() {             // 先走查询缓存缓存查询失败再通过 LazyIterator加载             if (knownProviders.hasNext())                 return knownProviders.next().getValue();             return lookupIterator.next();         }         // 省略remove()方法     }; JDK SPI 在 JDBC 中的应用 了解了 JDK SPI 实现的原理之后我们来看实践中 JDBC 是如何使用 JDK SPI 机制加载不同数据库厂商的实现类。 JDK 中只定义了一个 java.sql.Driver 接口具体的实现是由不同数据库厂商来提供的。这里以 MySQL 提供的 JDBC 实现包为例进行分析。 在 mysql-connector-java-*.jar 包中的 META-INF/services 目录下有一个 java.sql.Driver 文件中只有一行内容如下所示 com.mysql.cj.jdbc.Driver在使用 mysql-connector-java-*.jar 包连接 MySQL 数据库的时候我们会用到如下语句创建数据库连接 String url  jdbc:xxx://xxx:xxx/xxx; Connection conn  DriverManager.getConnection(url, username, pwd);DriverManager 是 JDK 提供的数据库驱动管理器其中的代码片段如下所示 static {loadInitialDrivers();println(JDBC DriverManager initialized); }在调用 getConnection() 方法的时候DriverManager 类会被 Java 虚拟机加载、解析并触发 static 代码块的执行在 loadInitialDrivers() 方法中通过 JDK SPI 扫描 Classpath 下  java.sql.Driver 接口实现类并实例化核心实现如下所示 private static void loadInitialDrivers() {String drivers  System.getProperty(jdbc.drivers)// 使用 JDK SPI机制加载所有 java.sql.Driver实现类ServiceLoaderDriver loadedDrivers  ServiceLoader.load(Driver.class);IteratorDriver driversIterator  loadedDrivers.iterator();while(driversIterator.hasNext()) {driversIterator.next();}String[] driversList  drivers.split(:);for (String aDriver : driversList) { // 初始化Driver实现类Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());} }在 MySQL 提供的 com.mysql.cj.jdbc.Driver 实现类中同样有一段 static 静态代码块这段代码会创建一个 com.mysql.cj.jdbc.Driver 对象并注册到 DriverManager.registeredDrivers 集合中 CopyOnWriteArrayList 类型如下所示 static {java.sql.DriverManager.registerDriver(new Driver()); }在 getConnection() 方法中DriverManager 从该 registeredDrivers 集合中获取对应的 Driver 对象创建 Connection核心实现如下所示 private static Connection getConnection(String url, java.util.Properties info, Class? caller) throws SQLException {// 省略 try/catch代码块以及权限处理逻辑for(DriverInfo aDriver : registeredDrivers) {Connection con  aDriver.driver.connect(url, info);return con;} }Dubbo 对 JDK SPI 的改进 通过前面的分析可以发现JDK SPI 在查找具体实现类的过程中需要遍历 SPI 配置文件中定义的所有实现类该过程中会将这些实现类全部实例化。如果 SPI 配置文件中定义了多个实现类而我们只需要其中一个实现类时就会生成不必要的对象。 Dubbo 为了解决上述问题自己设计了一套 SPI 实现但是思想与 JDK SPI 机制类似。作为思路的扩展这里简单介绍一下 Dubbo SPI 的实现原理SkyWalking 使用是 JDK SPI 而不是 Dubbo SPI 。 首先Dubbo 将 SPI 配置文件改成了 KV 格式例如 dubboorg.apache.dubbo.rpc.protocol.dubbo.DubboProtocol其中 key 就是一个简单的标记当我们在为一个接口查找具体实现类时可以指定 key 来选择具体实现例如这里指定 key 为 dubboDubbo SPI 就知道我们要的是org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 这个实现类。 Dubbo SPI 核心实现是 ExtensionLoader位于 dubbo-common 模块中的 extension 包中功能类似于 JDK SPI 中的 java.util.ServiceLoader其使用方式如下所示 Protocol protocol  ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(dubbo); // 很明显在查找 Protocol这个接口的实现类时还指定了dubbo这个keyExtensionLoader.getExtensionLoader() 方法会根据接口类型从缓存中查找相应的 ExtensionLoader 实现核心实现如下 private static final ConcurrentMapClass?, ExtensionLoader? EXTENSION_LOADERS  new ConcurrentHashMap();public static T ExtensionLoaderT getExtensionLoader(ClassT type) {     ExtensionLoaderT loader            (ExtensionLoaderT) EXTENSION_LOADERS.get(type);     if (loader  null) {         EXTENSION_LOADERS.putIfAbsent(type,                new ExtensionLoaderT(type));         loader  (ExtensionLoaderT) EXTENSION_LOADERS.get(type);     }     return loader; } 查找到接口对应的 ExtensionLoader 对象之后会调用 getExtension() 方法再根据传入的 key 查找相应的实现类最终将其实例化后返回 // 缓存记录了 key到实现类对象Holder之间的映射关系 private final ConcurrentMapString, HolderObject cachedInstances  new ConcurrentHashMap();public T getExtension(String name) {     HolderObject holder  getOrCreateHolder(name);     Object instance  holder.get();     if (instance  null) { // double-check防止并发问题         synchronized (holder) {             instance  holder.get();             if (instance  null) {                 // createExtension()方法中完成了 SPI配置文件的查找以及实现类                 // 的实例化具体实现与 JDK SPI原理类似其中还会处理 Dubbo中                 // 自定义的一些注解不再展开分析                 instance  createExtension(name);                 holder.set(instance);             }         }     }     return (T) instance; } 总结 本课时首先介绍了 JDK SPI 机制的原理并通过 Log 示例演示了 JDK SPI 的使用方式然后深入到 ServiceLoader 的源码中分析了 JDK SPI 的实现方式接下来介绍了 JDBC 4.0 如何使用 JDK SPI 机制加载数据库驱动类最后介绍了 Dubbo 对 JDK SPI 的改进。
http://www.dnsts.com.cn/news/280827.html

相关文章:

  • 国外网站排名 top100net实用网站开发
  • 长春企业建站平台商城类网站价格
  • 网站建设技术人员要会什么北京网站建设w亿玛酷1订制
  • 网站建设排名梁山网站建设费用
  • 佛山新网站建设方案陕西十二建设有限公司网站
  • 高邑网站建设网站建设投
  • seo包括网站建设吗邯郸网站优化
  • 陕西手机网站建设公司哪家好新浪微博可以做网站吗
  • 漳浦建设银行网站竞价网站做不做链接
  • 校园网站建设毕业设计wordpress首页添加登陆
  • 丽江市住房和城乡建设局网站wordpress主题4mudi
  • 实木餐桌椅网站建设某网站开发项目进度表
  • 做网站工资多少钱深圳市福田区
  • 网站色彩学网页制作公司地址
  • 计算机网站建设论文总结网站建设的课件
  • 网站制作与设计设计专业就业方向
  • 教育网络系统管理seo标题是什么
  • 快速建设网站视频教程广州网站建设策划
  • 网站建设 部署与发布做电影网站解决版权问题
  • 网站推广一般多少钱建筑人才招聘哪个网站最好
  • 做网站要什么条件php商城网站开发实例视频教程
  • 云呼叫中心系统 免费石家庄seo全网营销
  • 哈尔滨网站小程序制作seo 合理的网站结构
  • 做3个网站需要多大的服务器设计兼职网站推荐
  • 上海网站开发一对一培训北京室内设计公司
  • 西宁做网站君博领先网站开发中期检查
  • 软件商店下载电脑版官网抖音排名优化
  • 设计专业网站公司网站交互主要做什么
  • 甘肃网站制作公司住建城乡建设部网站证件查询
  • 模板建站是什么行业网站程序