Bromon原创 请尊重版权
在java世界混了这么久,自从脱离了群众基础深厚的Java菜鸟群体之后,被问得最多的问题,第一是“学java应该看什么书”,第二就是“j2ee是什么”。第一个问题好唬弄,第二个问题不好解释。J2EE框架庞大,核心技术有13项,太复杂,不好说明。J2EE的概念常被滥用,怎样才算J2EE应用?这个标准不好定。但是无论怎样,EJB是J2EE最重要的核心,这肯定是毫无疑问的。文章的题目也尽可能的小,仅仅涉及J2EE中的EJB中的会话Bean,免得行家看了笑话。演示的所有例子都在J2SDK 1.4.2+J2SDKEE 1.3.1下面通过,使用的服务器是J2SDKEE1.3.1自带的,为了简化操作,我们还使用了J2SDKEE1.3.1自带的部署工具deploytool。开发工具是Eclipse 2.1.2,没有使用任何额外的插件。
J2EE到底是干嘛的?假设你需要开发一个非常大型的系统(往大了想,金融级别的、电信级别的),很明显这样的系统除了完成功能以外,还要考虑其他的一些问题,比如:
分布性。 不同模块有可能分布在不同的JVM上。
伸缩性。 单个模块需要应付很高数量的并发操作,随时可能增加新的机器、新的分布组件。
事务性。 多个分布式组件之间需要严格的事务控制。
集成性。 这样的系统多半要和旧有系统集成,你甚至可能不知道你的客户用什么数据库、不知道服务器是什么状况。
在Java平台上,如果你按照一定的规范来开发系统,遵守一个要求严格的框架,定义符合规范的接口、然后编写一些类来实现指定的接口,那么你写出来的系统就可以实现上述要求。这套框架被统称为J2EE。我想这样解释会比较合理。
EJB有三种:会话Bean(Session Bean)、实体Bean(Entity Bean)、消息驱动Bean(Message-driven Bean)。其中消息Bean是新成员,可以暂不涉及。会话Bean和实体Bean很好区分,你可以简单的把实体Bean看作是数据库表格的代理,也就是数据。会话Bean可以看作是对数据的操作。想以想对象中的域(field)和方法(method)就能够理解。
会话Bean又分两种:有状态会话Bean(Stateful Session Bean)和无状态会话Bean(Stateless Session Bean)。有状态会话Bean可以保存客户状态。你可以简单的用HTTP中的Session来理解它,用户的一些信息可以暂时的保存起来,一旦会话结束就会消失。而无状态会话Bean则完全不保存信息。很明显,无状态会话Bean更简单,我们就从它开始。
一个规范的会话Bean需要四个接口和至少一个类。弄个最简单的Hello,world作例子吧。这个项目命名为Hello。根据SUN推荐的命名规范,我们应该定义如下的接口和类:
第一个是远程接口Hello.java:
/*
* Created on 2004-4-4
*/
package org.bromon.ejb.stateless;
/**
* @author Bromon
*/
public interface Hello extends javax.ejb.EJBObject
{
public String say() throws java.rmi.RemoteException;
}
这个接口定义了一个say()方法,这个方法是我们指定的真实提供服务的逻辑方法,它会在Bean类中实现。有一点需要说明的是,作为一个有经验的Java开发者,应该避免让对象和它的方法同名,比如:
Hello hello=new Hello();
hello.hello();
这里的对象hello有一个hello()方法,应该在设计类和接口的时候避免出现这种情况。
远程接口是不会和客户端打交道的,这是Home接口的任务,下面就定义Home接口HelloHome.java:
/*
* Created on 2004-4-4
*/
package org.bromon.ejb.stateless;
/**
* @author Bromon
*/
public interface HelloHome extends javax.ejb.EJBHome
{
Hello create() throws java.rmi.RemoteException,javax.ejb.CreateException;
}
Home接口定义了create()方法,客户端通过调用该方法得到一个Hello对象,然后才能够调用Hello接口中定义的方法。
下面可以编写执行实际操作的类了,名为HelloBean.java:
/*
* Created on 2004-4-4
*/
package org.bromon.ejb.stateless;
import javax.ejb.*;
/**
* @author Bromon
*/
public class HelloBean implements javax.ejb.SessionBean
{
private SessionContext ctx;
public void ejbCreate()
{
System.out.println("create");
}
public void ejbRemove()
{
System.out.println("remove");
}
public void ejbActivate()
{
System.out.println("activate");
}
public void ejbPassivate()
{
System.out.println("passivate");
}
public void setSessionContext(SessionContext ctx)
{
this.ctx=ctx;
}
public String say()
{
return("hello");
}
}
这个类实做了javax.ejb.SessionBean接口,所以它必须具备以下方法才能通过语法检查:
ejbCreate() 容器调用该方法来产生实例
ejbRemove() 容器调用该方法来销毁实例
ejbActivate() 容器调用该方法来激活实例
ejbPassivate() 容器调用该方法类钝化实例
setSessionContext(SessionContext ctx) 容器调用该方法,让实例在环境中注册
另外还有一个say()方法,这是真是提供服务的逻辑方法,需要注意的是,HelloBean中出现的逻辑方法必须在Hello接口中申明,否则通不过。同样的,HelloBean中如果出现对ejbCreate()方法的重载,必须载HelloHome接口中申明,否则通不过。关于这两点,会在以后的有状态会话Bean中演示。
至此,已经产生了一个可以正常工作的EJB,但是还记得上面提过的吗?最规范的会话Bean应该有四个接口,而我们现在只有两个。上面两个接口都是远程接口,他们的方法都需要抛出远程异常java.rmi.RemoteException。而实现远程接口是很耗资源的,这也是J2EE受人诟病的主要原因。实际上很多EJB并不是被远程调用的,调用它们的很可能是运行在同以台机器上的其他EJB,在这种情况下使用远程接口很明显是不明智的,这一点在实体Bean中体现得尤其明显。所以J2EE规范定义了本地接口来解决问题,专供本地调用的时候使用。下面来做定义:
本地接口HelloLocal.java:
/*
* Created on 2004-4-4
*/
package org.bromon.ejb.stateless;
/**
* @author Bromon
*/
public interface HelloLocal extends javax.ejb.EJBLocalObject
{
public String say();
}
这个接口和Hello接口的区别在于继承了不同的接口,并且它的say()方法不需要抛出远程异常。
然后需要定义本地Home接口HelloLocalHome.java:
/*
* Created on 2004-4-4
*/
package org.bromon.ejb.stateless;
/**
* @author Bromon
*/
public interface HelloLocalHome extends javax.ejb.EJBLocalHome
{
HelloLocal create() throws javax.ejb.CreateException;
}
同样的,它和HelloHome接口相比,也是继承了不同的接口,并且不需要抛出远程异常。但是它仍然需要抛出javax.ejb.CreateException,这是容器创建EJB的时候可能产生的异常,必须抛出。
编码工作基本完成,它还需要部署描述符,但是我们可以通过工具来完成。下面是部署的过程。
首先启动服务器:
j2ee –verbose
启动完成后大概是这个样子滴:(图一)
screen.width/2)this.width=screen.width/2" vspace=2 border=0>
然后就可以启动部署工具了:
deploytool
部署工具会自动探测到运行中的j2ee,否则在左边的窗口中就看不见server-localhost这样的树状结构,多半是j2ee没有启动完造成的。
然后的操作比较复杂,需要仔细和耐心。
新建一个application:
file-new-application,点击browse,选择生成的.ear文件所在的路径,并且填写文件名为Hello.ear。
然后添加EJB:
选中左边窗口中我们刚新建的Hello这个应用,然后:
file-new-enterprise bean
在弹出的提示窗口中next,点击edit可以添加文件,配置好的应该是这个样子滴:(图二)
screen.width/2)this.width=screen.width/2" vspace=2 border=0>
需要注意的地方都标注出来了。
然后next,需要填写一些选项,填成这个样子就OK:(图三)
screen.width/2)this.width=screen.width/2" vspace=2 border=0>
然后就可以一路next,最后finish,直接finish也可以。
然后需要为这个EJB指定一个JNDI名字,客户端是通过这个名字来查找这个EJB的:(图四)
screen.width/2)this.width=screen.width/2" vspace=2 border=0>
对照红色的地方,自己填写一个JNDI Name为hello,注意大小写。
验证一下是否正确:tools-verifier
可能会报告一个warning,这不影响部署,不过如果报告有failed就不行了,请仔细检查。
现在可以部署:tools-deploy(图五)
screen.width/2)this.width=screen.width/2" vspace=2 border=0>
需要选中Return Client Jar,系统会在指定位置产生一个.jar文件,这里是HelloClient.jar,里面包含了远程调用的本地存根(stud),这是客户端程序需要引用的。然后可以finish,部署过程结束后ok。
下面编写客户端程序Client.java:
/*
* Created on 2004-4-4
*/
package org.bromon.ejb.stateless;
import javax.naming.*;
import javax.rmi.*;
/**
* @author Bromon
*/
public class Client
{
public static void main(String[] args)
{
try
{
Context ctx=new InitialContext();
Object obj=ctx.lookup("hello");
HelloHome home=(HelloHome)PortableRemoteObject.narrow(obj,HelloHome.class);
Hello hello=home.create();
String result=hello.say();
System.out.println(result);
}catch(Exception e)
{
System.out.println(e);
}
}
}
编译和运行该程序都需要在classpath中加入部署中产生的那个HelloClient.jar。操作如下:
javac –classpath %classpath%;f:HelloClient.jar –d . Client.java
其中f:HelloClient.jar是部署中产生的.jar文件的位置,请自己对照修改。
运行该程序:
java –classpath %classpath%;f:HelloClient.jar org.bromon.ejb.stateless.Client
得到运行结果:hello。这可是j2ee的hello啊,老值钱了。弄个分布式的Hello服务群集,吓唬吓唬人。
无状态会话Bean的开发过程基本完成,很多细节都忽略了,不过作为入门还是不错,很多初学者都是因为花了很多时间都无法搞定hello world,结果吐血数升后放弃。当初偶在资料匮乏的情况下摸索上述过程,那真是一部血泪史,莫再提莫再讲。
例子中的代码全部从Eclipse中实际拷贝,热腾腾的还冒气,绝对可以执行。遇到问题的,自己考虑考虑先。
累啊,又对偶的键盘和热情造成了极大的磨损。有状态会话Bean以后再说吧。