美文网首页Java 杂谈
Java&Android开发-浅析ServiceLoad

Java&Android开发-浅析ServiceLoad

作者: java高级编程中心 | 来源:发表于2019-02-20 22:00 被阅读0次

    了解ServiceLoader,需要先了解 SPI (Service Provider Interface)

    SPI的简单来说就是在程序设计时将一个功能服务的接口与实现分离,在程序运行时通过JVM机制自动找到服务接口的实现类并创建,以达到解耦的目的,提高程序的可拓展性; 比如JDBC

    小编整理了一些java进阶学习资料和面试题,需要资料的请加JAVA高阶学习Q群:664389243 这是小编创建的java高阶学习交流群,加群一起交流学习深造。群里也有小编整理的2019年最新最全的java高阶学习资料!

    2.ServiceLoader

    ServiceLoader就是 Java平台提供的一个简单的 Service Provder Framework 。使用ServiceLoader有简单的以下几个步骤

    创建服务接口

    在服务接口的实现模块中,创建一个实现类实现对应的服务接口,并通过在项目的resource/META-INF/services文件夹下面创建一个对应该服务 接口全限定名 的文本文件,在该文本文件写入该服务接口实现类的全限定名,以此达到一个注册服务的作用(项目打包后在jar文件里也得存在该文件)

    服务调用方(需求方)通过ServiceLoader类的load方法加载服务并得到服务的实现类

    2.1 一个简单ServiceLoader场景实例

    这里以一个简单虚拟支付场景为例。 有一个业务模块目前需要使用支付服务,所以我们首先创建了一个PaymenService抽象接口表示 支付服务,接口类中有一个抽象方法**pay(String productName,double price)**表示支付某个商品 ###创建服务实现模块

    packagecom.knight.serviceimpl;importcom.knight.PaymentService;publicclassPaymentServiceImplimplementsPaymentService{@Overridepublicvoidpay(String productName,doubleprice){        System.out.println("支付模块:购买产品 "+productName +",价格"+price);    }}复制代码

    在IDEA中的结构如下

    创建服务接口类

    通过ServiceLoader获取服务

    业务模块中直接通过ServiceLoader类及PaymentService接口获取服务实例并实现业务逻辑(业务模块一般是不包含服务的实现模块的)

    package com.knight.business;importcom.knight.PaymentService;importjava.util.Iterator;importjava.util.ServiceLoader;publicclassBusinessModule{publicstaticvoid run(){Iterator serviceIterator =ServiceLoader.load(PaymentService.class).iterator();if(serviceIterator.hasNext()){PaymentServicepaymentService = serviceIterator.next();            paymentService.pay("Q币充值",100.00);        }else{System.out.println("未找到支付模块");        }    }}复制代码

    以上的核心代码是 通过 ServiceLoader的load方法,传入PaymentService接口类,会返回一个ServiceLoader的实例对象,通过该对象的**iterator()**方法会返回一个 Iterator 的迭代器,可以通过这个迭代器得到所有PaymentService的实现对象。

    最后 我们再创建一个app模块运行业务代码逻辑,app模块包含service、service-impl、business、3个模块。

    以上所有代码已上传 git

    ServiceLoader 核心源码简单解析

    ServiceLoader内部细节 1.首先通过静态方法load获取对应服务接口的ServiceLoader实例; 2.ServiceLoader类继承了Iterabale接口,内部实现了一个服务实例懒加载的迭代器;迭代器内部通过classLoader读 取对应META-INF/service/文件夹下的服务配置文件获取到所有的实现类类名称,当通过iterator()方法获取迭代器后,就可以依次实例化service的实现并将实现对象加入到缓存中。 3.解析配置文件的过程就是按行读取,每一行的文本都是一个服务实现类的全限定名,获取到类名就可以通过反射实例化对象了

    publicfinalclassServiceLoaderimplementsIterable{//Service配置文件的资源路径privatestaticfinalString PREFIX ="META-INF/services/";// The class or interface representing the service being loadedprivatefinalClass service;// 负责service配置资源加载,实例化service privatefinalClassLoader loader;// 服务实例的缓存,已被迭代被创建过的会加到这个cache中privateLinkedHashMap providers =newLinkedHashMap<>();privateServiceLoader(Class<S> svc, ClassLoader cl){        service = Objects.requireNonNull(svc,"Service interface cannot be null");        loader = (cl ==null) ? ClassLoader.getSystemClassLoader() : cl;        reload();    }//重载load, 删除缓存并重新实例化iteratorpublicvoidreload(){        providers.clear();        lookupIterator =newLazyIterator(service, loader);    }//懒加载的service迭代器privateclassLazyIteratorimplementsIterator{        Class service;        ClassLoader loader;        Enumeration configs =null;        Iterator pending =null;        String nextName =null;privateLazyIterator(Class<S> service, ClassLoader loader){this.service = service;this.loader = loader;        }privatebooleanhasNextService(){if(nextName !=null) {returntrue;            }//读取配置文件,最终转换成一个配置文件内容元素迭代器if(configs ==null) {try{                    String fullName = PREFIX + service.getName();if(loader ==null)                        configs = ClassLoader.getSystemResources(fullName);elseconfigs = loader.getResources(fullName);                }catch(IOException x) {                    fail(service,"Error locating configuration files", x);                }            }while((pending ==null) || !pending.hasNext()) {if(!configs.hasMoreElements()) {returnfalse;                }                pending = parse(service, configs.nextElement());            }//获取配置文件中的下一个元素nextName = pending.next();returntrue;        }privateSnextService(){if(!hasNextService())thrownewNoSuchElementException();            String cn = nextName;            nextName =null;            Class c =null;try{                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{                S p = service.cast(c.newInstance());                providers.put(cn, p);returnp;            }catch(Throwable x) {                fail(service,"Provider "+ cn +" could not be instantiated",                    x);            }thrownewError();// This cannot happen}publicbooleanhasNext(){returnhasNextService();        }publicSnext(){returnnextService();        }    }//按行读取文件,每一行都是服务实现类的接口名privateIteratorparse(Class<?> service, URL u)throwsServiceConfigurationError{            InputStream in =null;            BufferedReader r =null;            ArrayList names =newArrayList<>();try{                in = u.openStream();                r =newBufferedReader(newInputStreamReader(in,"utf-8"));intlc =1;while((lc = parseLine(service, u, r, lc, names)) >=0);            }catch(IOException x) {                fail(service,"Error reading configuration file", x);            }finally{try{if(r !=null) r.close();if(in !=null) in.close();                }catch(IOException y) {                    fail(service,"Error closing configuration file", y);                }            }returnnames.iterator();        }}复制代码

    Google autoService

    以上 当注册服务实现时如果需要手动创建文件并写入服务实现类名称 难免有些繁琐,我们可以使用谷歌提供的 AutoService 库简化这一过程

    使用方式

    gradle 引入autoService

    dependencies{compileOnly'com.google.auto.service:auto-service:1.0-rc2'annotationProcessor'com.google.auto.service:auto-service:1.0-rc2'}复制代码

    服务实现类上加上@AutoService注解,参数为服务抽象类

    packagecom.knight.serviceimpl;importcom.google.auto.service.AutoService;importcom.knight.PaymentService;@AutoService(PaymentService.class)publicclassPaymentServiceImplimplementsPaymentService{@Overridepublicvoidpay(String productName,doubleprice){        System.out.println("支付模块:购买产品 "+productName +",价格"+price);    }}复制代码

    3.项目编译触发auto-service注解处理过程后自动生成了配置文件

    小编整理了一些java进阶学习资料和面试题,需要资料的请加JAVA高阶学习Q群:664389243 这是小编创建的java高阶学习交流群,加群一起交流学习深造。群里也有小编整理的2019年最新最全的java高阶学习资料!

    相关文章

      网友评论

        本文标题:Java&Android开发-浅析ServiceLoad

        本文链接:https://www.haomeiwen.com/subject/geikyqtx.html