在MVC模型当中,J2EE程序通常是以JSP来充当显示层、Servlet来充当控制层、JavaBean用来充当模型层。当我们使用EJB体系后,模型层就使用EJB组件来充当。
无状态会话Bean,主要是用来处理单纯的业务逻辑。比如计算总价,获得金额,统计等等。多个用户可以同时并反复的调用这个会话Bean的同一个实例。也就是共享同一个实例来完成业务逻辑。
而有状态会话Bean,需要保留用户的会话状态。换句话说在特定的条件下一个用户只对应一个有状态的会话Bean的实例。比如购物车,一个用户就必须对应一个有状态的会话Bean的实例,这个实例保存了这个用户所购买了的所有产品。而且每个用户都买的产品都不一样,所以他们不能共享实例。
ejb-jar.xml (部署描述符)容器通过读取里面的配置信息,就知道哪些是会话Bean,哪些是实体Bena。还有他们的事务操作全部都定义在其中。
现在我们就来按照EJB规范来编写SessionBean,我们这个SessionBean的名称叫Count用于计算次数。
首先我们要编写的是Home接口,注意命名要规范。
CountHome:
Public interface CountHome extends EJBHome{
//现在只是建立一个Home接口,它必须继承EJBHome
public Count create () throws CreateException,RemoteException;
//该方法告诉容器需要获得一个去创建Bean类一个实例并返回它的引用(及远程接口)
}
------------------------------
其次是远程接口,我们命名为Count:
public interface Count extends EJBObect{
//远程接口里面定义业务方法,它会抛出远程异常
public int getNumber() throws RemoteException;
}
-------------------------------
最后我们编写Bean类,命名为CountBean:
public class CountBean implements SessionBean{
SessionContext sessionContext;//上下文对象
//注意我们编写的SessionBean必须实现SessionBean这个接口
//这个接口里面定义了会话Bean的生命周期
public void ejbCreate() throws CreateException {}//创建对象,可能抛出创建异常
public void ejbRemove(){}//移出对象
public void ejbActivate(){}//激活对象
public void ejbPassivate(){}//钝化对象
public void setSessionContext(SessionContext sessionCount){
//在Bean类上设置上下文
this.sessionContext = sessionCount;//系统会调用并传入上下文对象与Bean类中的上下文对象匹配
}
int count = 0;//声明一个成员变量
//实际的业务代码实现
public int getNumber(){
System.out.println(count);
count = count +1;
return count;
}
}
编写完这三个Java文件后,我们还需要编写ejb-jar.xml文件,配置整个部署信息。
<ejb-jar>
<display-name>EJBModule1</display-name>
<enterprise-beans>
<session>
<ejb-name>Count</ejb-name>
<home>org.ejb.sessionbean.CountHome</home>
<remote>org.ejb.sessionbean.Count</remote>
<ejb-class>org.ejb.sessionbean.CountBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</ejb-jar>
然后编辑weblogic-ejb-jar.xml将ejb组件的名称和jndi进行绑定
<weblogic-ejb-jar>
<weblogic-enterprise-bean>
<ejb-name>Count</ejb-name>
<jndi-name>Count</jndi-name>
</weblogic-enterprise-bean>
</weblogic-ejb-jar>
使用JB编译后,生成一个*.ear文件。将这个文件部署到服务器上。如果没有异常则说明部署成功。
部署成功后,我们编写一个Client来测试EJB组件。
public class ConverterClient {
public ConverterClient() {}
public static void main(String[] args) {
/*第一部分--------------------------------*/
Properties properties = null;
try {
properties = new Properties();//配置文件
//声明初始化工厂类
properties.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
//指定服务器通信地址
properties.put(Context.PROVIDER_URL, "t3://localhost:7001");
//security_principal 安全_主要的(用户)
properties.put(Context.SECURITY_PRINCIPAL, "");
//security_credentials 安全_信任状(密码)
properties.put(Context.SECURITY_CREDENTIALS, "");
} catch (Exception e) {
System.out.println("设置环境异常");
}
/*第二部分------------------------------*/
ConverterHome home = null;
if(properties!=null){
try {
Context context = new InitialContext(properties);//初始化上下文
Object obj = context.lookup("Converter");//通过JNDI查找实例
try {//通过远程调用得到home对象(得到容器的骨架)
home = (ConverterHome) PortableRemoteObject.narrow(obj,
ConverterHome.class);
} catch (Exception ex1) {
System.out.println("得到远程引用异常");
ex1.printStackTrace(System.out);
}
} catch (NamingException ex) {
System.out.println("上下文异常");
ex.printStackTrace(System.out);
}
}
/*第三部分-----------------------------------------*/
Converter converter= null;
try {
converter = home.create();//通过home骨架得到Converter存根(引用)
converter.getHello("Hello world");
} catch (RemoteException ex2) {
ex2.printStackTrace(System.out);
} catch (CreateException ex2) {
ex2.printStackTrace(System.out);
}
}
有状态会话Bean,主要是保存的用户会话状态。一个有状态会话Bean实例对应一个用户。
那么我们首先来编写一个有状态的会话Bean。我们使用的开发工具为JBuilder2006,它开发EJB起来比较方便。EJB容器使用weblogic8.1中文版。
建立相应的EJB项目后,开打EJB Designer设计窗口。设计一个有状态会话Bean。
在会话Bean上点击右键。设定相应参数。如图:
Bean Name:指的是有状态会话Bean的名称。
Interfaces:表示它的调用接口的类型,remote表示远程。
Session type:表示会话Bean的类型,Stateful表示有状态。
Session synchronization:表示是否需要同步,false为不。
Transaction type:表示事务管理,Container表示EJB容器管理。
我们现在要写的一个例子是一个购物车的例子。我们介绍一下整个工程中所有的类:
EJB model:
|___ ShopingCar.java 有状态会话Bean的远程接口
|
|___ ShoppingCarBean.java Bean类,代码实现类
|
|___ ShoppingCarHome.java Home接口
Entity :
|___ Product.java 产品类,表示实体。这个类必须实现 java.io.Serializable接口。
首先介绍Product的代码,非常简单。
/****************************************************************/
import java.io.Serializable;
public class Product implements Serializable {
private String proName;//产品名称
private double proPrice;//产品价格
public Product() {
}
public Product(String proName,double proPirce){
this.setProName(proName);
this.setProPrice(proPrice);
}
以下全是get和set方法......
/****************************************************************/
为什么这个实体类要实现序列化接口?应为EJB容器在初始化Bean的时候都有一个唯一的标识
这些获得唯一的标识的类都是必须实现Serializable的。当然还有其他用途就不一一介绍....
大家可以在上图看到,我在Bean当中添加了2个方法,一个是addList(Product product)另外一个是getList().相信大家看到这2个方面就该明白是干什么了。
以下是贴出相应Bean类的代码
/********************************************************/
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.CreateException;
import org.ejb.entity.Product;
import java.util.List;
import java.util.ArrayList;
public class ShoppingCarBean implements SessionBean {
SessionContext sessionContext;
List list;
public void ejbCreate() throws CreateException {
}
public void ejbCreate(String name) throws CreateException {
list = new ArrayList();//初始化产品集合
}
public void ejbRemove() {
}
public void ejbActivate() {
}
public void ejbPassivate() {
}
public void setSessionContext(SessionContext sessionContext) {
this.sessionContext = sessionContext;
}
//添加某个产品到集合中
public void addList(Product product) {
list.add(product);
}
//得到所有产品
public List getList() {
return list;
}
}
/**********************************************************/
当我在客户端远程调用EJB的时候,我就会使用到添加产品和得到所有产品的方法。
那么我们现在就来看看。在JSP页面里面如何调用:
<html>
<head>
<title>handlertest </title>
</head>
<body bgcolor="#ffffff">
<%
try{
Context context = new InitialContext();//初始化上下文对象
Object obj = context.lookup("ShoppingCar");//通过JNDI找到对象
//得到骨架(通过远程调用,得到Home接口)
ShoppingCarHome home = (ShoppingCarHome)PortableRemoteObject.narrow(obj,ShoppingCarHome.class);
ShoppingCar shop = home.create("banseon");//通过Home接口,得到存根
shop.addList(new Product("苹果",12.2));//添加产品
shop.addList(new Product("香蕉",3.4));
shop.addList(new Product("桔子",4.5));
int i = shop.getList().size();
out.println("现在产品总量为:"+i+"<br>");
Handle d = shop.getHandle();
session.setAttribute("Handle",d);
shop = null;
out.println("从新获取存根");
shop = (ShoppingCar)((Handle)session.getAttribute("Handle")).getEJBObject();
out.print(shop.getList().size());
shop.remove();//删除服务器实例和本地存根。
}catch(Exception e){
e.printStackTrace();
}
%>
</body>
</html>
其中最主要的是我们要保存客户端与服务器的通信的句柄。就是Handle
首先我们要得到,然后放入一个Session当中。Handle里面是2进制数据。
Handle d = shop.getHandle();
session.setAttribute("Handle",d);
当我们下回再与服务器通信的时候就可以取出,告诉服务器与我们客户端匹配的有状态会话Bean是哪一个了。
shop = (ShoppingCar)((Handle)session.getAttribute("Handle")).getEJBObject();
基本上的用法就是这些。大家可以自由发挥。。。有空来这里提提问题吧。