- 浏览: 39583 次
- 性别:
- 来自: 北京
最新评论
概述:
前端时间看了一下dubbo源码被它使用的基于SPI(service provider interface)开发模式所吸引,这种方式组织的程序可以方便dubbo使用者自己扩展和实现自己的插件。
废话不多说了,讲代码吧。
开发过dubbo过滤器的同学应该很熟悉这种配置,在“classpath/services/接口全名”有一个文件用于定义该接口的所有实现类。并且在配置文件中加入自己配置的名字就可以用了。
这里我模仿这种方式使用反射机制创建了这些服务实现,并供系统通过名字定位需要使用的服务具体实现。没什么太难的东西大家看看就知道了。
ExtensionServiceLoader工具类的实现:
该类中loadExtensionClasses为核心方法。他首先会加载了所有SPI类文件,然后解析文件中的定义键值对,最后生成class的map供以后代码使用。代码中有注释可以参考这里就不多说。
SPI 注解:
所有需要定位为spi的接口都加上这个注解,并且可以给一个默认的实现名称
使用方式:
下面是我自己测试的一些代码。
主要逻辑为首先要创建业务接口(IEcho)并添加@SPI注解,然后为他添加两个具体的实现(Echo1、Echo2)这样就定义好了我们的SPI服务。接下来就需要根据名字调用具体的服务。
将服务实现配置到到配置文件中。(jdk也有一个实现叫serviceLoader应该具体自己百度一下。jdk使用的方式就是将接口名作为文件名然后文件列表中包含该接口的实现列表分别以name=classFullName)先入为主我也使用的这种方式配置服务,下面为具体测试代码。
IEcho 一个具体的业务接口:
注意SPI注解
IEcho具体实现类:
配置文件:
文件路径一般为 classes\META-INF\services\org.qhy.spi.api.IEcho
测试代码:
前端时间看了一下dubbo源码被它使用的基于SPI(service provider interface)开发模式所吸引,这种方式组织的程序可以方便dubbo使用者自己扩展和实现自己的插件。
废话不多说了,讲代码吧。
开发过dubbo过滤器的同学应该很熟悉这种配置,在“classpath/services/接口全名”有一个文件用于定义该接口的所有实现类。并且在配置文件中加入自己配置的名字就可以用了。
这里我模仿这种方式使用反射机制创建了这些服务实现,并供系统通过名字定位需要使用的服务具体实现。没什么太难的东西大家看看就知道了。
ExtensionServiceLoader工具类的实现:
该类中loadExtensionClasses为核心方法。他首先会加载了所有SPI类文件,然后解析文件中的定义键值对,最后生成class的map供以后代码使用。代码中有注释可以参考这里就不多说。
package org.qhy.spi.pkg.anonation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface SPI { String value(); }
SPI 注解:
所有需要定位为spi的接口都加上这个注解,并且可以给一个默认的实现名称
package org.qhy.spi.pkg.internal; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.Enumeration; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.qhy.spi.pkg.anonation.SPI; /** * ClassName: org.qhy.spi.pkg.internal.ExtensionServiceLoader <br/> * Description: spi服务接口加载类实现. <br/> */ public class ExtensionServiceLoader<T> { private static final String SERVICES_DIRECTORY = "META-INF"+File.separator+"services"+File.separator; //存放不同类型的loader private static final ConcurrentMap<Class<?>, ExtensionServiceLoader<?>> loaderMap = new ConcurrentHashMap<Class<?>, ExtensionServiceLoader<?>>(); //存放不同实例 private final ConcurrentMap<String, Object> instanceMap = new ConcurrentHashMap<String, Object>(); //加载不同的所有的实现的类定义 private final ConcurrentMap<String, Class<?>> extensionClassesMap = new ConcurrentHashMap<String, Class<?>>(); private Class<?> type; /** * Description: . <br/> * @param extensionType * @return * @throws Exception */ public static <T> ExtensionServiceLoader<T> getServiceLoader(Class<?> extensionType) throws Exception { if(extensionType == null){ throw new IllegalArgumentException("extensionType is null!"); } if(!extensionType.isAnnotationPresent(SPI.class)){ throw new IllegalArgumentException("extensionType ("+extensionType+")Invalid extension,because: No annotations (@"+SPI.class.getSimpleName()+")!"); } //从map中获取 ExtensionServiceLoader<T> serviceLoader = (ExtensionServiceLoader<T>) loaderMap.get(extensionType); if(serviceLoader == null){ try { serviceLoader = new ExtensionServiceLoader<T>(extensionType); } catch (Exception e) { throw e; } loaderMap.put(extensionType, serviceLoader); } return serviceLoader; } public T getServiceInstance(String name) throws Exception{ if(name == null || name.trim().length()==0){ throw new IllegalArgumentException("name is null!"); } //从map中取实例如果取不到 就创建病存放到map中 T t = (T)instanceMap.get(name); if(t == null){ Class<?> clazz = extensionClassesMap.get(name); if(clazz == null){ throw new IllegalArgumentException("name:["+name+"] not defination in file("+SERVICES_DIRECTORY+type.getName()+")"); } try { t = (T)clazz.newInstance(); } catch (Exception e) { throw e; } instanceMap.putIfAbsent(name, t); } return t; } public T getDefaultInstance() throws Exception{ SPI spi = type.getAnnotation(SPI.class); return this.getServiceInstance(spi.value()); } private ExtensionServiceLoader(Class<?> extensionType) throws IllegalStateException, ClassNotFoundException, IOException { this.type = extensionType;//该loader对应的类型-一接口一loader this.loadExtensionClasses(extensionType); } /** * * Description: 加载不同的类定义. <br/> * @param extensionType 扩展类型 * @throws ClassNotFoundException * @throws IOException * @throws IllegalStateException */ private void loadExtensionClasses(Class<?> extensionType) throws ClassNotFoundException, IOException,IllegalStateException{ Enumeration<java.net.URL> urls; //文件名 也就是接口名 String fileName = SERVICES_DIRECTORY+extensionType.getName(); ClassLoader classLoader = ExtensionServiceLoader.class.getClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); classLoader = ClassLoader.getSystemClassLoader(); } if (urls != null && urls.hasMoreElements()) { while (urls.hasMoreElements()) { java.net.URL url = urls.nextElement(); BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8")); String line = null; try{ while ((line = reader.readLine()) != null) { if(line == null || line.trim().length()==0 || line.contains("#")){ continue; } //读取文件一行定义并处理 line =line.trim(); String[] defArray = line.split("="); if(defArray.length != 2){ continue; } //拆分文件,大一部分是名字,第二部分是类全名 String name = defArray[0],className=defArray[1]; Class<?> clazz =Class.forName(className,true,classLoader); //判断文件定义的类,是不是加载接口的子类 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("class line defination [" + className + "] not an subType of("+type.getName()+")"); } extensionClassesMap.put(name,clazz); } }finally{ reader.close(); } } } } }
使用方式:
下面是我自己测试的一些代码。
主要逻辑为首先要创建业务接口(IEcho)并添加@SPI注解,然后为他添加两个具体的实现(Echo1、Echo2)这样就定义好了我们的SPI服务。接下来就需要根据名字调用具体的服务。
将服务实现配置到到配置文件中。(jdk也有一个实现叫serviceLoader应该具体自己百度一下。jdk使用的方式就是将接口名作为文件名然后文件列表中包含该接口的实现列表分别以name=classFullName)先入为主我也使用的这种方式配置服务,下面为具体测试代码。
IEcho 一个具体的业务接口:
注意SPI注解
package org.qhy.spi.api; import org.qhy.spi.pkg.anonation.SPI; /** * @author qihuiyong * */ @SPI(value = "echo1") public interface IEcho { public void echo(); }
IEcho具体实现类:
/********************************实现1******************************************/ package org.qhy.spi.impl; import org.qhy.spi.api.IEcho; public class Echo1 implements IEcho { @Override public void echo() { System.out.println("echo:Opration_11111111"); } } /********************************实现2******************************************/ package org.qhy.spi.impl; import org.qhy.spi.api.IEcho; public class Echo2 implements IEcho { @Override public void echo() { System.out.println("echo:Opration_2222222222222222"); } }
配置文件:
文件路径一般为 classes\META-INF\services\org.qhy.spi.api.IEcho
echo1=org.qhy.spi.impl.Echo1 echo2=org.qhy.spi.impl.Echo2 #echo3=org.qhy.spi.impl.Echo3 #exe=org.qhy.spi.impl.Execute2
测试代码:
package org.qhy.spi; import org.qhy.spi.api.IEcho; import org.qhy.spi.pkg.internal.ExtensionServiceLoader; public class Test { public static void main(String[] args) throws Exception { ExtensionServiceLoader<IEcho> serviceLoader = ExtensionServiceLoader.getServiceLoader(IEcho.class); IEcho echo2 =serviceLoader.getServiceInstance("echo2"); IEcho defaultEcho =serviceLoader.getDefaultInstance(); echo2.echo(); defaultEcho.echo(); } }
发表评论
-
javassi实现t动态代理模式
2016-09-08 15:08 448最近研究了一下javassist框架,目前我对它的理解是它是一 ... -
JSP COOKIE使用
2015-12-05 16:08 478原来一直在想要实现可 ... -
Jedis事务用法
2015-11-25 20:06 1716今天同事问了一个redis的问题,主要就是出现错误之后就把所有 ... -
zookeeper笔记
2015-11-24 08:17 278这个笔记是2013年初写的,当时学习solr发现solr使用了 ... -
jdk1.7安装之后切换不回去
2015-11-02 20:03 596错误现象: 在win7上安装过jdk1.7之后,然后把环境变量 ... -
hadoopMapReduce小例子
2015-03-28 21:08 455今天做了一个简单的mapreduce小程序,目的是熟悉一下怎么 ... -
VMware虚拟机搭建Hadoop集群
2014-12-05 22:07 946最近搭建了hadoop2.5的集群,在这里分享一下希望能帮助到 ... -
Hibernate代码生成插件(eclipse)
2014-05-04 12:31 796阅读对象: 有一些hibernate使用经验的开发人员; 介绍 ... -
eclipse文件同步插件
2014-04-29 17:41 608clipse同步插件用于指定两个目录(源目录和目标目录)做文件 ... -
修改WAS(WebSpehre)默认编码和jvm内存申请
2014-04-28 17:09 1073登陆WAS管理控制台,打开:服务器->应用程序服务器-& ... -
Apache HttpClient 代理、登陆访问百度开放平台
2014-04-23 17:50 806最近做了下使用httpclient登陆百度开放平台获取G ... -
windows下编写mapreduce程序
2014-04-20 09:51 747配置linux的hadoop环境比较繁琐,为了方便的编写 ... -
RMI随机生成端口解决方法(结合spring)
2014-04-20 08:20 2908RMI有一个网络端口和一个数据端口,网络端口我们在程序里 ... -
WAS(WebSphere)修改端口
2014-04-20 07:58 39921、修改管理页面端口(默认:9060) a>进入控制台页 ...
相关推荐
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类。...
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类。...
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...
anylog 是一个可以在代码的任意区域无入侵地加入日志的工具,适用于线上问题排查。 anylog 为开发人员提供一个易于使用的平台,帮助开发... 如果要深入了解spi机制,请自行google:java spi 标签:anylog
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...
对于UI/UX设计,有界面设计工具,如Sketch、Adobe XD,可以帮助设计师快速构建应用程序界面模型,并生成规范的设计稿供开发人员参考实现。 跨平台支持: 跨平台开发工具如Xamarin、React Native和Flutter,让...
中文版,平时开发超实用工具。 Java 2 Platform 软件包 java.applet 提供创建 applet 所必需的类和 applet 用来与其 applet 上下文通信的类。...java.lang 提供利用 Java 编程语言进行程序设计的基础类。 ......
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类。...
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...
java对音乐文件的操作的jar工具类,含有三个jl MP3 tritonus
增加:JCTPStructUtil工具类 修正:交易API部分函数出现空指针异常的问题 修正:行情API部分函数出现空指针异常的问题 变更:发布包中增加JCTP.jar *********************************************** JCTP 0.3.1 ...
javaee 6 规范 chm版本 第1章 引言 1.1 感谢 1.2 版本1.3的感谢 1.3 版本1.4的感谢 ...13.1 JNLP(Java Web Start) 13.2 Java EE SPI 附录 附录A 早期版本的部署描述符 附录B 修订历史 科瑞网酷
javax.sql.rowset.serial 提供实用工具类,允许 SQL 类型与 Java 编程语言数据类型之间的可序列化映射关系。 javax.sql.rowset.spi 第三方供应商在其同步提供者的实现中必须使用的标准类和接口。 javax.swing 提供...
java工具源码 什么是阿斯特拉? Astra是用于分析和重构Java源代码的Java工具。 例如: “对类型A的引用应改为对类型B的引用” “方法A的调用者应添加一个附加参数B ” “查找带有A注释的类” 阿斯特拉(Astra)已在...