1. 概述
本文将演示如何集成Spring与远程企业级Java Bean(EJB)。我们将创建EJB及其远程接口,在JEE容器中运行它们,然后启动Spring应用,通过远程接口实例化Bean执行远程调用。
⚠️ 如果对EJB概念不熟悉,可参考入门文章(此处保留原文链接,实际使用时需替换为有效链接)
2. EJB环境搭建
2.1. EJB远程接口
先定义两个简单Bean——一个有状态(Stateful)和一个无状态(Stateless):
@Remote
public interface HelloStatefulWorld {
int howManyTimes();
String getHelloWorld();
}
@Remote
public interface HelloStatelessWorld {
String getHelloWorld();
}
2.2. EJB实现类
实现远程接口:
@Stateful(name = "HelloStatefulWorld")
public class HelloStatefulWorldBean implements HelloStatefulWorld {
private int howManyTimes = 0;
public int howManyTimes() {
return howManyTimes;
}
public String getHelloWorld() {
howManyTimes++;
return "Hello Stateful World";
}
}
@Stateless(name = "HelloStatelessWorld")
public class HelloStatelessWorldBean implements HelloStatelessWorld {
public String getHelloWorld() {
return "Hello Stateless World!";
}
}
✅ 有状态/无状态Bean的区别可参考入门文章
2.3. EJB容器配置
使用Wildfly实例运行应用。配置Maven插件部署到Wildfly:
<profile>
<id>wildfly-runtime</id>
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>1.1.0.Alpha5</version>
<configuration>
<hostname>127.0.0.1</hostname>
<port>9990</port>
<username>admin</username>
<password>admin1234!</password>
<filename>${project.build.finalName}.jar</filename>
</configuration>
</plugin>
</profile>
2.4. 启动EJB服务
通过Maven命令启动容器:
mvn clean package cargo:run -Pwildfly-runtime
成功部署后日志会显示JNDI绑定信息:
java:global/ejb-remote-for-spring/HelloStatefulWorld!com.baeldung.ejb.tutorial.HelloStatefulWorld
java:app/ejb-remote-for-spring/HelloStatefulWorld!com.baeldung.ejb.tutorial.HelloStatefulWorld
java:module/HelloStatefulWorld!com.baeldung.ejb.tutorial.HelloStatefulWorld
java:jboss/exported/ejb-remote-for-spring/HelloStatefulWorld!com.baeldung.ejb.tutorial.HelloStatefulWorld
java:global/ejb-remote-for-spring/HelloStatefulWorld
java:app/ejb-remote-for-spring/HelloStatefulWorld
java:module/HelloStatefulWorld
java:global/ejb-remote-for-spring/HelloStatelessWorld!com.baeldung.ejb.tutorial.HelloStatelessWorld
java:app/ejb-remote-for-spring/HelloStatelessWorld!com.baeldung.ejb.tutorial.HelloStatelessWorld
java:module/HelloStatelessWorld!com.baeldung.ejb.tutorial.HelloStatelessWorld
java:jboss/exported/ejb-remote-for-spring/HelloStatelessWorld!com.baeldung.ejb.tutorial.HelloStatelessWorld
java:global/ejb-remote-for-spring/HelloStatelessWorld
java:app/ejb-remote-for-spring/HelloStatelessWorld
java:module/HelloStatelessWorld
3. Spring环境搭建
3.1. Maven依赖
添加Wildfly EJB客户端库和远程接口依赖:
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<version>10.1.0.Final</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.baeldung.spring.ejb</groupId>
<artifactId>ejb-remote-for-spring</artifactId>
<version>1.0.1</version>
<type>ejb</type>
</dependency>
✅ 最新版本可在Maven仓库查询
3.2. 命名策略上下文
创建javax.naming.Context
Bean用于远程查找:
@Bean
public Context context() throws NamingException {
Properties jndiProps = new Properties();
jndiProps.put("java.naming.factory.initial",
"org.jboss.naming.remote.client.InitialContextFactory");
jndiProps.put("jboss.naming.client.ejb.context", true);
jndiProps.put("java.naming.provider.url",
"http-remoting://localhost:8080");
return new InitialContext(jndiProps);
}
❌ 踩坑提醒:必须正确配置URL和命名工厂,否则连接会失败
3.3. JNDI查找模式
标准JNDI绑定模式:
${appName}/${moduleName}/${distinctName}/${beanName}!${viewClassName}
⚠️ 因我们部署的是简单jar(非ear)且未显式设置名称,所以
appName
和distinctName
为空
3.4. 构建Spring Bean
使用JNDI查找远程EJB:
@Bean
public HelloStatelessWorld helloStatelessWorld(Context context)
throws NamingException {
return (HelloStatelessWorld)
context.lookup(this.getFullName(HelloStatelessWorld.class));
}
@Bean
public HelloStatefulWorld helloStatefulWorld(Context context)
throws NamingException {
return (HelloStatefulWorld)
context.lookup(this.getFullName(HelloStatefulWorld.class));
}
private String getFullName(Class classType) {
String moduleName = "ejb-remote-for-spring/";
String beanName = classType.getSimpleName();
String viewClassName = classType.getName();
return moduleName + beanName + "!" + viewClassName;
}
❌ 关键点:JNDI绑定名称必须完全匹配,否则会抛出
NamingException
4. 集成测试
在控制器中注入Bean测试集成:
@RestController
public class HomeEndpoint {
// ...
@GetMapping("/stateless")
public String getStateless() {
return helloStatelessWorld.getHelloWorld();
}
@GetMapping("/stateful")
public String getStateful() {
return helloStatefulWorld.getHelloWorld()
+ " called " + helloStatefulWorld.howManyTimes() + " times";
}
}
启动Spring服务,成功日志:
EJBCLIENT000013: Successful version handshake completed
测试无状态Bean:
curl http://localhost:8081/stateless
Hello Stateless World!
测试有状态Bean(注意调用次数递增):
curl http://localhost:8081/stateful
Hello Stateful World called 1 times
curl http://localhost:8081/stateful
Hello Stateful World called 2 times
5. 总结
我们实现了Spring与EJB的集成,通过远程接口透明调用JEE容器中的EJB。关键步骤包括:
- ✅ 创建EJB远程接口和实现类
- ✅ 配置Wildfly容器并部署EJB
- ✅ 在Spring中配置JNDI上下文
- ✅ 使用JNDI模式查找远程Bean
- ✅ 通过Spring控制器验证集成效果
即使Spring广泛流行,EJB在企业环境中仍有应用。本示例展示了如何结合Jakarta EE的分布式能力与Spring的易用性。
完整代码见GitHub仓库(保留原文链接)