Spring vs. EJB

文章对比了Spring与EJB在企业开发中的应用。从Scope看,EJB面向事务型企业应用,Spring是轻量化组件架构,适用更多结构;在组件架构方面,Spring灵活但复用性受影响,EJB统一完整;语义上,EJB有规范规定的统一语义,Spring缺乏语义支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

source:
http://blog.youkuaiyun.com/zzpchina/archive/2005/02/16/289293.aspx

   新年一开始,就要转贴,为什么?其实很简单,这篇文章写的很不错,更不错的是关于他的评论,我说它很不错呢,就是说他很有意思,其实大家都是搞不同的应用,在不同的背景下自然衍生各种不同的技术与标准了。如同:有了微软,并不是说开元就没什么用了。道理很浅显,正如它一直被忽略。。。。。

1.Scope比较

先说scope,ejb的scope是什么?ejb针对什么系统来设计的?这个我想一个人一个答案,我说了不算,同样大家说了也不算,我们来看规范(题外说一句,我想本来我没啥

资格在这里谈论ejb,我应用ejb的项目不多,但是我有个习惯,就是读规范学技术,不管别人怎么说先看这个东西是怎么来的,我想这一点还使我有些开口的自信,不至于太

贻笑大方),ejb规范的头几句:
Enterprise JavaBeans is an architecture for component-based computing.Enterprise beans are components of transaction-oriented enterprise

applications.


好了,很明确,ejb是针对transaction-oriented enterprise application的组件,那么也就使说ejb是面向 以事务为中心的企业软件,而不是别的。ejb的核心是

transaction,不是domain model或别的。
why transaction?我在电力行业做过一阵信息化软件架构师,在电力这样一个需要处理大量数据的领域里,很少有oo model,而是做完entity分析,交给dba去优化,以数据

性能为主,这样的系统里,数据操作的粒度就是transaction,而不是object或是别的。我想这该算是一个大型企业级系统,我看到的是transaction,因此ejb这个把scope定

在enterprise级的东西而言,这样的设计还使合理。而且ejb对transaction这部分的处理确实比较完整,cmt,bmt,不同的transaction scope控制的都很好。基于这种认识,

我认识transaction script是ejb的标准应用模式,而不是domain model或是别的。
这是对ejb最大的误解的来源,我看过的所有ejb书里,只有o'reilly的一本隐约提到transaction是ejb的中心。其他一律不提,疯狂的鼓吹ejb多好多好ejb多么万能,我想

,如果不知道ejb是transaction-oriented的,那么ejb奇怪的对象模型的确不可接受。

再说spring,spring是什么呢?我没有看到特别明确的定义,不过我想仿照ejb,定义spring为:
Spring is a Javabean-based framework that supporting component architecture development.Spring provides lighter context for heterogeneous entrprise

application.

我e文很差,cet-6 6次都没过,我想说明的是,spring是一个轻量化的组件架构,可以支持多种企业应用程序模式.看到这里有人又该说了,no,no还有ioc,还有aop,没错这

些很cool的特性我没说,但是包含了,component architecture意味着两个方面,component working(编写组件)和container working(编写容器环境),spring的ioc和aop是

用在container working里的.当然肯定还有其他的一些没有概括,但是我想主体我还是说到了的.这样scope就很明确了,spring的基础是javaBean,javaBean支持完整的oo建

模,因此spring可以适用更多的结构,domain model和别的一些。

那么开始比较,spring有一个理论上普适的组件模型,但是鉴于大型应用多为transaction-oriented,那么用spring的理由就是domain model,ejb不能提供完整的oo模型而

spring可以。

结论:由于scope不同,其实比较spring和ejb那个更适合企业开发没什么意义,因为这里面根本就是两个不同的范畴,在scope上指责ejb不如spring,就好像说raimundox,你

就不能替老婆把孩子生了,还让她那么痛苦的怀胎十月。其实我也不想,我也想替,可惜我们这功能.....扯远了,ejb也是,没这功能你怎么强求他呢?你要说ejb设计的不好

