会话 Bean(Session Bean)

Session Bean 是实现业务逻辑的地方。简单地说,像我们要实现两数相加或是从数据库中读取数据,都是通过Session Bean 来实现。根据是否可以维护会话状态,Session Bean 分为有状态 bean 和无状态 bean。有状态 bean 可以维护会话状态,无状态 bean 不维护会话状态。要维护会话状态,意味着 EJB 容器要为每个用户创建一个 bean实例,并通过该实例保存着与用户的会话状态。不维护会话状态,意味着一个 bean 实例不需要保存与某个用户的会话状态,这时一个 bean 实例可以为多个用户服务。 

要开发一个 Session Bean,  我们需要定义接口和 Bean class。其中接口分为远程(remote)和本地(local)接口。EJB3.0在中,不要求你同时实现 remote 和 local 接口,但实现两者是比较好的做法。 
 远程接口(remote interface):定义了 session bean 的业务方法,这些方法可以被来自 EJB 容器之外的应用访问到。 
 本地接口(local interface):同样定义了 session bean 的业务方法,这些方法可以被同处于 EJB 容器内的其它应用使用。因为 local 接口允许 bean 之间直接通过内存交互,没有分布式对象协议的开销,从而改善了性能。 
 Bean 类(bean class):bean class 包含了业务逻辑,它必须具备一个远程或本地接口。在 Bean 类,我们应该实现接口的业务方法,尽管这并不是必须的,但我们没理由不这样做。 

1 Stateless Session Beans(无状态 bean)开发 

由于无状态会话 Bean 不维护会话状态,意味着一个 bean 实例可以为多个用户服务。因此 EJB 容器使用实例池化技术管理无状态会话 Bean。简单的说就是:当无状态会话 Bean 部署到应用服务器时,EJB 容器会为它预先创建一些 bean 实例放在对象池。当有用户访问 EJB 方法时,EJB 容器会从对象池中取出一个实例为之服务,服务完了就回到对象池。当下一个用户再访问 EJB 方法时,EJB 容器有可能再次把该实例取出来为之服务。正因如此,无状态会话 Bean 只需要少量的实例就可以为成百上千的用户服务,大大提高了系统性能。 

由于无状态会话 Bean 能够支持多个用户,并且通常在 EJB 容器中共享,可以为需要大量客户的应用提供更好的扩充能力。无状态会话 Bean 比有状态会话 Bean 更具性能优势,在条件允许的情况下开发人员应该首先考虑使用无状态会话 Bean。 

1.1开发只实现 Remote 接口的无状态 Session Bean 
在开发前,先熟悉一下本例子的调用流程图: 
会话 Bean(Session Bean)_第1张图片  
1. 浏览器请求 Test.jsp 文件 
2. 应用服务器的 JSP 引掣编绎 Test.jsp 
3. Test.jsp 通过 JNDI 查找获得 HelloWorld EJB 的存根对象,然后调用 SayHello()方法,EJB 容器截获到方法调用。 
4. EJB 容器调用 HelloWorld 实例的 SayHello()方法. 

现在我们就开始本例子的开发。首先在 Eclipse 中新建一个普通的 java 项目,然后把[Jboss 安装目录]/client 下的所有 jar 文件加入到项目的构建路径中。如果不需要在项目中使用单元测试用例或普通 J2SE 调用 EJB,你只需要加入 javaee.jar,该文件在本书源代码 lib/javaee 目录下。接下来开始代码编写。 

开发步骤如下: 
第一步:定义一个包含业务方法的接口。这个接口不需要包含任何注释,它是一个普通的 java 接口。调用 EJB的客户端使用这个接口引用从 EJB 容器返回的存根(stub)。代码如下:HelloWorld.java 
Java代码   收藏代码
  1. package com.foshanshop.ejb3;  
  2. public interface HelloWorld {  
  3.     public String SayHello(String name);  
  4. }  

