EJB3学习笔记_Message-Driven Bean

<p><span style="font-size: medium;"><strong>Why Messageing</strong></span></p>
<p></p>
<p>1、Asynchrony(异步)</p>
<ul>
<li>A typical RMI-IIOP(Session Bean) client must wait while the server performs its processing</li>
</ul>
<p>2、Decoupling(解耦)</p>
<ul>
<li>An RMI-IIOP client has to know the individual servers it wants to use</li>
</ul>
<p>3、Reliability(可靠)</p>
<ul>
<li>When an RMI--IIOP client calls the server, the latter has to be running</li>
</ul>
<p>4、Support for multiple senders and receivers(多点发送,多点接受)</p>
<ul>
<li>RMI-IIOP limits you to a single client talking to a single server at any given time</li>
</ul>
<p></p>
<p><span style="color: #0000ff;">面向消息编程也可以说时一种SOA的实现。</span></p>
<p></p>
<p><span style="color: #0000ff;">中间通过消息中间件,缓存和分发消息。</span></p>
<p><img src="http://hi.csdn.net/attachment/201005/5/0_1273073061TgtL.gif" alt="" width="358" height="200"></p>
<p></p>
<p></p>
<p></p>
<p><span style="font-size: medium;"><strong>Message Oriented Middleware(MOM)</strong></span></p>
<p></p>
<p></p>
<p>1、MOM is a term used to refer to any infrastructure that supports messaging</p>
<p>2、MOM products</p>
<ul>
<li>IBM WebSphere MQ</li>
<li>BEA tuxedo/Q</li>
<li>Microsoft MSMQ</li>
<li>Sun Java System Messaging Server</li>
<li>Tibco Rendezvous</li>
<li>Sonic Software SonicMQ</li>
<li>FioranoMQ</li>
</ul>
<p></p>
<p></p>
<p><span style="font-size: medium;"><strong>The Java Message Service(JMS)</strong></span></p>
<p></p>
<p>目标:使用统一的API 去访问不同的消息服务中间件</p>
<p>1、Is a messaging standard designed to eliminate many of the disadvantages that MOM-based products faced</p>
<p>2、Has two parts</p>
<ul>
<li>An API</li>
</ul>
<p> we use it to write code to send and receive messages</p>
<ul>
<li>A Service Provider Interface(SPI)</li>
</ul>
<p>Plug in JMS providers</p>
<p> A JMS provider knows how to talk to a specific MOM implementation</p>
<p></p>
<p></p>
<p></p>
<p><span style="font-size: medium;"><strong>Messaging Domains</strong></span></p>
<p></p>
<p>消息的两种方式</p>
<p>1、发布/订阅 的方式 , 基于主题的</p>
<ul>
<li>允许多个接受者,类似于广播的方式</li>
<li>生产者将消息发送到主题上(Topic)</li>
<li>接受者必须先订阅</li>
</ul>
<p>2、点对点 的方式 , 基于队列的</p>
<ul>
<li>一个消息只能被一个接受者接受一次</li>
<li>生产者把消息发送到队列中(Queue),这个队列可以理解为电视机频道(channel)</li>
<li>在这个消息中间件上有多个这样的channel</li>
<li>接受者无需订阅,当接受者未接受到消息时就会处于阻塞状态</li>
</ul>
<p></p>
<p></p>
<p><img src="http://hi.csdn.net/attachment/201005/5/0_127307393959my.gif" alt="" width="532" height="348"></p>
<p></p>
<p></p>
<p></p>
<p><span style="font-size: medium;"><strong>Client View of a JMS System</strong></span></p>
<p></p>
<ol>
<li>获得JMS驱动(封装了与特定消息服务器交互的协议)(类似JDBC驱动),j2ee服务器会把驱动绑定到JNDI上,可以以此获得</li>
<li>通过工厂创建连接 Connection ,代表到特定目标的一个链路(也和JDBC是一码事)。</li>
<li>创建会话 Session , 每个会话都代表一个事务。(就比如多线程,大家共用一个Connection,但Session都是自己的)</li>
<li>从JNDI中查询 生产者消息发送的目的地,消费者消费的来源,这些目标信息。(所以需要在应用服务器中配置好这些目标)</li>
<li>创建基于当前会话的 生产者和消费者。(JMS Producer Or JMS Consumer),创建时需要参数</li>
<li>发送和接受消息(send or receive message)</li>
</ol>
<p></p>
<p><img src="http://hi.csdn.net/attachment/201005/5/0_12730746011S80.gif" alt="" width="584" height="394"></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p><span style="font-size: medium;"><strong>使用Jboss部署 Topic/Queue</strong></span></p>
<p></p>
<p></p>
<p>1、打开Jboss控制台(<a href="http://localhost:8080/jmx-console/">http://localhost:8080/jmx-console/</a>)</p>
<p>2、找到域 jboss.mq , 找到服务 service=DestinationManager(目标管理器)</p>
<p>3、这个bean提供了创建队列和主题的方法(createQueue()和createTopic()),都需要2个参数,一个名字,一个JNDI名字</p>
<p></p>
<p>例子:创建队列</p>
<p><img src="http://hi.csdn.net/attachment/201005/6/0_127316096362Nx.gif" alt="" width="366" height="135"></p>
<p></p>
<p>可以再控制台查看到此队列:(这些都是临时的,重启服务后就没了,不过可以通过配置文件预先创建)</p>
<p><img src="http://hi.csdn.net/attachment/201005/6/0_127316116151RL.gif" alt=""></p>
<p></p>
<p></p>
<p><span style="font-size: medium;"><strong>通过客户端去连接</strong></span></p>
<p></p>
<p><strong>点对点的</strong></p>
<p></p>
<p>创建生产者:</p>
<p><textarea cols="80" rows="15" name="code" class="java:collapse">import javax.jms.*;
import javax.naming.*;

public class Producer{
public static void main(String[] args)throws NamingException{
Context ctx = new InitialContext();
ConnectionFactory cf = (ConnectonFactory)ctx.lookup("ConnectionFactory");//名字在Jboss中缺省就叫ConnectionFactory
Destination des = (Destination)ctx.lookup("queue/Testqueue");//查询目标
Connection con = cf。createConnection();//创建连接
Session session = con.createSession(false,Session.AUTO_ACKNOWLEDGE);/*创建会话,第一个参数标示是否使用事务,第二个表示自动通知,指收到消息后自动恢复消息中间件,中间件就会认为你收到了然后把消息删除,如果不是自动通知则意味可以重复接受消息,在这里是生产者,这个参数没有意思,但API如此,只能写。*/

MessageProducer producer = session.createProducer(des);//创建消息生产者,传入基于的目标
TextMessage msg = session.createTextMessage();//创建message,这里TextMessage是一个子类。
msg.setText("clat");
producer.send(mgs);
con.close();
System.out.println("over!");
}
}</textarea></p>
<p></p>
<p></p>
<p>创建消费者:</p>
<p></p>
<p><textarea cols="81" rows="15" name="code" class="java:collapse">import javax.jms.*;
import javax.naming.*;

public class Producer{
public static void main(String[] args)throws NamingException{
Context ctx = new InitialContext();
ConnectionFactory cf = (ConnectonFactory)ctx.lookup("ConnectionFactory");//名字在Jboss中缺省就叫ConnectionFactory
Destination des = (Destination)ctx.lookup("queue/Testqueue");//查询目标
Connection con = cf。createConnection();//创建连接
Session session = con.createSession(false,Session.AUTO_ACKNOWLEDGE);/*创建会话,第一个参数标示是否使用事务,第二个表示自动通知,指收到消息后自动恢复消息中间件,中间件就会认为你收到了然后把消息删除,如果不是自动通知则意味可以重复接受消息。*/

MessageConsumer consumer = session.createConsumer(des);
con.start();//通知消息中间件,准备就绪,可以分发消息了
Message message = consumer.receive();
if(message instanceof TextMessage){
TextMessage msg = (Textmessage)message;
String text = msg.getText();
System.out.println(text);
}
con.close();
}
}</textarea></p>
<p></p>
<ul>
<li>先起消费者会进入阻塞,直到生产者放入消息后,消费者取消息,然后退出</li>
<li>队列中的消息只能接受一次,下次消费端接收会进入阻塞,直到有新消息进来</li>
</ul>
<p>如果要改写为基于主题的 ,只需要修改Destination,改目标位主题就行</p>
<ul>
<li>先起生产者,由于没有预定(没有消费者监听),没有任何作用。所以需要先预定,再分发。</li>
<li>先起消费者,消费者阻塞,直到对应目标中有生产者放入消息,可以有多个消费者。</li>
<li>
<span style="color: #3366ff;">持久化订阅者</span>:特殊的消费者,告诉主题,我一直订阅着,即使网络断开,消息服务器也记住所有持久化订阅者,如果有新消息,也会知道必定有人回来消费。</li>
</ul>
<p></p>
<p>这里消费者,可以以另一种方式,监听器来完成消费消息,等于把消息当成一个事件处理,代码如下:</p>
<p></p>
<p><textarea cols="79" rows="15" name="code" class="c-sharp:collapse">public class MyMessageListener implements MessageListener{
public void onMessage(Message message){
if(message instanceof TextMessage){
TextMessage msg = (TextMessage) message;
String text;
try{
text = msg.getText();
System.out.println(text);
}catch(JMSException e){
e.printStackTrace();
}
}
}
}

public class Producer{
public static void main(String[] args)throws NamingException{
Context ctx = new InitialContext();
ConnectionFactory cf = (ConnectonFactory)ctx.lookup("ConnectionFactory");//名字在Jboss中缺省就叫ConnectionFactory
Destination des = (Destination)ctx.lookup("queue/Testqueue");//查询目标
Connection con = cf。createConnection Session session = con.createSession(false,Session.AUTO_ACKNOWLEDGE
MessageConsumer consumer = session.createConsumer(des);
con.start();
// Message message = consumer.receive();
// if(message instanceof TextMessage){
// TextMessage msg = (Textmessage)message;
// String text = msg.getText();
// System.out.println(text);
// }
// 注册消息监听器,他会单独起一个线程监听消息,主线程不会阻碍
consumer.setMessageListemer(new MyMessageListener())
//这里 就不能关闭连接了,因为监听器还在监听。
// con.close();
}
}</textarea></p>
<p></p>
<p></p>
<p><strong><span style="font-size: medium;">在服务器中查看发送后在队列中缓存的消息</span></strong></p>
<p></p>
<p>1、以jboss为例,在目标队列中,找到对应方法。</p>
<p><img src="http://hi.csdn.net/attachment/201005/30/0_1275232464JJau.gif" alt=""></p>
<p></p>
<p>该方法,返回所有在当前队列中被缓存的方法:</p>
<p></p>
<p><img src="http://hi.csdn.net/attachment/201005/30/0_1275232638zXWH.gif" alt=""></p>
<p></p>
<p>例子,当前队列中只有一条消息,如下:</p>
<p>Header:消息头</p>
<p>Body:消息体</p>
<p><img src="http://hi.csdn.net/attachment/201005/30/0_1275232735j8M6.gif" alt=""></p>
<p></p>
<p></p>
<p></p>
<p><strong><span style="font-size: medium;">消息的类型</span></strong></p>
<p></p>
<p>主要有以下几类:</p>
<p><img src="http://hi.csdn.net/attachment/201005/30/0_1275233013TcN9.gif" alt=""></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p><span style="font-size: large;"><strong>进入正题了</strong></span></p>
<p></p>
<p><strong><span style="font-size: small;">Message-Driven Bean</span></strong></p>
<p></p>
<p></p>
<p>1、A special EJB component that can receive JMS messages as well as other types of messages</p>
<p>2、Consumes messages from different destinations</p>
<p>3、MDB没有远程、本地业务接口。</p>
<p>4、与statless session bean 一样,是无状态的,可共享的,在容器中有共享池。</p>
<p>5、对于所有厂家提供的消息驱动bean, 起码支持JMS。(也可以支持soap等)</p>
<p> 要支持JMS,那就要支持消息监听器(就是上面提到消费消息的第二种方法)</p>
<p>6、消息监听器的onMessage方法无返回值,也无排除异常,所以MDB也如此。</p>
<p>7、MDB是无状态的,不会负责维护用户的信息。</p>
<p>8、MDB是单线程。(EJB全是单线程的)</p>
<p></p>
<p></p>
<p>例子:</p>
<p>当消息到来时,容器会在共享池中找一个bean 进行处理,并把当前接受到得消息传递给他</p>
<p>这与之前自己处理消息有什么不同能?</p>
<p>1、我们不用考虑线程的问题了,MDB能处理消息大量的并发</p>
<p>2、能提供容器的种种服务,比如缺省提供的事务服务。</p>
<p>3、不过消息的顺序是没有保证的。</p>
<p>4、持久化订阅者:消费者肯定会再来,消息会缓存住等待消费者。</p>
<p><textarea cols="84" rows="15" name="code" class="java:collapse">//mappedName在不同中间件服务器上意义不同,可能是jndi名。非必须
//activationConfig:激活配置。必须的
@MessageDriven(mappedName="jms/FirstMdb",activationConfig={
//通知类型,缺省EJB使用容器管理事务,这里声明无意义,去掉保留一样。
@ActivationConfigProperty(propertyName="acknowledgeMode",propertyValue="Auto-acknowledge"),
//目标类型
@ActivationConfigProperty(propertyName="destinationType",propertyValue="javax.jms.Queue"),
//目标的JNDI名
@ActivationConfigProperty(propertyName="destination",propertyValue="queue/testQueue"),)
})
public class FirstMdb implements MessageListener{
public void onMessage(Message message){
TextMessage msg = (TextMessage) message;
String text;
try{
text=mgs.getText();
System.out.println(text);
}catch(JMSException e){
e.printStackTrace();
}
}
}</textarea></p>
<p></p>
<p></p>
<p><span style="font-size: large;"><strong></strong></span></p>
<p></p>
<p></p>
<p></p>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值