EJB学习随手笔记

本文详细介绍了EJB(Enterprise JavaBeans)的概念,包括无状态Bean和有状态Bean,以及EJB的接口类型如Remote和Local。讲解了EJB在业务逻辑实现中的作用,会话Bean的生命周期,以及JNDI查找EJB的基本原理。此外,还涉及到了EJB的命名规则、状态管理和拦截器的使用,以及在Web服务中的应用。
摘要由CSDN通过智能技术生成
名词:
注解方式:
@persistenceContext:持续、存留;环境、上下文;
@Stateless: 无状态(无权的)
@Remote:  远程接口

一、EJB接口
remote和local的?
二、(Enterprice JavaBeans )EJB基础知识:
①EJB是一个用于分布式业务应用的标准服务端组件模型。采用EJB架构编写的应用是可伸的、事务性的、多用户安全的。一次编写这些应用,
 然后部署在任何支持EJB规范的服务器平台,如:JBOSS/WEBLOGIC。
②EJB定义了三种企业Bean,分别是会话Bean(Session Bean)、实体Bean(Entity Bean)和消息驱动Bean(MessageDriven Bean)。
 Session Bean:Session Bean用于实现业务逻辑,分为有状态Bean 和 无状态Bean。
               每当客户端请求时,容器就会选择一个Session Bean来客户端服务。
            Session Bean可以直接访问数据库,但是更多,会通过Entity Bean
           实现数据访问。
           @persistenceContext  声明进行操作的Session Bean,对象;
 实体Bean:   从名字上我们就能猜到,实体Bean代表真实物体的数据;但是这里可以把
            实体Bean看做是用来存放数据的JavaBean,比普通JavaBean多一个功能,不仅
            可以存放数据的角色,还要跟数据库进行对象和关系的映射。
            @Entity    @Table(name=“表名”)
   消息驱动Bean(MDB):是设计用来专门处理基于消息请求的组件。它能收发异步JMS消息,
                 并能轻易的与其他EJB进行交互。所以它特别适合用于当一个业务
               执行的时间特别长,而执行的结果无需实时向用户反馈的场合。
三、会话Bean(Session bean)
用于实现业务逻辑,分为有状态和无状态两种;每当客户端请求时,容器就会选择一个Session Bean来为客户端服务。
Session Bean作为业务处理对象出现在各种应用体系中;

1、JNDI==JNDI===JNDI↓

客户需要通过JNDI查找EJB(JSP---》EJB)
JNDI:(The Java Naming and Directory InterFace)
Java命名和目录的端口,是一组在Java应用中访问命名和目录服务的API。为开发人员提供了查找和访问各种命名和目录服务的通用、统一的形式。借助于
JNDI提供的接口,能够通过名字定为用户、机器、网络、对象服务等。
命名服务:就像DNS一样,通过命名服务器提供服务,大部分的J2EE服务器都含有命名服务器。
目录服务:一种简化的RDBMS系统,通过目录具有的属性保存一些简单的信息。目录服务通过目录服务器实现,比如:微软ACTIVE DIRECTORY等。
JNDI 的好处:
(1)包含大量命名和目录服务,可以使用相同API 调用访问任何命名或目录服务。
(2)可以同时连接多个命名和目录服务。
(3)允许把名称同JAVA 对象或资源关联起来,不必知道对象或资源的物理ID。
(4)使用通用接口访问不同种类的目录服务
(5)使得开发人员能够集中使用和实现一种类型的命名或目录服务客户API 上。

什么是上下文:由0或多个绑定构成。比如:java/MySql,java为上下文(context),MySql为命名
什么是子上下文(subContext):上下文下的上下文。比如:MyJNDITree/ejb/helloBean,ejb为子上下文。

JNDI编程过程
因为JNDI是一组接口,所以只需根据接口规范编程就可以。要通过JNDI进行资源访问,必须设置初始化上下文的参数,
主要是设置JNDI驱动的类名(java.naming.factory.initial)和提供命名服务的URL(java.naming.provider.url)。
因Jndi的实现产品有很多。所以java.naming.factory.initial的值因提供JNDI服务器的不同而不同。java.naming.provider.url
的值包括提供命名服务的主机地址和端口号。

