EJB是一个用于分布式业务应用的标准服务端组件模型。采用EJB架构编写的应用是可伸缩的、事务性的、多用户安全的。采用EJB编写的这些应用,可以部署在任何支持Enterprice JavaBeans规范的服务器平台,如jboss、weblogic等。
EJB实际上是用于编写业务层代码。EJB提供了很多在企业开发中需要使用到的服务,如事务管理/安全/持久化/分布式等,因为这些服务由EJB容器提供,无需我们自行开发,这样大大减少了我们的开发工作量. EJB是程序实现了很大程度上的解耦
因为EJB设计的初衷是用于分布式场合,如果你的程序不需要使用到分布式能力的话,就没有必要使用EJB,使用spring+hibernate就可以很轻松的实现一个服务。
EJB比较适合用于大型企业,因为大型企业一般都会存在多个信息系统,而这些信息系统又相互关联。为了避免业务功能重复开发,实现最大程度的重用,有必要把业务层独立出来,让多个信息系统共享一个业务中心,这样应用就需要具备分布式能力。
如下图
分布式应用的一个比较典型的应用就是当前比较火的网上商城:
注意:tomcat只是一个web容器而不是应用服务器,tomcat没有实现ejb规范。
应用服务器组件结构如下:
EJB3的运行环境(以Weblogic为例)
是商业市场占有率第一的商业JavaEE应用服务器,它具有出色的稳定性,并提供了人性化的管理界面,还有企业需要使用到的众多功能。
EJB中的三种bean
1.会话bean(session bean)
负责与客户端交互,是编写业务逻辑的地方,在会话bean中可以通过jdbc直接操作数据库,但大多数情况下都是通过实体bean来完成对数据库的操作.
2.实体bean(entity bean)
它实际上属于java持久化规范(简称JPA)里的技术, JPA的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate、TopLink等ORM框架各自为营的局面。
3.消息驱动bean(message-driven bean)
它是专门用于异步处理java消息的组件.具有处理大量并发消息的能力.
回话bean(包括有状态和无状态)
•无状态会话bean
平常,我们使用最多的是无状态bean,因为它的bean实例可供多个用户使用,所以它的性能比有状态bean高.正因为一个bean实例被多个用户使用.那么,前一个用户设置的值有可能被后一个用户所修改,所以它无法正确保存某个用户设置的值,因此是无状态的.
•有状态会话bean
有状态bean平常使用的并不多,因为它的一个bean实例只供一个用户使用,所以性能开销比较大,正因为它的实例只被一个用户使用, 用户为它设置的值是不会被其他用户修改,所以可以正确保存用户设置的值,因此是有状态的.
关于这两个的理解,个人觉得是,无状态会话bean就类似于一个servlet,是单例模式的,可以被多个用户并发的访问,而有状态会话bean就类似于一个strut2,是原型的,针对每一个用户的访问,都有一个与之对应的strut2的实例。
示例代码:
(开发一个无状态的回话bean)
步奏:
1.编写一个接口(接口可以是远程接口或本地接口)
2.写一个实现类
3.打成jar包,部署到weblogic服务器里面
相关代码:
接口:
package com.langdingbj.sessionbean;
/**font-family:"Courier New";mso-font-kerning:0pt">
* 定义一个接口
* @authorJUN
*/0pt">
publicinterface HelloEJB {0pt">
void show(String msg);
}
实现类:
package com.langdingbj.sessionbean.impl;import javax.ejb.Remote;import javax.ejb.Stateless;import com.langdingbj.sessionbean.HelloEJB;/** * 第一个无状态的回话bean
* @author JUN
*/
@Stateless
@Remote(HelloEJB.class)
public class HelloEJBImpl implements HelloEJB {
public void show(String msg) {
System.out.println("你好!.." + msg);
}
}
部署即运行方法见:
http://bbs.landingbj.com/showtopic.jsp?boardcode=WLTL&hit=510&showid=245534&rootid=245534
注意:本地接口和远程接口的区别
如果是本地接口:
修改如下(默认是本地接口,可以不写即为默认):
@Statelessfont-family:"Courier New";mso-font-kerning:0pt">
@Local(HelloEJB.class)0pt">
publicclass HelloEJBImpl implements HelloEJB {0pt">
publicvoid show(String msg) {0pt">
System.out.println("你好!.." + msg);
}
}
如果是有状态的bean,写法如下:
@Stateful0pt">
@Local(HelloEJB.class)0pt">
publicclass HelloEJBImpl implements HelloEJB {0pt">
publicvoid show(String msg) {0pt">
System.out.println("你好!.." + msg);
}0pt">
}
当ejb和应用程序是使用的同一个jvm的时候,就是用本地接口,不是同一个jvm的时候就是用远程接口,而在实际开发中由于不知道到底是不是在一个jvm里面运行,所以这两种情况都要考虑,为了避免在运行出错建议写法如下:
接口部分:
package com.langdingbj.sessionbean;
/**font-family:"Courier New";mso-font-kerning:0pt">
* 定义一个接口
* @authorJUN
*/0pt">
publicinterface HelloEJB {0pt">
void show(String msg);0pt">
}
新增部分:
package com.langdingbj.sessionbean;
/**font-family:"Courier New";mso-font-kerning:0pt">
* 定义一个接口
* @authorJUN
*/0pt">
publicinterface HelloEJBLocal extends HelloEJB{font-family:"Courier New";mso-font-kerning:0pt">
}
实现部分:
package com.langdingbj.sessionbean.impl;import javax.ejb.Local;import javax.ejb.Remote;import javax.ejb.Stateful;import com.langdingbj.sessionbean.HelloEJB;import com.langdingbj.sessionbean.HelloEJBLocal;/** * 第一个无状态的回话bean
* @author JUN
*/
@Stateful
@Remote(HelloEJB.class)
@Local(HelloEJB.class)
public class HelloEJBImpl implements HelloEJBLocal {
public void show(String msg) {
System.out.println("你好!.." + msg);
}
}
实体bean
它有一个主健作为唯一的标识符
组成部分: 由本地接口、远程接口、bean类、主健类和配置描述器组成。
①本地接口:扩展了javax.ejb.ejbhome接口,包括create()、remove()、finder 和home等方法
具体含义如下:
1)create()方法调用bean类中的ejbcreate()方法。相当于数据的insert 方法。
2)remove()方法相当于数据库的delete 3)finder()方法,使客户能够查询和接收满足查询条件的实体bean的引用。每个实体bean的本地接口中都必须有一个findbyprimarykey() 方法
4)home 方法,类似于无状态会话bean。
②主健类:实体bean必须包括一个主健类,主健类用于标识实体bean实例,而且实体bean数据类型必须是唯一的。主健类可以是java的基本类型string integer 也可以是用户自定义的。也可以是多个字段的主健的复合主健。
bean 类和bean的上下文环境:
实现javax.ejb.ejbobject 接口,其中包含业务方法的语法格式定义.
bean 类实现了javax.ejb.entitybean接口,同javax.ejb.sessionbean接口一样,entitybean 接口包含了ejb
容器调用bean实例的语法格式.
在bean的构造器执行之后,立即调用setentitycontext() 方法,同时把bean实例的entitycontext 传递给它.bean类实现了home方法和远程接口中的业务方法,home方法是针对匿名实例的方法不应使用有关的主健值.
分为:
1)、容器管理持久性(container-managerd persistence)cmp
特点: ejb 容器自动生成,用于把实体bean的数据写入到数据库中。
优点: bean作者可以避免编写实体bean与关系数据库数据访问方面的代码。cmp将自动处理这一过程。
个性: 每一个cmp 实体bean 都有一组容器管理的字段,这些字段存储在数据库,并可从中加载.通常,每个容器管理的字段都对应于关系数据库中的一个列.容器管理的每个字段必须在ejb-jar.xml中定义,这使容器能够把容器管理的字段与bean类中的set和get方法进行匹配比较.另外,bean作者可以增加另外一个cmp配置描述文件 weblogic-cmp-rdbms.xml,其中包含数据库表名和每个容器管理的字段和相应的数据列的映射.
2)、bean管理持久性(bean-managerd persistence ) bmp
特点:在bmp实体中,bean作者需要自己编写数据库访问代码,也就是编写jdbc代码,插入、删除和查询数据库中的实体bean数据。
优点:可以让bean的作者完全灵活的处理实体bean的持久性数据,因为作者需要写数据访问的代码,他几乎可以使用任何持久性存储方式ejb2.0 cmp提供实体bean之间的标准关系映射,使容器能自动管理业务对象之间的交互。cmp拥有更多的访问控制,因此cmp比bmp有较好的性能。
消息驱动bean
首先介绍一下Java消息服务(Java Message Service),Java 消息服务(Java Message Service,简称JMS)是用于访问企业消息系统的开发商中立的API。企业消息系统可以协助应用软件通过网络进行消息交互。JMS的编程过程很简单,概括为:应用程序A发送一条消息到消息服务器的某个目得地(Destination),然后消息服务器把消息转发给应用程序B。因为应用程序A和应用程序B没有直接的代码关连,所以两者实现了解偶。如下图:
消息的类型(他们都是派生自Message 接口):
1.StreamMessage:一种主体中包含Java 基元值流的消息。
2.MapMessage:一种主体中包含一组名-值对的消息。没有定义条目顺序。
3.TextMessage:一种主体中包含Java 字符串的消息(例如,XML 消息)。
4.ObjectMessage:一种主体中包含序列化Java 对象的消息。
5.BytesMessage:一种主体中包含连续字节流的消息。
消息的传递模型
JMS 支持两种消息传递模型:点对点(point-to-point,简称PTP)和发布/订阅(publish/subscribe,简称pub/sub)。这两种消息传递模型非常相似,但有以下区别:
①PTP 消息传递模型规定了一条消息只能传递给一个接收方。采用javax.jms.Queue 表示。
②Pub/sub 消息传递模型允许一条消息传递给多个接收方。采用javax.jms.Topic表示
javax.jms.Queue 和javax.jms.Topic 都扩展自javax.jms.Destination 类。
开始JMS编程前,我们需要先配置消息到达的目标地址(Destination),因为只有目标地址存在了,我们才能发送消息到这个地址。
在java类中发送消息,
一般发送消息有以下步骤:
(1) 得到一个JNDI初始化上下文(Context)
InitialContext ctx = new InitialContext();
(2) 根据上下文查找一个连接工厂 QueueConnectionFactory 。该连接工厂是由JMS提供的,不需我们自己创建,每个厂商都为它绑定了一个全局JNDI,我们通过它的全局JNDI便可获取它;
QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("QueueConnectionFactory");
(3) 从连接工厂得到一个连接 QueueConnection
conn = factory.createQueueConnection();
(4) 通过连接来建立一个会话(Session);
session = conn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
这句代码意思是:建立不需要事务的并且能自动确认消息已接收的会话。
(5) 查找目标地址:
例子对应代码:Destination destination = (Destination ) ctx.lookup("queue/msg");
(6) 根据会话以及目标地址来建立消息生产者MessageProducer (QueueSender和TopicPublisher都扩展自MessageProducer接口)
例子对应代码:
MessageProducer producer = session.createProducer(destination);
TextMessage msg = session.createTextMessage("您好,这是我的第一个消息驱动Bean");
producer.send(msg);
采用消息驱动Bean (Message Driven Bean)接收消息
消息驱动Bean(MDB)是设计用来专门处理基于消息请求的组件。它和无状态Session Bean一样也使用了实例池技术,容器可以使用一定数量的bean实例并发处理成百上千个JMS消息。正因为MDB具有处理大量并发消息的能力,所以非常适合应用在一些消息网关产品。如果一个业务执行的时间很长,而执行结果无需实时向用户反馈时,也很适合使用MDB。如订单成功后给用户发送一封电子邮件或发送一条短信等。
一个MDB通常要实现MessageListener接口,该接口定义了onMessage()方法。Bean通过它来处理收到的JMS消息。
package javax.jms;
public interface MessageListener {
public void onMessage(Message message);
}
当容器检测到bean守候的目标地址有消息到达时,容器调用onMessage()方法,将消息作为参数传入MDB。MDB在onMessage()中决定如何处理该消息。你可以使用注释指定MDB监听哪一个目标地址(Destination)。当MDB部署时,容器将读取其中的配置信息。
详细配置如下:
@MessageDriven(activationConfig =
{
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination",
propertyValue="queue/msg")
})
public class PrintBean implements MessageListener {
public void onMessage(Message msg) {
//处理代码
}
}