,也不对,人家有专门的领域。因此我说,在scope上比较spring和ejb没意义,根本不是一个级别的。

2.component architecture

Component architecture有一个基本观点,就是component和context的分离,理想情况下,component只负责业务的实现,而container提供context,这个context只技术

context,比如事务比如安全的支持。于是,component architecture的基本观点就是business关注点和technique关注点分离,business由component负责,technique由

context或者叫container实现。那么很明确了,component architecture里有两种编程,针对component的和针对container的。
好,有了这个理解,我们可以继续了,如果有人有疑意,那么抱歉,这片文章并不适合您,后面全部的论点都是基于这个观点的,如果不认可这个,下面的也不会认可,您

也不用浪费时间了。

首先看ejb的component方面,ejb在component architecute做得非常的好,ejb就是一个业务组件,在container-managed的情况下,组件通过声明可以应用容器提供的技术

context,当container-managed不满需要的情况下,可以bean-managed,只要保持对外的事务语义就可以了(记得吗?ejb是transaction-oriented,事务最重要)。在ejb里

,组件和容器的约定非常明确,哪些需要组件写,哪些是容器提供的非常明确,这是component architecture里很好的习惯,明确组件和容器的界限(ejb的一个缺点,矫枉过

正,有一些操作也被禁止)。编写代码非常容易,业务,only业务。其实ejb的规范里,ejb的coder其实最好是domain expert,实现业务逻辑就好了。

现在来看spring的component方面,spring以javaBean为基础,贫容器,也就是对容器没要求,于是,spring第一个缺点,contianer和component的约定不清晰(写到这里我

已经听到一些人在叫了,这是spring的优点,自由,别急,后面我会证明这是spring的软肋),但是spring用了一种比较聪明的办法,那就是加强container working.

看一下spring的container working,通过spring的aop api,我们可以很容易的为特定组件定制容器环境,把一个组件需要的容器技术环境以aspect的形式weave到component

里,非常的灵活,也非常的强大,spring通过这种形式来弥补组件/容器约定不足的情况,一切由component选择,容器除了装配(ioc/dip)不提供任何技术context,想要什

么自己来,这个给了component实现者自己选择的权利,很好(但也隐含了spring的最大的不足,别急我后面会说)。

再来看ejb,非常遗憾,ejb container working的能力几乎为0,当然算上jca的话还不算太差,但是那只是资源,而不是技术context。why?因为ejb认为他已经提供了所有

企业级开发所必需的技术context了,事务(ejb里我总把他放在第一位)、分布、并发、安全等等。在ejb看来container working的能力几乎无用,而且不安全,除了jboss开放

了比较多的container working接口其他的ejb container提供这方面的支持很少很少.当然提供很多技术context并不是坏事,要命的是不能配置,要么全用要么不用(倒是很

原子),这是ejb最大的不足,容器环境不可配,也是spring强于ejb的地方。

上面我们已经看到了spring和ejb都是component architecture,那么component能想到的最直接的用处就是复用。那么比较这一点就是比较ejb和spring component

architecture的关键。看到这里spring的支持者们该常出一口气了,spring复用一定强于ejb复用,赫赫,但我的结论正好相反,ejb复用优于spring复用!!收起你们的愤怒

,收起你们不屑,听我把话说完。

在component architecture里,component是业务实现,而不该有技术代码,就算用也要通过容器环境去和容器交互来完成操作,比如ServletContext等东西。 那么其实

组建结构下复用的关键不是组建而是容器!!


以前有一个颇有名气的dx(gigix别看了,说你呢),说"COM和EJB都鼓吹模块化和复用,模块化是真的,复用是骗人的",com我不是很熟,不好下结论,ejb呢?ejb不易复用我

承认,但是骗人吗?不骗,后面我给出一种ejb复用的思路大家参考。反正组件一点技术都不作,只有业务逻辑想用就要有相应的容器环境,那么容器环境的复用性才是组件复