下面为访问JBOSS服务器的例子代码:
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext = new InitialContext(props); //设置JNDI 访问的环境,如果客户端运行在jboss,不需要传入props;
HelloWorld helloworld = (HelloWorld) ctx.lookup("HelloWorldBean/remote");
下面为访问Sun应用服务器的例子代码:
Properties props = new Properties();
props.setProperty("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.provider.url", "localhost:3700");
InitialContext = new InitialContext(props);
HelloWorld helloworld = (HelloWorld) ctx.lookup("com.foshanshop.ejb3.HelloWorld");
下面为访问Webblogic10应用服务器的例子代码:
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
props.setProperty("java.naming.provider.url", "t3://localhost:7001");
InitialContext = new InitialContext(props);
HelloWorld helloworld = (HelloWorld) ctx.lookup("HelloWorldBean
#com.foshanshop.ejb3.HelloWorld");

JBOSS环境下JNDI树命名约定:
① java:copm
这个上下文环境和其子上下文环境仅能被与之相关的特定组件访问和使用
② java:
子上下文环境和绑定的对象只能被Jboss服务器虚拟机内的应用访问
③ 其他上下文环境
只要实现序列化就可以被远程用户调用。

2、无状态bean开发(Stateless Session Beans)
无状态会话bean主要用来实现单次使用的服务,该服务能被启用很多次,但是由于无状态会话Bean并不保留任何有关状态的信息,其效果是每次
调用提供单独的使用,在很多情况下,无状态会话Bean 提供可重用的单次使用服务。

尽管无状态会话Bean 并不为特定的客户维持会话状态,但会有一个以其成员变量形式表示的过度状态。当一个客户调用无状态会话Bean 的方法时,
Bean 的成员变量的值只表示调用期间的一个过度状态。当该方法完成时,这个状态不再保留。

除了在方法调用期间,所有的无状态会话Bean 的实例都是相同的,允许EJB 容器给任何一个客户赋予一个实例。许多应用服务器利用这个特点,
共享无状态会话Bean 以获得更好的性能。

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

@Stateless , @Remote,第一个注释定义这是一个无状态会话Bean,第二个注释指明这个无状态Bean 的remote 接口,指明实现的接口是远程接口,
在使用这两个注释时需要使用一些EJB 的类包,这些类包都可以在jboss 安装目录的client,/server/default/deploy/jboss-aop-jdk50.deployer,
/server/default/deploy/ejb3.deployer,/lib/endorsed 等文件夹下找到,或者在源代码的Lib 文件夹下获得。
  
  接口的定义如下:   HelloWorld.java
  实现类的命名规则是:接口+Bean ,如: HelloWorldBean 
  
  ***************************************************************************************************************************************
  在这里作者要重点说明一下Jboss EJB JNDI 名称默认的命名规则,命名规则如下:
1> 如果EJB 打包进后缀为*.ear 的J2EE 发布文件,默认的JNDI 路径名称是
访问本地接口:EAR-FILE-BASE-NAME/EJB-CLASS-NAME/local
访问远程接口:EAR-FILE-BASE-NAME/EJB-CLASS-NAME/remote
例:EJB HelloWorld 打包进名为HelloWorld.ear 的J2EE 应用,访问她远程接口的JNDI 名是:
HelloWorld/HelloWorldBean/remote
2> 如果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。

目前网上很多教材获取JNDI 路径名的方式不适用在jboss 下,如:
HelloWorld helloworld = (HelloWorld) ctx.lookup(HelloWorld.class.getName());这种方式适用于Sun ApplicationServer 及glassfish
我们把上面的客户端应用打成war 文件。然后把她拷贝到“[jboss 安装目录]\server\default\deploy”目录下。如果
war文件的文件名为EJBTest.war ,我们可以通过http://localhost:8080/EJBTest/Test.jsp 访问客户端。   
****************************************************************************************************************************************
@Local 注释指明实现的接口是本地接口。当@Local 和@Remote 注释都不存在时,会话Bean 实现的接口默认为Local 接口。  
如果在本机调用EJB(确保客户端与EJB 容器运行在同一个JVM),采用Local 接口访问EJB 优于Remote 接口,因为Remote
  接口访问EJB需要经过远程方法调用(RPCs)环节,而Local接口访问EJB直接从JVM中返回EJB的引用。 
  ****************************************************************************************************************************************
  如果你试图在独立的Tomcat 服务器中执行客户端代码(如何在独立的Tomcat 环境中调用EJB 请考照第二章:在独立的Tomcat 中调用EJB),你将获得如下例外:
  java.lang.NullPointerExceptionorg.jboss.ejb3.stateless.StatelessLocalProxy.invoke(StatelessLocalProxy.java:74)产生此例外的原因是,调用Local接口
  的客户端与EJB 容器不在同一个VM(虚拟内存堆)。相对于发布到jboss deploy 目录下的客户端应用而言,他与EJB 容器运行在同一个VM。如果客户端与EJB容器
  在不同的VM,只能通过其Remote 接口进行访问。 
  
  
  调用Local 接口时,两次累加的结果都不一样,一个是2,一个是4。
  这是因为Stateless Session Bean 不负责记录使用者状态,Stateless Session Bean 一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,
  Stateless Session Bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。如果它有自己的属性(变量),那么这些变量就会受到所有
  调用它的用户的影响。
  
  在Jboss 网站看到,在EJB3.0 RC9 以上版本中Remote 及Local 接口可以指向同一个业务接口,这样客户端就不会因调用接口的不同而来回切换业务接口类。
  当然这种使用场合是在Remote 和Local 的接口方法相同的情况下。
  
3、有状态Bean开发(Stateful Session Beans)
有状态Bean是一个可以维持自身状态的会话Bean,每个用户都有自己的一个实例,在用户的生命周期内,Stateful Session Bean 保持了用户的信息,即“有状态”
一旦用户灭亡(调用结束或实例结束),Stateful Session Bean的生命周期也告结束。每个用户最初都会得到一个初始的Stateful Session Bean。

stateful session bean 必须实现Serializable 接口,这样EJB 容器才能在她们不再使用时序列化存储她们的状态信息。
@Stateful 注释定义这是一个有状态会话Bean,@Remote注释指明有状态Bean 的remote 接口。


     @SuppressWarnings("serial") 注释屏蔽缺少serialVersionUID 定义的警告。      

因为stateful session bean 的每个用户都有自己的一个实例,所以两者对stateful session bean 的操作不会影响对方。
  另外注意:如果后面需要操作某个用户的实例,你必须在客户端缓存Bean 的Stub 对象(JSP 通常的做法是用Session缓存),
  这样在后面每次调用中,容器才知道要提供相同的bean 实例。
  
4、如何改变Session Bean的JNDI名称
在Jboss 中要自定义JNDI 名称,可以使用@LocalBinding 和@RemoteBinding 注释,@LocalBinding 注释指定Session Bean 的Local 接口的JNDI 名称,
  @RemoteBinding 注释指定Session Bean 的Remote 接口的JNDI名称,例子:
  @Remote ({Operation.class})
@RemoteBinding (jndiBinding="foshanshop/RemoteOperation")
@Local ({LocalOperation.class})
@LocalBinding (jndiBinding="foshanshop/LocalOperation")
在weblogic10 中,你可以通过@Stateless.mappedName()设置全局JNDI名称
@Stateless(mappedName="OperationBeanRemote")
客户端调用EJB 的代码片断如下:
InitialContext ctx = new InitialContext(props);
Operation operation = (Operation) ctx.lookup("OperationBeanRemote#com.foshanshop.ejb3.Operation");

5、Session Bean的生命周期:


6、拦截器(Interceptor)
拦截器可以监听程序的一个或所有方法。拦截器对方法调用流提供了细粒度控制。可以在无状态会话bean、有状态会话bean 和消息驱动bean 上使用它们。
拦截器可以是同一bean 类中的方法或是一个外部类。

  @Interceptors({HelloInterceptor.class})
  public class HelloChinaBean implements HelloChina,HelloChinaRemote {
  
  @Interceptors 注释指定一个或多个在外部类中定义的拦截器。上面拦截器HelloInterceptor 对HelloChinaBean 中的所有方法进行监听。
  
  拦截器HelloInterceptor.java:
  
  public class HelloInterceptor {
@AroundInvoke
public Object log(InvocationContext ctx) throws Exception {
System.out.println("*** HelloInterceptor intercepting");
long start = System.currentTimeMillis();
try{
if (ctx.getMethod().getName().equals("SayHello")){
System.out.println("*** SayHello 已经被调用! *** " );
}
if (ctx.getMethod().getName().equals("Myname")){
System.out.println("*** Myname 已经被调用! *** " );
}
return ctx.proceed();
}catch (Exception e) {
throw e;
}finally {
long time = System.currentTimeMillis() - start;
System.out.println("用时:"+ time + "ms");
}
}
}


@AroundInvoke 注释指定了要用作拦截器的方法。用@AroundInvoke 注释指定的方法必须遵守以下格式:
public Object XXX(InvocationContext ctx) throws Exception XXX 代表方法名可以任意。

除了可以在外部定义拦截器之外,还可以将Session Bean 中的一个或多个方法定义为拦截器。下面以前面的HelloChinaBean 为例,
介绍在Session Bean 中如何定义拦截器。

@Stateless
  @Remote ({HelloChinaRemote.class})
  @Local(HelloChina.class)
public class HelloChinaBean implements HelloChina,HelloChinaRemote {
public String SayHello(String name) {
return name +"说:你好!中国.";
}
public String Myname() {
return "我是佛山人";
}
@AroundInvoke
public Object log(InvocationContext ctx) throws Exception {
try{
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值