会话 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
在开发前,先熟悉一下本例子的调用流程图:
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
package com.foshanshop.ejb3;public interface HelloWorld { public String SayHello(String name);}package com.foshanshop.ejb3.impl;import com.foshanshop.ejb3.HelloWorld;import javax.ejb.Remote;import javax.ejb.Stateless;@Stateless@Remote ({HelloWorld.class})public class HelloWorldBean implements HelloWorld { public String SayHello(String name) { return name +"说:你好!世界,这是我的第一个EJB3哦."; }}Package javax.ejb;@Target(TYPE) @Retention(RUNTIME)public @interface Stateless { String name( ) default ""; String mappedName() default "";}<?xml version="1.0"?><project name="HelloWorld" default="ejbjar" basedir="."> <property environment="env" /> <property name="src.dir" value="${basedir}/src" /> <property name="jboss.home" value="${env.JBOSS_HOME}" /> <property name="build.dir" value="${basedir}/build" /> <property name="build.classes.dir" value="${build.dir}/classes" /> <!-- Build classpath --> <path id="build.classpath"> <fileset dir="${jboss.home}/client"> <include name="*.jar" /> </fileset> <pathelement location="${build.classes.dir}" /> </path> <target name="prepare" depends="clean"> <mkdir dir="${build.dir}" /> <mkdir dir="${build.classes.dir}" /> </target> <target name="compile" depends="prepare" description="编绎"> <javac srcdir="${src.dir}" destdir="${build.classes.dir}" debug="on"deprecation="on" optimize="off" includes="**"> <classpath refid="build.classpath" /> </javac> </target> <target name="ejbjar" depends="compile" description="创建EJB发布包"> <jar jarfile="${basedir}/HelloWorld.jar"> <fileset dir="${build.classes.dir}"> <include name="**/*.class" /> </fileset> <metainf dir="${src.dir}/META-INF"> <include name="*.xml" /> </metainf> </jar> </target> <target name="clean"> <delete dir="${build.dir}" /> </target></project><%@ page contentType="text/html; charset=GBK"%> <%@ page import="com.foshanshop.ejb3.HelloWorld, javax.naming.*, java.util.Properties"%> <% Properties props = new Properties(); props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); props.setProperty("java.naming.provider.url", "localhost:1099"); try { InitialContext ctx = new InitialContext(props); HelloWorld helloworld = (HelloWorld) ctx.lookup("HelloWorldBean/remote"); out.println(helloworld.SayHello("佛山人")); } catch (NamingException e) { out.println(e.getMessage()); } %>