用的关键。ejb不易复用是因为如果脱离容器,我们就必须给它提供相应的技术context,事务、分布、并发等等一个也不能少, 因此容器外复用ejb效率很低。注意,

是容器外,组件本来就是跑在容器里的,谁让你非要拿出去用),而容器内呢?因为ejb规范规定ejb容器应该兼容,别说webSphere到bea的移植有多难,其实不难,或

者说难度比把spring组件移植到pico复杂一点,当然你用vendor-specified的特性就没办法了,这不再规范内,你违规就别怨人家。因此,ejb的复用是可以的,而且是规范保

证的,容器外也有办法,也不是很难,我后面说。

再看spring,的确他很灵活,但这正是致命伤,component完全是业务实现,但是容器呢?spring怎么保证容器的环境?没有,只能你自己保证,当你沾沾自喜的说,spring

里的component都是pojo,可以很好复用的时候,可曾想到,这复用的代价是要复用容器。比如有个componentA,在SystemA里需要事务模型A和安全模型A,在SystemB里需要事

务模型B和安全模型B,当你从SystemA里复用componentA的时候,你要怎样?重写事务模型B和安全模型B,然后你可以堂而皇之的说,你复用了组件。的确,你复用了组件,但

是你重写了容器,值吗?更可怕的是,spring容器和组件没有约定,那么怎么保证你的组建不写技术代码?ejb只要Bean-Managed并提供统一的事务模型就好了,spring呢?你

靠什么保证?你自己?这是spring一大硬伤,完全container-managed下缺少特定的component边界控制.你可以说,特殊要求的事务模型ejb还实现不了呢,的确,这是有可能

,但是ejb transaction model不能适用的情况有多少?如果真的不行,只能说ejb的简单复用在这里失效。

对于组件还有一个问题就是部署,这也是ejb为人诟病的地方.的确,ejb的部署够复杂,但在ejb规范里有一个专门的角色来负责部署的,ejb是component architecture,那

么比如有一个人来粘合技术和业务,这个人不该是programmer(我刚才说了,ejb的实现者最好是业务专家,实现业务逻辑),ejb的部署才是很厉害的人,他需要知道什么业务

需要什么样的技术支持,该怎样得到性能,因此deployer才是ejb architecture里最牛的,我重来不以为写ejb的是高手,但是一直都敬仰ejb的deployer.当然这里有一个调试

困难的问题,这是ejb的硬伤,没办法,这甚至是组件开发的硬伤.
再来看spring,spring宣称他部署简单,我觉得rod johnson在转移视线,想想看,打成一个war和打成一个ear有多大的区别?那么部署的差异在哪?差异在ejb的deploy

description和spring的context.xml的编写上!在用spring开发中要有一个人来写context.xml这个人往往比较了解系统,知道什么组件用什么拦截,那个组件依赖那个,甚至

会是架构师在作这件事情,那么和ejb里对系统有大局观的人来做deploy有多大区别?可能就是Xml的编写吧,我想在工具的支持下就是熟练度的问题,因此我觉得部署上

spring和ejb差不多,spring不用启server,调试放便些。

结论,在component architecture上,spring灵活,ejb统一完整,各胜擅长,spring的灵活以降低复用为代价,但是如果有common的技术实现,的确很好复用,但是

spring+一套common的技术实现也就约等于ejb了吧?

3.语义

那么spring复用的问题表明了什么呢?其实是缺乏语义的支持,ejb开发可以看作在一个统一的语义环境下来作的,这个语义由ejb规范规定,因此ejb的复用有语义保证,而

spring呢?贫语义,一切都要开发者自己来实现。因此,如果ejb的环境语义可扩展并且可配置(比如去掉分布),那么spring毫无优势,标准的一致的完整的组件架构使ejb

会大有作为,但是现在并没有,才有了spring的火爆.这是一种畸形的胜利,完备语义的输给了贫语义的,问题是什么,强迫消费...谁让ejb非得强迫客户去买用不到的分布式

