EJB3中SessionBean使用Local及Remote接口的注意事项

使用EJB3开发已经快一年了,觉得是时候总结一下,顺道分享一下一些个人在开发过程中的一些心得,希望能对各位刚开始开发的朋友有所帮助。

 

废话不多说,直接正题。

 

这次主要是讲述为何SessionBean中的Local接口在某些情况下不能使用自定义类型的情况以及可能的解决方法。

 

众所周知,一个标准的EJB3中大大简化了SessionBean的业务接口(无论是数量还是编码量上),这样SessionBean只需要实现对应接口并讲接口分发到对应客户端即可。

 

但是,实际情况并没有这么简单。

 

首先说下Local接口:

 

在网上,能找到的例子Local接口中的方法通常都是以下形式的:

 

void service();

void service(String);

String service(String);

String service(String) throws Exception;

 

可以发现一点,这里的类型除了Local接口本身是自定义类型之外都是Java自身提供的类型,这种类型有个特点,就是其类实例是被Java的系统类加载器加载的。至于为何要提及这一点,在后面会提及。

 

现在我们来写一个稍微复杂一点的业务方法

 

其中,CustomResult、CustomParam、CustomException均为自定义类型。

 

将写好的EJB组件打包成.jar包发布到服务器,发布顺利完成,这里我们来写个Servlet来调用这个SessionBean,将所需要的类(Local接口,及Local接口Import的类)复制至对应项目的classPath中,以下是调用SessionBean的代码片段:

 

说明:上述代码只给出部分主要的代码片段

 

客户端也写好了,将客户端打包成一个.war包也发布到与EJB相同的同一服务器中(例如同一个JBoss)。

 

此时EJB组件以及Web应用都运行在同一个JVM环境下,按照绝大部分的讲法,在同一个JVM中调用Local接口是可行的,但是现在执行一下,你将会收到一个类似这样的异常:IllegalArgumentException:“Wrong Target”,意思是目标不正确。

 

但如果将STSBServiceLocal中的execute方法的自定义类型换为基本类型则可行。

 

为何会出现这个异常,明明在同一个JVM中,拥有自定义类型的Local业务接口却不能正常调用?

 

其实原因在于大多数应用服务器(Application Server)都通过使用自定义类加载器的方式来实现服务器上的应用服务间的隔离以,以保证应用服务的相对独立性。如果EJB组件与web应用的服务包是互相分开发布的,这样EJB组件和Web应用将会被两个分别不同的类加载器加载,由于Java定义一个类是通过类加载器及类全称限定名称({ClassLoader, QualifiedName})来确定一个唯一的类的,所以分开发布的两个包中的自定义类型会认为是两个不同的类型,这样无异于在接口中声明的是 String execute(Boolean) 方法,但是调用的却是 Double execute(Float) 方法一样

 

又为何使用Java自身的类型却不会出问题,原因就是Java自身的类在一开始就被加载,并且类加载器通常采用parent-first策略,所以Java自身的类型在同一个JVM中无论在哪里引用有且只有一个类实例,所以使用基本类型就不会出错。

 

针对这一点的解决方法有两个,第一个是讲EJB组件以及Web服务打包为一个EAR包发布,这样Web应用就可以正常调用EJB组件了。

但是这样的做法有一定的局限性,而且一旦某个EJB组件依赖的包与Web应用发生冲突,这时就只能将两个包分开发布。

在分开发布的时候由于客户端(Web应用)与EJB服务不再处于同一个类加载器中,这时可能就要通过修改Local接口,或者使用Remote接口来解决自定义类型不能使用的问题。

如果不能修改接口并且坚持要使用Local接口(因为Local接口的性能比Remote高),在JBoss下(因为我只熟悉JBoss的配置,其它服务器的请自行琢磨)有个方案,就是使用jboss-classloading.xml以及jboss-classloading-domain.xml(放在META-INF或WEB-INF下),来控制类加载策略(具体配置见文章末端)。

 

最后提及一下Remote接口,通过这个接口调用的Bean所涉及的自定义类型均需要实现Seriallizable接口并且创建一个为一个的serialVersionUID。

由于Remote接口的调用是基于RMI协议的,所以所有自定义的类将会被序列化并传输到客户端,而通过序列化后的类不再是通过{ClassLoader, QualifiedName}这种形式类判断两个类是否同一个类,而是通过serialVersionUID来判断,如果serialVersionUID不同则会抛出一个DeserializeException(好像是这个名字),意思是无法反序列化这个类。

 

 

JBoss类加载具体的配置:

jboss-classloading-domain.xml

 

此文件的作用是定义一个类加载的域,同一域中的类可以在一定程度上共享。

 

jboss-classloading.xml

 

此文件的作用是为应用配置一个独立的类加载器,domain属性将此类加载器加入刚才定义的YourDomain域中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值