您现在的位置是:首页 > 文章详情

组件化框架设计之Java SPI机制(三)

日期:2019-11-13点击:373

11/12号文档资料已全面更新!;《【阿里P7】移动互联网架构师高级教程+BAT面试题》,点击下方链接前往领取:
【阿里P7】移动互联网架构师进阶高级教程+BAT面试题
本篇文章将从深入理解java SPI机制来介绍组件化框架设计:

一、SPI机制定义

SPI机制(Service Provider Interface)其实源自服务提供者框架(Service Provider Framework,参考【EffectiveJava】page6),是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。引入服务提供者就是引入了spi接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔。

二、典型实例:jdbc的设计

通常各大厂商(如Mysql、Oracle)会根据一个统一的规范(java.sql.Driver)开发各自的驱动实现逻辑。客户端使用jdbc时不需要去改变代码,直接引入不同的spi接口服务即可。
Mysql的则是com.mysql.jdbc.Drive,Oracle则是oracle.jdbc.driver.OracleDriver。

伪代码如下:

 //注:从jdbc4.0之后无需这个操作,spi机制会自动找到相关的驱动实现 //Class.forName(driver); //1.getConnection()方法,连接MySQL数据库。有可能注册了多个Driver,这里通过遍历成功连接后返回。 con = DriverManager.getConnection(mysqlUrl,user,password); //2.创建statement类对象,用来执行SQL语句!! Statement statement = con.createStatement(); //3.ResultSet类,用来存放获取的结果集!! ResultSet rs = statement.executeQuery(sql);

jdbc连接源码分析

  1. java.sql.DriverManager静态块初始执行,其中使用spi机制加载jdbc具体实现
 //java.sql.DriverManager.java //当调用DriverManager.getConnection(..)时,static会在getConnection(..)执行之前被触发执行 /** * Load the initial JDBC drivers by checking the System property * jdbc.properties and then use the {@code ServiceLoader} mechanism */ static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }

2.loadInitialDrivers()中完成了引入的数据库驱动的查找以及载入,本示例只引入了oracle厂商的mysql,我们具体看看。