第二步:编写 Bean class。 
HelloWorldBean.java 。Bean 类推荐的命名方式是:接口+Bean ,如: HelloWorldBean 。 
Java代码   收藏代码
  1. package com.foshanshop.ejb3.impl;  
  2. import com.foshanshop.ejb3.HelloWorld;  
  3. import javax.ejb.Remote;  
  4. import javax.ejb.Stateless;  
  5. @Stateless  
  6. @Remote ({HelloWorld.class})  
  7. public class HelloWorldBean implements HelloWorld {  
  8.   
  9.   
  10.     public String SayHello(String name) {  
  11.         return name +"说:你好!世界,这是我的第一个EJB3哦.";  
  12.     }  
  13. }  

在 Bean 类上面有两个注释@Stateless 和@Remote,@Stateless 注释指明这是一个无状态会话 Bean。@Stateless 注释的定义如下: 
Java代码   收藏代码
  1. Package javax.ejb;  
  2. @Target(TYPE) @Retention(RUNTIME)  
  3. public @interface Stateless {  
  4.    String name( ) default "";  
  5.    String mappedName() default "";  
  6. }  

name()属性用于指定 session bean 的 EJB 名称。该名称在 EJB Jar 包中必须是全局唯一的,而在 EAR 中却可以重复(因为 EAR 可以包含多个 EJB Jar,而每个 jar 可以存在一个同名的 EJB,在 EAR 中要定位某个 EJB,可以这样使用:xxx.jar#HelloWorldBean)。如果不指定该属性,默认就是 bean class 的非限定名称。对本例而言,EJB 名称默认为 HelloWorldBean。 
mappedName()属性指定 Bean 的全局 JNDI 名称,这个属性在 weblogic,Sun 应用服务器和 glassfish 起作用。 

@Remote 注释指定这个无状态 Bean 的 remote 接口。Bean 类可以具有多个 remote 接口,每个接口之间用逗号分隔,如:@Remote ({HelloWorld.class,Hello.class,World.class})。如果你只有一个接口,你可以省略大括号,对于本例而言,可以写成这样:@Remote (HelloWorld.class)。 

经过上面两步,一个HelloWorld EJB就开发完了。现在我们把它发布到Jboss中。在发布前我们需要把它打成Jar包。JAR包的方法有很多,打如使用 jar 命令、集成开发工具或者Ant。下面为你介绍两种常用的打包方式:Eclipse打包向导和 Ant 打包。 

Eclipse 打包向导 
在 Eclipse 开发环境下,可以通过导出向导进行打包。在项目名称上点击右键,在跳出的菜单中选择“Export(导出),在“Export”对话框选择“JAR file”点“Next”,在“select the resources to export(选择要导出的资源)”一栏,展开你的项目并选择需要打包的文件。然后选择一个存放目录及文件名。点“Finish”结束打包。 

Ant 打包任务 
使用 Ant 打包是比较方便的,也是作者推荐的打包方式。我们可以在项目根目录下建立一个名为 build.xml 的 xml文件,然后在 xml 文件里面定义我们的打包任务。如下: 
Xml代码   收藏代码
  1. <?xml version="1.0"?>  
  2. <project name="HelloWorld" default="ejbjar" basedir=".">  
  3.    <property environment="env" />  
  4.    <property name="src.dir" value="${basedir}/src" />  
  5.    <property name="jboss.home" value="${env.JBOSS_HOME}" />  
  6.    <property name="build.dir" value="${basedir}/build" />  
  7.    <property name="build.classes.dir" value="${build.dir}/classes" />  
  8.   
  9.   
  10.    <!-- Build classpath -->  
  11.    <path id="build.classpath">  
  12.        <fileset dir="${jboss.home}/client">  
  13.               <include name="*.jar" />  
  14.         </fileset>  
  15.         <pathelement location="${build.classes.dir}" />  
  16.     </path>  
  17.     <target name="prepare" depends="clean">  
  18.         <mkdir dir="${build.dir}" />  
  19.         <mkdir dir="${build.classes.dir}" />  
  20.     </target>  
  21.     <target name="compile" depends="prepare" description="编绎">  
  22.         <javac srcdir="${src.dir}" destdir="${build.classes.dir}" debug="on"  
  23. deprecation="on" optimize="off" includes="**">  
  24.               <classpath refid="build.classpath" />  
  25.         </javac>  
  26.     </target>  
  27.     <target name="ejbjar" depends="compile" description="创建EJB发布包">  
  28.         <jar jarfile="${basedir}/HelloWorld.jar">  
  29.               <fileset dir="${build.classes.dir}">  
  30.                  <include name="**/*.class" />  
  31.               </fileset>  
  32.               <metainf dir="${src.dir}/META-INF">  
  33.                  <include name="*.xml" />  
  34.               </metainf>  
  35.         </jar>  
  36.     </target>  
  37.     <target name="clean">  
  38.         <delete dir="${build.dir}" />  
  39.     </target>  
  40. </project>  

上面建立了一个名为 HelloWorld 的 Ant 项目,default="ejbjar"指定运行 Ant 时,如果没有给定任务名称,则默认执行 ejbjar 任务。basedir="."指定项目的路径为 build.xml 文件所在目录。 
<property environment="env" />用于引用操作系统的环境变量。 
<property name="jboss.home" value="${env.JBOSS_HOME}" />定义了一个名为 jboss.home 的属性,它的值引用名为 JBOSS_HOME 的环境变量(环境变量 JBOSS_HOME 是在安装 jboss 时让大家设置的)。 
<property name="build.dir" value="${basedir}/build" />定义一个名为 build.dir 的属性,它的值指向项目路径下 build目录,该目录用于存放编绎后的临时文件。 
<property name="build.classes.dir" value="${build.dir}/classes" />定义一个名为 build.dir 的属性,它的值指向项目路径下 build/ classes 目录,该目录用于存放编绎后的 class 文件。 
<path id="build.classpath">节点定义了一个 id 为 build.classpath 的类路径,类路径包含[jboss 安装目录]\client 下的所有 jar 文件及/build/ classes 下的所有类文件。 
<target name="prepare" depends="clean">节点用于在项目路径下创建 build 和/build/ classes 文件夹。该任务依赖clean 任务,执行 prepare 任务前会先执行 clean 任务。 
<target name="compile" depends="prepare" description="编绎">节点定义了一个编绎任务,该任务调用 javac 对 src目录下的源文件进行编绎。classpath 引用 id 为 build.classpath 的类路径。编绎后的 class 文件存放在/build/classes目录,在任务执行前会先执行 prepare 任务。 
<target name="ejbjar" depends="compile" description="创建 EJB 发布包">节点定义了一个打包任务,该任务调用 jar命令对/build/classes 目录下的所有 class 文件进行打包,并且把 src/META-INF 目录下的所有 xml 文件打进 jar 文件的 META-INF 目录。生成后的jar文件存放在项目的根目录下,名为 HelloWorld.jar。在任务执行前会先执行 compile任务。 

当 build.xml 编写完成后,我们可以在 eclipse 中执行 Ant 任务。方法是:打开 build.xml 文件,在窗口右边的 Outline(大纲)中右键点击任务名称,在出现的菜单中点击“Run As(运行方式)”-“Ant Build(Ant 构建)” 

如果你的 eclipse 中没有大纲窗口,你可以点击“window”-“show view(显示视图)-“Outline” 

不管使用何种打包方式,一个 EJB 打包后应具有以下目录结构: 
  EJB 应用根目录 
  | -- **/*.class (你的.class 文件) 
  | -- META-INF 
          | -- MANIFEST.MF (如果使用工具打包,该文件由工具自动生成) 

当 HelloWorld 打成 jar 文件后,我们把它发布到 Jboss。发布前先检查 jboss 是否已经启动,如果没有启动,我们可以进入[jboss 安装目录]/bin,  双击 run.bat 启动 Jboss。不指定启动参数的情况下,Jboss 默认使用 default 配置项。 
把 jar 文件拷贝到[jboss 安装目录]\server\default\deploy\目录。观察 Jboss 控制台输出,如果没有抛出例外并看到下面的输出界面,发布就算成功了。 

当 EJB 发布成功后,Jboss 容器会为它生成一个全局 JNDI 名称,我们可以利用这一点进一步判断 EJB 发布是否成功。我们进入 Jboss 的管理台查看它的 JNDI 名称,输入下面 URLhttp://localhost:8080/jmx-console/点击 service=JNDIView,查看 EJB 的 JNDI 名称。 

在出现的页面中,找到“List of MBean operations:”栏。 
点击“Invoke”按钮 

在出现的页面中,我们可以看到 JBOSS 的 JNDI 树,它的命名约定如下: 
(1)java:comp (java:comp namespace) 
  这个上下文环境和其子上下文环境仅能被应用组件内部访问和使用 
(2)java:(java: Namespace) 
  子上下文环境和绑定的对象只能被处在同一个 JVM 内的客户访问 
(3)Global JNDI Namespace 
  上下文环境能被所有客户访问,不管它们是否处在同一个 JVM 内。 

当 EJB 发布到 Jboss 时,如果我们没有为它指定全局JNDI名称或修改过其默认EJB名称,Jboss就会按照默认的命名规则为 EJB 生成全局 JNDI 名称,默认的命名规则如下: 
如果把 EJB 作为模块打包进后缀为*.ear 的 JAVA EE 企业应用文件,默认的全局 JNDI 名称是本地接口:EAR-FILE-BASE-NAME/EJB-CLASS-NAME/local远程接口:EAR-FILE-BASE-NAME/EJB-CLASS-NAME/remoteEAR-FILE-BASE-NAME 为 ear 文件的名称,EJB-CLASS-NAME 为 EJB 的非限定类名。 
例: HelloWorld 应用作为 EJB 模块打包进名为 HelloWorld.ear 的企业应用文件,把它的远程接口的 JNDI 名称是: 
HelloWorld/HelloWorldBean/remote 
如果把 EJB 应用打包成后缀为*.jar 的模块文件,默认的全局 JNDI 名称是 
本地接口:EJB-CLASS-NAME/local 
远程接口:EJB-CLASS-NAME/remote 
例:把 HelloWorld 应用打包成 HelloWorld.jar 文件,它的远程接口的 JNDI 名称是:HelloWorldBean/remote注意:EJB-CLASS-NAME 是不带包名的,如 com.foshanshop.ejb3.impl.HelloWorldBean 只需取 HelloWorldBean。如果你通过@Stateless.name()、     @Stateful.name()及其等价的 XML 指定了 EJB 名称,那么上面的 EJB-CLASS-NAME应该换为 EJB 名称,此时的 JNDI 名称格式如:EJB 名称/remote、EAR 文件名/EJB 名称/remote。 

在 Global JNDI Namespace 一栏,我们看到了 HelloWorldBean 的远程接口的 JNDI 名称为 HelloWorldBean/remote。意味着 EJB 已经发布成功。接下来我们看看客户端如何访问它。 
Test.jsp 
Jsp代码   收藏代码
  1. <%@ page contentType="text/html; charset=GBK"%>  
  2.  <%@ page import="com.foshanshop.ejb3.HelloWorld, javax.naming.*,  
  3.  java.util.Properties"%>  
  4.  <%  
  5.         Properties props = new Properties();  
  6.         props.setProperty("java.naming.factory.initial",  
  7.  "org.jnp.interfaces.NamingContextFactory");  
  8.         props.setProperty("java.naming.provider.url""localhost:1099");  
  9.         try {  
  10.             InitialContext ctx = new InitialContext(props);  
  11.             HelloWorld helloworld = (HelloWorld)  
  12.  ctx.lookup("HelloWorldBean/remote");  
  13.             out.println(helloworld.SayHello("佛山人"));  
  14.         } catch (NamingException e) {  
  15.             out.println(e.getMessage());  
  16.         }  
  17.  %>  

你可能感兴趣的:(Restful,会话Bean)