环境的单?但是统一语义的威力不会因此掩灭,现在有两条路,spring联合os社区,制定lightweight j2ee语义集合,争取成为标准。第二,ejb实现技术语义可配置可扩展。

谁会胜利?不好说,但是似乎ejb的脚步快一些!

附:容器外复用ejb

其实ejb在容器外完全是可以用的,但是为了最大限度保证能用,bean-managed是推荐(不是cmp,bmp而是cmt,bmt),那么怎么传送一个transaction进去?SessionContext(

好像是这名记不清了,都快2:00了,困呀...就是ejb那个context接口),一个接口嘛,自己mock一下,给一个transaction进去就好了。怎么装配?spring的setter

injection。如果用spring,那么cmt也可以实现,拦截啦,不过就看能不能实现ejb transaction model了。entity bean,如果是bmp,就用它好了,cmp,继承一个上

hibernate。这些都模拟好了,找一个in memory的jndi,把spring context封进去,这样相当于我们用spring实现了一个lightweight ejb container(其实就是给spring一个

ejb api的皮),轻到什么程度?除了注射什么都没有。
然后client就可以构造jndi,然后lookup了
看到这里一定有人说,你吃饱了撑的,这么费劲容器外复用ejb,为什么不直接用spring,这样也不够pojo,每个组件都有ejb的类的继承,好,我告诉你这么做的好处,首先

,虽然不够pojo,但是足够bean,因此spring来管理ejb是可以的,证明我的观点容器外使用ejb可以(赫赫,不要说偶rpwt...).其次的,当业务发展了,你的系统需要分布了

,把spring去掉,拿出ejb,redeploy,ok了,延展,稳定,分布都有了,这才是复用该有的境界,改一个部署整个环境换掉,去掉lightweight的ejb container,换乘