//java.util.serviceLoader.java

 private static void loadInitialDrivers() { String drivers; try { drivers = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { //使用系统变量方式加载 return System.getProperty("jdbc.drivers"); } }); } catch (Exception ex) { drivers = null; } //如果spi 存在将使用spi方式完成提供的Driver的加载 // If the driver is packaged as a Service Provider, load it. // Get all the drivers through the classloader // exposed as a java.sql.Driver.class service. // ServiceLoader.load() replaces the sun.misc.Providers() AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { //查找具体的provider,就是在META-INF/services/***.Driver文件中查找具体的实现。 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); /* Load these drivers, so that they can be instantiated. * It may be the case that the driver class may not be there * i.e. there may be a packaged driver with the service class * as implementation of java.sql.Driver but the actual class * may be missing. In that case a java.util.ServiceConfigurationError * will be thrown at runtime by the VM trying to locate * and load the service. * * Adding a try catch block to catch those runtime errors * if driver not available in classpath but it's * packaged as service and that service is there in classpath. */ //查找具体的实现类的全限定名称 try{ while(driversIterator.hasNext()) { driversIterator.next();//加载并初始化实现类 } } catch(Throwable t) { // Do nothing } return null; } }); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) { return; } String[] driversList = drivers.split(":"); .... } }

3.java.util.ServiceLoader 加载spi实现类.

上一步的核心代码如下,我们接着分析:

 //java.util.serviceLoader.java ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ //查找具体的实现类的全限定名称 while(driversIterator.hasNext()) { //加载并初始化实现 driversIterator.next(); } } catch(Throwable t) { // Do nothing }

主要是通过ServiceLoader来完成的,我们按照执行顺序来看看ServiceLoader实现:

//初始化一个ServiceLoader,load参数分别是需要加载的接口class对象,当前类加载器 public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); } public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) { return new ServiceLoader<>(service, loader); }

遍历所有存在的service实现

 public boolean hasNext() { if (acc == null) { return hasNextService(); } else { PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() { public Boolean run() { return hasNextService(); } }; return AccessController.doPrivileged(action, acc); } }
 //写死的一个目录 private static final String PREFIX = "META-INF/services/"; private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { String fullName = PREFIX + service.getName(); //通过相对路径读取classpath中META-INF目录的文件,也就是读取服务提供者的实现类全限定名 if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, "Error locating configuration files", x); } } //判断是否读取到实现类全限定名,比如mysql的“com.mysql.jdbc.Driver ” while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next();//nextName保存,后续初始化实现类使用 return true;//查到了 返回true,接着调用next() }
 public S next() { if (acc == null) {//用来判断serviceLoader对象是否完成初始化 return nextService(); } else { PrivilegedAction<S> action = new PrivilegedAction<S>() { public S run() { return nextService(); } }; return AccessController.doPrivileged(action, acc); } } private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName;//上一步找到的服务实现者全限定名 nextName = null; Class<?> c = null; try { //加载字节码返回class对象.但并不去初始化(换句话就是说不去执行这个类中的static块与static变量初始化) // c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { //初始化这个实现类.将会通过static块的方式触发实现类注册到DriverManager(其中组合了一个CopyOnWriteArrayList的registeredDrivers成员变量)中 S p = service.cast(c.newInstance()); providers.put(cn, p);//本地缓存 (全限定名,实现类对象) return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }

上一步中,Sp = service.cast(c.newInstance()) 将会导致具体实现者的初始化,比如mysqlJDBC,会触发如下代码:

//com.mysql.jdbc.Driver.java ...... private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); ...... static { try { //并发安全的想一个copyOnWriteList中方 java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }

4.最终Driver全部注册并初始化完毕,开始执行DriverManager.getConnection(url, “root”, “root”)方法并返回。

使用实例
四个项目:spiInterface、spiA、spiB、spiDemo

spiInterface中定义了一个com.zs.IOperation接口。

spiA、spiB均是这个接口的实现类,服务提供者。

spiDemo作为客户端,引入spiA或者spiB依赖,面向接口编程,通过spi的方式获取具体实现者并执行接口方法。

├─spiA │ └─src │ ├─main │ │ ├─java │ │ │ └─com │ │ │ └─zs │ │ ├─resources │ │ │ └─META-INF │ │ │ └─services │ │ └─webapp │ │ └─WEB-INF │ └─test │ └─java ├─spiB │ └─src │ ├─main │ │ ├─java │ │ │ └─com │ │ │ └─zs │ │ ├─resources │ │ │ └─META-INF │ │ │ └─services │ │ └─webapp │ │ └─WEB-INF │ └─test │ └─java ├─spiDemo │ └─src │ ├─main │ │ ├─java │ │ │ └─com │ │ │ └─zs │ │ ├─resources │ │ └─webapp │ │ └─WEB-INF │ └─test │ └─java └─spiInterface └─src ├─main │ ├─java │ │ └─com │ │ └─zs │ ├─resources │ └─webapp │ └─WEB-INF └─test └─java └─spiInterface

spiDemo

 package com.zs; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Iterator; import java.util.ServiceLoader; public class Launcher { public static void main(String[] args) throws Exception { // jdbcTest(); showSpiPlugins(); } private static void jdbcTest() throws SQLException { String url = "jdbc:mysql://localhost:3306/test"; Connection conn = DriverManager.getConnection(url, "root", "root"); Statement statement = conn.createStatement(); ResultSet set = statement.executeQuery("select * from test.user"); while (set.next()) { System.out.println(set.getLong("id")); System.out.println(set.getString("userName")); System.out.println(set.getInt("age")); } } private static void showSpiPlugins() { ServiceLoader<IOperation> operations = ServiceLoader.load(IOperation.class); Iterator<IOperation> operationIterator = operations.iterator(); while (operationIterator.hasNext()) { IOperation operation = operationIterator.next(); System.out.println(operation.operation(6, 3)); } } }

SPI示例 完整代码。
原文链接https://blog.csdn.net/lemon89/article/details/79189475

最后

我们今年整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

以下为我们整理的资料免费分享;【阿里P7】Android高级教程+BAT面试题

1.Android高级技术脑图

2.P7级Android高级架构视频教程

3.Android核心高级技术PDF文档+BAT大厂面试真题解析

4.Android思维脑图(技能树)

1.Android高级技术脑图;

查漏补缺,体系化深入学习提升

image

2.【Android高级架构视频教程】;

image

全套部分展示;

java与Android内核进阶专题视频与源码

image

image

阿里P7级全套高级学习视频;

image

3.Android核心高级技术PDF文档,BAT大厂面试真题解析

image

image

4.Android思维脑图(技能树)

image

免费分享

【阿里P7】移动互联网架构师进阶高级教程+BAT面试题

为什么免费分享?

我的目的是让更多需要的Android开发朋友能够提升自己的技术水平
无论是Android,还是qq,微信,360等,想在互联网上最大程度推广,就必须免费!
如果我的学习资料对你有帮助,点个赞,谢谢!

原文链接:https://yq.aliyun.com/articles/727002
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章