1. 概述
本文將演示如何集成 Spring 和遠程企業 JavaBean(EJB)。
為了實現這一點,我們將創建一些 EJB 以及必要的遠程接口,然後將在 JEE 容器中運行它們。之後,我們將啓動我們的 Spring 應用程序,並使用遠程接口實例化我們的 Bean,以便它們可以執行遠程調用。
如果您對 EJB 的含義或其工作原理有任何疑問,我們之前已經發布過一篇關於該主題的入門文章,您可以參考一下。
2. EJB 設置
我們需要創建遠程接口和 EJB 實現。為了使它們可用,我們還需要一個容器來託管和管理 Bean。
2.1. EJB 遠程接口
讓我們先定義兩個簡單的 Bean——一個無狀態 Bean和一個狀態 Bean。
我們將從它們的接口開始:
@Remote
public interface HelloStatefulWorld {
int howManyTimes();
String getHelloWorld();
}
@Remote
public interface HelloStatelessWorld {
String getHelloWorld();
}2.2. EJB 實現
現在,讓我們實現我們的遠程 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實例運行我們的應用程序。 如果您想設置自己的實例,這篇文章可能會有所幫助。 您可以配置一個配置文件,將應用程序部署到安裝的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>9990</username>
<password>admin1234!</password>
<filename>${project.build.finalName}.jar</filename>
</configuration>
</plugin>
</profile>
2.4. 運行 EJBs
配置完成後,我們可以直接從 Maven 命令行運行容器:
mvn clean package cargo:run -Pwildfly-runtime我們現在擁有一個運行實例的 Wildfly 正在託管我們的 Bean。我們可以通過日誌行來確認這一點:
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/HelloStatelessWorld3. Spring 部署
現在我們已經啓動了 JEE 容器,並部署了 EJBs,就可以開始我們的 Spring 應用。我們將使用 spring-boot-web 以簡化手動測試,但這並非遠程調用所必需。
3.1. Maven 依賴
為了連接到遠程 EJBs,我們需要 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>wildfly-ejb-client-bom的最新版本可以在 這裏 找到。
3.2. 命名策略上下文
有了這些依賴項在 classpath 中,我們可以實例化一個javax.naming.Context 來進行我們遠程 Bean 的查找。我們將它創建為 Spring 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 模式
為了在 Spring 容器中連接我們的遠程 Bean,我們需要知道如何訪問它們。為此,我們將使用它們的 JNDI 綁定。下面是這些綁定的標準模式:
${appName}/${moduleName}/${distinctName}/${beanName}!${viewClassName}請注意,由於我們部署了一個簡單的 jar 而不是 ear,並且沒有顯式設置名稱,因此我們沒有 appName 和 distinctName。有關更多詳細信息,請參閲我們的 EJB 簡介文章,以防出現任何不一致的情況。
我們將使用這種模式將我們的遠程 Bean 與 Spring Bean 綁定。
3.4. 構建我們的 Spring Bean
為了訪問我們的 EJB,我們將使用之前提到的 JNDI。 記得我們之前用來檢查企業 Bean 是否部署的日誌行嗎?
我們現在將看到這些信息的使用情況:
@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 綁定,否則上下文將無法訪問遠程 EJB 並創建必要的底層基礎設施。
請注意,從 Context 對象中調用 lookup 方法,如果在找不到您需要的 Bean 時將會拋出 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 命令來驗證它們是否按預期運行:
curl http://localhost:8081/stateless
Hello Stateless World!讓我們檢查一下我們的狀態化版本:
curl http://localhost:8081/stateful
Hello Stateful World called 1 times
curl http://localhost:8081/stateful
Hello Stateful World called 2 times5. 結論
在本文中,我們學習瞭如何將 Spring 集成到 EJB 中,並向 JEE 容器進行遠程調用。我們創建了兩個遠程 EJB 接口,並且能夠通過 Spring Bean 以透明的方式調用它們。
儘管 Spring 得到了廣泛的應用,但在企業環境中,EJB 仍然很受歡迎。在這個快速示例中,我們展示瞭如何同時利用 Jakarta EE 的分佈式優勢以及 Spring 應用程序的易用性。