从JMS标准看接口编程、模块整合及相关技术jndi,spi等
程序员文章站
2022-07-14 07:54:59
...
最近看了一篇微信上的文章:是 【码农翻身】公众号的刘欣作者写的Java帝国之JMS的诞生。最近恰好关注到支付宝的开源分布式消息中间件–Metamorphosis(MetaQ)(现在这个项目改名为RocketMQ)非常想看一下源码,但我知道阿里的一般比较复杂,所以从简单的activeMQ开始,当然这个JMS标准产生过程看看更好。
http://mp.weixin.qq.com/s/3b66QhyPl9sEkCnbgedF2g
一、文章的内容简介:
简述一下:JMS就是java消息服务,用于不同中模块对象之间的信息交互。比如其中之一P2P,就是一个发送消息,一个接收消息,就结束了,常用在异步处理时,不需要立即由接收方返回结果时(当然异步转同步也是可以的,发送方如果wait(),线程等待结果时,比如DUBBO的同步调用);还有一个主题订阅模式,就是一个人发消息,所以订阅过的人都可以收到消息。
但是很多厂商已经设计并开发了不同的消息中间件,那么,如果要对不同的产品进行统一标准,要需要对模块接口进行编程,厂商只是实现具体的功能。而模块接口正如文章中村长说:“不要被纷繁的现象迷住了双眼, 要看透背后的本质, 做出适当的抽象才可以。”。最后只剩下了一些:工厂queueConnFactory、队列queue、连接connect、交互session、生产者producer、消费者consumer、消息msg这些基本的概念产生的接口对象。这样基本的JMS标准就有了。这些接口只是一个架构,真正的实现是不同提供商提供的。
众多产品上升到标准的过程中,如果兼顾这么多产品呢?文章中提到的是JNDI(Java命名和目录接口)技术。这个技术我们一般会在配置数据库的时候会用到,比如配置JNDI数据源。但一个技术产生一定是有原因的,如果知道了原因,就知道如何在恰当的时候使用,以及处理问题的时候想到。
二、JNDI的例子:
JNDI简单的说可以有一个配置文件,那么就可以按名字从库中找到相应的对象。例子也是网上找的。比如我希望传一个String的地址,返回一个URL对象(或者实现某接口的对象),可以这样。
1.一个配置文件。有一个name,用来根据名称找对象。有一个type,表示找到的对象是什么接口的对象。还有一个factory后面要实现的,用来产生所要的对象。最后有一个url就是factory所要的相关参数。
<?xml version='1.0' encoding='utf-8'?>
<Context>
<Resource name="test/url"
auth="Container"
type="java.net.URL"
factory="com.tomcat.jndi.MyURLFactory"
url="file:///c:/test.properties"/>
</Context>
2.生产所要的对象的类的代码。
public Object getObjectInstance(Object obj, Name name, Context nameCtx,Hashtable environment) throws Exception {
URL url = null;
Reference ref = (Reference) obj;
Enumeration<RefAddr> cfgAttrs = ref.getAll();//这个是取配置文件的所有属性。
while (cfgAttrs.hasMoreElements()) {//循环查看属性
RefAddr cfgAttr = cfgAttrs.nextElement();
String attrName = cfgAttr.getType();//属性的名称
String attrValue = (String) cfgAttr.getContent();//属性的内容
if ("url".equals(attrName)) {
url = new URL(attrValue);//如果找到url属性,就用这个地址产生一个URL对象。当然实际使用中,可以new出最后的对象,设置相关的属性。
}
}
return url;//最终返回产生的对象。
}
3.使用
Context ctxt = new InitialContext();
Context envCtxt = (Context) ctxt.lookup("java:comp/env/");
URL url = (URL) envCtxt.lookup("test/url");//配置中的对象的名字,找到需要的对象。
4. 例子很简单,我们常用的JNDI数据源也是这样,从中可以看出,jndi就是配置后,不用使用基于Class.forName("xxx")的方式来装载类了,把装载过程从硬编码中移到配置中,这样就可以动态整合不同的模块功能了。
在我之前做过的项目中,也有对接口编程的例子,看到过使用数据库记录相关接口的实现类,并有名称,到时按条件查到后用Class.forName,也是一种动态。
三、目前见的比较多的SPI(Service Provider Interface)机制
除了上面说过JDNI外,我在DUBBO中看到了很多使用SPI的地方,DUBBO就是微核心设计,所以关注与接口整合功能模块,而且spi用的很复杂,加载的不一定是实际类,而是动态代理类。
当产生接口框架后,厂商提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
同样基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。
简单的例子可以看:http://singleant.iteye.com/blog/1497259
阿里的DUBBO中全部用的是SPI。
四、总结
从上面的jms标准到JNDI与SPI的机制可以看到,对于一个产品来说,模块之间的功能关联应该通过接口来实现,而模块内部不一定都要写在接口。而我们想设计一个产品的时候,通过也是抽象的概念,用接口分离出整合关系,这样的产品才是一个微核心,可插拔的产品,更具灵活性。模块之间是通过JNDI或者SPI机制产生具体的实现类。
平时写代码,通常照猫画虎,ACTION/SERVICE/DAO都写成interface与impl方式。通过上面的介绍,更能理解接口的好处以及通过接口整合模块功能的方式,在开发中也不一定都弄成接口与实现分离的方式吧。
如果要区分一下什么时候用JNDI,什么时候用SPI。不妨考虑一下熟悉的JNDI的数据源是不是可以用SPI来实现?而SPI的东西是不是可以用JNDI来实现呢?初步感觉JNDI多了配置文件,而且数据源中的很多内容是标准中定义的接口,比如datasource,connection,recordset等等,所以说这种方式已经深入到很多内部的协作了,文章开关讲的JMS用JDNI也是浸入到内部的类的协作了。而用SPI,很多时候只是对外的一个接口的实现,并不是很浸入到实现的模块内部的关系。至于其它的方面还需要以后体会...
五、预告
前阵了研究了一下Apache commons fileupload的源码,特别是处理文件上传时写的非常好,一边读取流,一边产生内部类的iterator对象,并按boundery分段把输入流写到不同的输出流。下次再写...
http://mp.weixin.qq.com/s/3b66QhyPl9sEkCnbgedF2g
一、文章的内容简介:
简述一下:JMS就是java消息服务,用于不同中模块对象之间的信息交互。比如其中之一P2P,就是一个发送消息,一个接收消息,就结束了,常用在异步处理时,不需要立即由接收方返回结果时(当然异步转同步也是可以的,发送方如果wait(),线程等待结果时,比如DUBBO的同步调用);还有一个主题订阅模式,就是一个人发消息,所以订阅过的人都可以收到消息。
但是很多厂商已经设计并开发了不同的消息中间件,那么,如果要对不同的产品进行统一标准,要需要对模块接口进行编程,厂商只是实现具体的功能。而模块接口正如文章中村长说:“不要被纷繁的现象迷住了双眼, 要看透背后的本质, 做出适当的抽象才可以。”。最后只剩下了一些:工厂queueConnFactory、队列queue、连接connect、交互session、生产者producer、消费者consumer、消息msg这些基本的概念产生的接口对象。这样基本的JMS标准就有了。这些接口只是一个架构,真正的实现是不同提供商提供的。
众多产品上升到标准的过程中,如果兼顾这么多产品呢?文章中提到的是JNDI(Java命名和目录接口)技术。这个技术我们一般会在配置数据库的时候会用到,比如配置JNDI数据源。但一个技术产生一定是有原因的,如果知道了原因,就知道如何在恰当的时候使用,以及处理问题的时候想到。
二、JNDI的例子:
JNDI简单的说可以有一个配置文件,那么就可以按名字从库中找到相应的对象。例子也是网上找的。比如我希望传一个String的地址,返回一个URL对象(或者实现某接口的对象),可以这样。
1.一个配置文件。有一个name,用来根据名称找对象。有一个type,表示找到的对象是什么接口的对象。还有一个factory后面要实现的,用来产生所要的对象。最后有一个url就是factory所要的相关参数。
<?xml version='1.0' encoding='utf-8'?>
<Context>
<Resource name="test/url"
auth="Container"
type="java.net.URL"
factory="com.tomcat.jndi.MyURLFactory"
url="file:///c:/test.properties"/>
</Context>
2.生产所要的对象的类的代码。
public Object getObjectInstance(Object obj, Name name, Context nameCtx,Hashtable environment) throws Exception {
URL url = null;
Reference ref = (Reference) obj;
Enumeration<RefAddr> cfgAttrs = ref.getAll();//这个是取配置文件的所有属性。
while (cfgAttrs.hasMoreElements()) {//循环查看属性
RefAddr cfgAttr = cfgAttrs.nextElement();
String attrName = cfgAttr.getType();//属性的名称
String attrValue = (String) cfgAttr.getContent();//属性的内容
if ("url".equals(attrName)) {
url = new URL(attrValue);//如果找到url属性,就用这个地址产生一个URL对象。当然实际使用中,可以new出最后的对象,设置相关的属性。
}
}
return url;//最终返回产生的对象。
}
3.使用
Context ctxt = new InitialContext();
Context envCtxt = (Context) ctxt.lookup("java:comp/env/");
URL url = (URL) envCtxt.lookup("test/url");//配置中的对象的名字,找到需要的对象。
4. 例子很简单,我们常用的JNDI数据源也是这样,从中可以看出,jndi就是配置后,不用使用基于Class.forName("xxx")的方式来装载类了,把装载过程从硬编码中移到配置中,这样就可以动态整合不同的模块功能了。
在我之前做过的项目中,也有对接口编程的例子,看到过使用数据库记录相关接口的实现类,并有名称,到时按条件查到后用Class.forName,也是一种动态。
三、目前见的比较多的SPI(Service Provider Interface)机制
除了上面说过JDNI外,我在DUBBO中看到了很多使用SPI的地方,DUBBO就是微核心设计,所以关注与接口整合功能模块,而且spi用的很复杂,加载的不一定是实际类,而是动态代理类。
当产生接口框架后,厂商提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
同样基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。
简单的例子可以看:http://singleant.iteye.com/blog/1497259
阿里的DUBBO中全部用的是SPI。
四、总结
从上面的jms标准到JNDI与SPI的机制可以看到,对于一个产品来说,模块之间的功能关联应该通过接口来实现,而模块内部不一定都要写在接口。而我们想设计一个产品的时候,通过也是抽象的概念,用接口分离出整合关系,这样的产品才是一个微核心,可插拔的产品,更具灵活性。模块之间是通过JNDI或者SPI机制产生具体的实现类。
平时写代码,通常照猫画虎,ACTION/SERVICE/DAO都写成interface与impl方式。通过上面的介绍,更能理解接口的好处以及通过接口整合模块功能的方式,在开发中也不一定都弄成接口与实现分离的方式吧。
如果要区分一下什么时候用JNDI,什么时候用SPI。不妨考虑一下熟悉的JNDI的数据源是不是可以用SPI来实现?而SPI的东西是不是可以用JNDI来实现呢?初步感觉JNDI多了配置文件,而且数据源中的很多内容是标准中定义的接口,比如datasource,connection,recordset等等,所以说这种方式已经深入到很多内部的协作了,文章开关讲的JMS用JDNI也是浸入到内部的类的协作了。而用SPI,很多时候只是对外的一个接口的实现,并不是很浸入到实现的模块内部的关系。至于其它的方面还需要以后体会...
五、预告
前阵了研究了一下Apache commons fileupload的源码,特别是处理文件上传时写的非常好,一边读取流,一边产生内部类的iterator对象,并按boundery分段把输入流写到不同的输出流。下次再写...