heavyweight的就是重量级。
当然这么实现很难,在ejb3里会容易些,我敢打赌,spring以后一定是lightweight ejb container的供应商,免不免费,os不os要看rod johnson了,不过我觉得希望不大。
<think>我们遇到了一个EJB查找失败的错误。错误信息表明:在尝试查找名为`com.lkm.pmi.dept.service.DeptmentSerivce/deptInfoRemote`的bean时,没有找到。同时,嵌套异常是`javax.naming.NameNotFoundException`,指出在查找`deptInfoRemote#com.lkm.ejb.dept.remote.DeptInfoRemote`时,没有找到子上下文`deptInfoRemote#com`。 错误分析: 1. 错误发生在EJB注入或查找时。 2. 从错误信息看,可能是以下原因: - EJB的JNDI名称不正确。 - EJB未正确部署在应用服务器上。 - 客户端代码(这里是一个JSF管理的Bean)中引用的EJB名称错误。 具体错误信息分解: - `No bean named 'com.lkm.pmi.dept.service.DeptmentSerivce/deptInfoRemote' available`:说明Spring(可能是被包装的)在尝试按名称查找bean时失败。 - 嵌套异常是`javax.naming.NameNotFoundException`,并且指出在查找`deptInfoRemote#com.lkm.ejb.dept.remote.DeptInfoRemote`时出错,剩余名称为`deptInfoRemote#com/lkm/ejb/dept/remote/DeptInfoRemote`。 注意:错误信息中出现了两个不同的名称: 1. `com.lkm.pmi.dept.service.DeptmentSerivce/deptInfoRemote` 2. `deptInfoRemote#com.lkm.ejb.dept.remote.DeptInfoRemote` 这可能是由于不同的查找方式导致的。通常,在Java EE中,EJB的默认JNDI名称遵循一定的模式,但不同的应用服务器可能有所不同。 解决方案: 步骤1:确认EJB接口和实现 - 确保`DeptInfoRemote`是一个远程业务接口,并且有正确的注解。 - 确保EJB实现类实现了这个接口,并且使用了`@Stateless`或`@Stateful`注解。 示例EJB接口: ```java package com.lkm.ejb.dept.remote; import javax.ejb.Remote; @Remote public interface DeptInfoRemote { // 方法声明 } ``` 示例EJB实现: ```java package com.lkm.ejb.dept.impl; import javax.ejb.Stateless; import com.lkm.ejb.dept.remote.DeptInfoRemote; @Stateless(name = "DeptInfoRemote", mappedName = "DeptInfoRemote") public class DeptInfoRemoteBean implements DeptInfoRemote { // 实现方法 } ``` 步骤2:检查EJB的JNDI名称 - 不同的应用服务器(如WebLogic, WildFly, GlassFish等)有不同的JNDI命名规则。 - 在WebLogic中,默认的全局JNDI名称通常是:`<接口的完整类名>#<接口的完整类名>` 或 `<mappedName>#<接口的完整类名>`。 步骤3:在客户端代码中正确注入 - 在JSF管理的Bean中,我们使用`@EJB`注解注入EJB。需要确保指定的名称与服务器上的JNDI名称匹配。 错误中的注入点: `@EJB private DeptmentSerivce deptmentSerivce;` 但是,错误信息中提到了`DeptmentSerivce`(注意拼写,可能是`DeptmentService`?)和`deptInfoRemote`。这里可能存在混淆。 步骤4:可能的修正 - 情况1:如果`DeptmentSerivce`是一个EJB,那么应该检查它的JNDI名称。 - 情况2:如果`DeptmentSerivce`是一个本地接口,而`deptInfoRemote`是另一个EJB,那么可能是注入时使用了错误的名称。 修正建议: 1. 检查EJB注入的注解: 在`DeptmentBean`中,我们注入了`DeptmentSerivce`(注意拼写,可能是拼写错误?),但错误信息中提到了`deptInfoRemote`。这可能是两个不同的EJB。 如果`DeptmentSerivce`是一个EJB,那么应该这样注入: ```java @EJB private DeptmentSerivce deptmentSerivce; ``` 但是,如果`DeptmentSerivce`的实现依赖于另一个EJB(`DeptInfoRemote`),那么可能是`DeptmentSerivce`本身没有被正确部署。 2. 如果`DeptmentSerivce`是一个EJB,那么请检查它的JNDI名称。可以在服务器日志中查看部署的JNDI名称。 3. 尝试使用`mappedName`或`lookup`属性明确指定JNDI名称: ```java @EJB(mappedName = "DeptInfoRemote") // 或者使用具体的JNDI名称 private DeptmentSerivce deptmentSerivce; ``` 或者 ```java @EJB(lookup = "java:global/your-app-name/DeptInfoRemote!com.lkm.ejb.dept.remote.DeptInfoRemote") private DeptmentSerivce deptmentSerivce; ``` 4. 检查应用服务器中EJB的实际JNDI名称。例如,在WebLogic中,可以在管理控制台查看。 5. 确保EJB接口和实现在类路径中,并且部署正确。 6. 如果项目结构是EAR,包含EJB模块和Web模块,确保EJB模块已经正确部署,并且Web模块有对EJB模块的依赖。 7. 检查拼写错误:`DeptmentSerivce` 应该是 `DeptmentService`?另外,在错误信息中,`com.lkm.pmi.dept.service.DeptmentSerivce` 这个包名和类名是否确实存在? 8. 如果`DeptmentSerivce`是一个本地业务接口,那么不应该使用远程查找。确保在同一个应用中使用本地接口。 示例修正后的代码: 假设我们有一个EJB,它的远程接口是`DeptInfoRemote`,而我们的`DeptmentSerivce`是一个拼写错误的类,实际上应该是`DeptmentService`,并且它实现了`DeptInfoRemote`接口。 在客户端(Web模块)中,我们这样注入: ```java @EJB private DeptInfoRemote deptInfoRemote; ``` 或者,如果我们有一个本地接口: ```java @EJB private DeptInfoLocal deptInfoLocal; ``` 因此,在`DeptmentBean`中,应该注入正确的EJB接口。 修正`DeptmentBean`中的注入: ```java @EJB private DeptInfoRemote deptInfoRemote; // 或者使用本地接口 ``` 然后,在`save()`和`delete()`等方法中,使用这个注入的EJB。 如果问题仍然存在,请检查应用服务器日志,查看EJB部署时的JNDI名称,然后在注入时使用`lookup`属性指定该名称。 由于我们无法直接查看服务器环境,所以需要根据实际情况调整。 总结:这个错误是由于EJB注入时找不到对应的bean。需要检查EJB的部署名称和注入时使用的名称是否匹配,并确保EJB已经成功部署。 §§相关问题§§: 1. 在Java EE中,如何正确查找EJB的JNDI名称? 2. 如何解决WebLogic服务器上的EJB注入问题? 3. 在JSF中,如何注入远程EJB和本地EJB? 4. 如何调试Java EE应用中的JNDI查找失败问题?</think>### 错误分析:EJB 注入失败 这个错误表明应用服务器无法找到名为 `deptInfoRemote` 的 EJB 组件。错误信息包含几个关键点: 1. **NoSuchBeanDefinitionException**:Spring 容器找不到指定的 bean 2. **NameNotFoundException**:JNDI 查找失败,找不到 `deptInfoRemote#com.lkm.ejb.dept.remote.DeptInfoRemote` 3. **LinkException**:JNDI 名称解析失败,缺少子上下文 ### 错误原因分析 1. **EJB 名称不匹配**: - 代码中使用的名称:`DeptmentSerivce` - JNDI 查找的名称:`deptInfoRemote#com.lkm.ejb.dept.remote.DeptInfoRemote` 2. **部署问题**: - EJB 未正确部署到应用服务器 - JNDI 名称配置错误 - 接口与实现类不匹配 3. **依赖关系问题**: - 客户端模块缺少 EJB 依赖 - 类路径问题导致接口不可见 ### 解决方案 #### 步骤 1:修复 EJB 接口定义 ```java // DeptInfoRemote.java package com.lkm.ejb.dept.remote; import javax.ejb.Remote; @Remote public interface DeptInfoRemote { void saveEventData(DeptmentDm dept); void deleteDeptmentInfo(Long id); DeptmentDm getDeptmentInfo(Long id); List<DeptmentDm> getDeptmentInfoList(int page, int size, Map<String, Object> filters, Map<String, String> sortParams); } ``` #### 步骤 2:修复 EJB 实现类 ```java // DeptInfoRemoteBean.java package com.lkm.ejb.dept.impl; import javax.ejb.Stateless; import com.lkm.ejb.dept.remote.DeptInfoRemote; @Stateless(name = "deptInfoRemote", mappedName = "java:global/app-name/DeptInfoRemote") public class DeptInfoRemoteBean implements DeptInfoRemote { // 实现接口方法... @Override public void saveEventData(DeptmentDm dept) { // 持久化逻辑 } // 其他方法实现... } ``` #### 步骤 3:修复客户端注入 ```java // DeptmentBean.java @ManagedBean(name = "deptmentBean") @ViewScoped public class DeptmentBean { // 使用正确的接口类型和名称 @EJB(mappedName = "java:global/app-name/DeptInfoRemote") private DeptInfoRemote deptmentService; // 方法中使用 deptmentService 替代 deptmentSerivce public void save() { deptmentService.saveEventData(department); // ... } } ``` #### 步骤 4:添加 JNDI 查找后备方案 ```java // 添加手动查找方法 private DeptInfoRemote lookupDeptService() { try { Context ctx = new InitialContext(); return (DeptInfoRemote) ctx.lookup("java:global/app-name/DeptInfoRemote"); } catch (NamingException e) { throw new RuntimeException("EJB lookup failed", e); } } // 在方法中使用 public void save() { if (deptmentService == null) { deptmentService = lookupDeptService(); } deptmentService.saveEventData(department); } ``` ### 完整解决方案 #### 1. EJB 接口定义 ```java package com.lkm.ejb.dept.remote; import java.util.List; import java.util.Map; public interface DeptInfoRemote { void saveEventData(DeptmentDm dept); void deleteDeptmentInfo(Long id); DeptmentDm getDeptmentInfo(Long id); List<DeptmentDm> getDeptmentInfoList(int page, int size, Map<String, Object> filters, Map<String, String> sortParams); } ``` #### 2. EJB 实现类 ```java package com.lkm.ejb.dept.impl; import javax.ejb.Stateless; import com.lkm.ejb.dept.remote.DeptInfoRemote; @Stateless(name = "deptInfoRemote", mappedName = "java:global/DeptManagement/DeptInfoRemote") public class DeptInfoRemoteBean implements DeptInfoRemote { @Override public void saveEventData(DeptmentDm dept) { // 实际持久化逻辑 } @Override public void deleteDeptmentInfo(Long id) { // 删除逻辑 } @Override public DeptmentDm getDeptmentInfo(Long id) { // 查询单个部门 return null; } @Override public List<DeptmentDm> getDeptmentInfoList(int page, int size, Map<String, Object> filters, Map<String, String> sortParams) { // 分页查询部门列表 return null; } } ``` #### 3. 修复后的 JSF Managed Bean ```java import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import java.util.HashMap; import java.util.List; import java.util.Map; @ManagedBean(name = "deptmentBean") @ViewScoped public class DeptmentBean { private DeptmentQuery department = new DeptmentQuery(); private List<DeptmentQuery> departments; // 使用正确的接口类型 @EJB(mappedName = "java:global/DeptManagement/DeptInfoRemote") private DeptInfoRemote deptmentService; @PostConstruct public void init() { loadDepartments(); } // 手动查找后备方案 private DeptInfoRemote lookupDeptService() { try { Context ctx = new InitialContext(); return (DeptInfoRemote) ctx.lookup("java:global/DeptManagement/DeptInfoRemote"); } catch (NamingException e) { throw new RuntimeException("EJB lookup failed", e); } } public void save() { if (deptmentService == null) { deptmentService = lookupDeptService(); } // 修复:使用正确的属性值 DeptmentDm deptmentDm = new DeptmentDm(); deptmentDm.setAddr(department.getAddr()); deptmentDm.setName(department.getName()); deptmentService.saveEventData(deptmentDm); department = new DeptmentQuery(); departments = null; } // 其他方法保持不变... } ``` ### 关键修复点 1. **JNDI 名称标准化**: ```java @Stateless(name = "deptInfoRemote", mappedName = "java:global/DeptManagement/DeptInfoRemote") ``` 2. **接口-实现一致性**: - 确保 EJB 实现类正确实现接口 - 接口和实现类在正确的包结构中 3. **客户端注入修复**: ```java @EJB(mappedName = "java:global/DeptManagement/DeptInfoRemote") private DeptInfoRemote deptmentService; ``` 4. **添加后备查找机制**: - 当自动注入失败时,手动进行 JNDI 查找 5. **名称一致性**: - 统一使用 `deptInfoRemote` 作为 EJB 名称 - 避免拼写错误(如 `Serivce` → `Service`) ### 验证步骤 1. 部署 EJB 模块后,检查应用服务器的 JNDI 树: ``` java:global/DeptManagement/DeptInfoRemote ``` 2. 在客户端使用以下代码测试 JNDI 查找: ```java Context ctx = new InitialContext(); DeptInfoRemote service = (DeptInfoRemote) ctx.lookup("java:global/DeptManagement/DeptInfoRemote"); ``` 3. 确保客户端模块包含 EJB 接口的依赖: ```xml <!-- Maven 依赖示例 --> <dependency> <groupId>com.lkm</groupId> <artifactId>dept-ejb</artifactId> <version>1.0</version> <type>ejb</type> </dependency> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值