Integration Guide for Spring and EJB – Spring和EJB的集成指南

最后修改: 2018年 1月 17日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll show how to integrate Spring and remote Enterprise Java Beans (EJB).

在这篇文章中,我们将展示如何整合Spring和远程企业Java Bean(EJB)

To do this, we’ll create some EJBs and the necessary remote interfaces, and then we’ll run them inside a JEE container. After that, we’ll start our Spring application and, using the remote interfaces, instantiate our beans so that they can execute remote calls.

要做到这一点,我们将创建一些EJB和必要的远程接口,然后在JEE容器中运行它们。之后,我们将启动我们的Spring应用程序,并使用远程接口来实例化我们的Bean,以便它们能够执行远程调用。

If there is any doubt about what EJBs are or how they work, we already published an introductory article on the topic here.

如果对什么是EJB或它们如何工作有任何疑问,我们已经发表了一篇关于这个主题的介绍性文章这里

2. EJB Setup

2.EJB设置

We’ll need to create our remote interfaces and our EJB implementations. To make them usable, we’ll also need a container to hold and manage beans.

我们需要创建我们的远程接口和我们的EJB实现。为了使它们能够使用,我们还需要一个容器来容纳和管理Bean。

2.1. EJB Remote Interfaces

2.1.EJB远程接口

Let’s start by defining two very simple beans — one stateless and one stateful.

让我们从定义两个非常简单的Bean开始–一个是无状态,一个是有状态。

We’ll begin with their interfaces:

我们将从他们的界面开始。

@Remote
public interface HelloStatefulWorld {
    int howManyTimes();
    String getHelloWorld();
}

@Remote
public interface HelloStatelessWorld {
    String getHelloWorld();
}

2.2. EJB Implementation

2.2.EJB的实现

Now, let’s implement our remote EJB interfaces:

现在,让我们来实现我们的远程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!";
    }
}

If stateful and stateless beans sound unfamiliar, this intro article may come in handy.

如果有状态Bean和无状态Bean听起来很陌生,那么这篇介绍文章可能会派上用场。

2.3. EJB Container

2.3.EJB容器

We can run our code in any JEE container, but for practicality purposes, we’ll use Wildfly and the cargo Maven plugin to do the heavy lifting for us:

我们可以在任何JEE容器中运行我们的代码,但为实用起见,我们将使用Wildfly和cargo Maven插件为我们完成重任。

<plugin>
    <groupId>org.codehaus.cargo</groupId>
    <artifactId>cargo-maven2-plugin</artifactId>
    <version>1.6.1</version>
    <configuration>
        <container>
            <containerId>wildfly10x</containerId>
            <zipUrlInstaller>
                <url>
                  http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.zip
                </url>
            </zipUrlInstaller>
        </container>
        <configuration>
            <properties>
                <cargo.hostname>127.0.0.1</cargo.hostname>
                <cargo.jboss.configuration>standalone-full</cargo.jboss.configuration>
                <cargo.jboss.management-http.port>9990</cargo.jboss.management-http.port>
                <cargo.servlet.users>testUser:admin1234!</cargo.servlet.users>
            </properties>
        </configuration>
    </configuration>
</plugin>

2.4. Running the EJBs

2.4.运行EJBs

With these configured, we can run the container directly from the Maven command line:

配置好这些后,我们就可以直接从Maven命令行运行容器了。

mvn clean package cargo:run -Pwildfly-standalone

We now have a working instance of Wildfly hosting our beans. We can confirm this by the log lines:

我们现在有一个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/HelloStatelessWorld

3. Spring Setup

3.Spring设置

Now that we have our JEE container up and running, and our EJBs deployed, we can start our Spring application. We’ll use spring-boot-web to make it easier to test manually, but it isn’t mandatory for the remote call.

现在我们的JEE容器已经启动并运行,我们的EJB也已部署,我们可以启动我们的Spring应用程序。我们将使用spring-boot-web以方便手动测试,但对于远程调用来说,这并不是必须的。

3.1. Maven Dependencies

3.1.Maven的依赖性

To be able to connect to the remote EJBs, we’ll need the Wildfly EJB Client library and our remote interface:

为了能够连接到远程EJB,我们将需要Wildfly EJB Client库和我们的远程接口。

<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>

The last version of wildfly-ejb-client-bom can be found here.

wildfly-ejb-client-bom的最新版本可以在这里找到。

3.2. Naming Strategy Context

3.2.命名策略的背景

With these dependencies in the classpath, we can instantiate a javax.naming.Context to do the lookup of our remote beans. We’ll create this as a Spring Bean so that we can autowire it when we need it:

有了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);
}

The properties are necessary to inform both the remote URL and the naming strategy context.

这些属性对于告知远程URL和命名策略上下文是必要的

3.3. JNDI Pattern

3.3.JNDI模式

Before we can wire our remote beans inside the Spring container, we’ll need to know how to reach them. For this, we’ll use their JNDI bindings. Let’s see the standard pattern for these bindings:

在我们能够在Spring容器中连接我们的远程Bean之前,我们需要知道如何到达它们。为此,我们将使用它们的JNDI绑定。让我们看看这些绑定的标准模式。

${appName}/${moduleName}/${distinctName}/${beanName}!${viewClassName}

Keep in mind that, since we deployed a simple jar instead of an ear and didn’t explicitly set up a name, we don’t have an appName and a distinctName. There are more details at our EJB Intro article in case something seems odd.

请记住,由于我们部署了一个简单的jar而不是ear,并且没有明确设置名称,因此我们没有appNamedistinctName。在我们的EJB介绍文章中有更多的细节,以防出现一些奇怪的现象。

We’ll use this pattern to bind our remote beans to our Spring ones.

我们将使用这种模式来绑定我们的远程Bean和我们的Spring Bean。

3.4. Building Our Spring Beans

3.4.构建我们的Spring Bean

To reach our EJBs, we’ll use the aforementioned JNDI. Remember log lines that we used to check if our enterprise beans were deployed?

为了到达我们的EJB,我们将使用前面提到的JNDI。还记得我们用来检查我们的企业Bean是否被部署的日志线吗?

We’ll see that information in use now:

我们现在就来看看这些信息的使用情况。

@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;
}

We need to be very careful about the correct full JNDI binding, or the context won’t be able to reach the remote EJB and create the necessary underlying infrastructure.

我们需要非常注意正确的完整JNDI绑定,否则上下文将无法到达远程EJB并创建必要的底层基础设施。

Keep in mind that the method lookup from Context will throw a NamingException in case it doesn’t find the bean you are requiring.

请记住,从Context 出发的lookup 方法将抛出一个NamingException,以防它找不到你所需要的bean。

4. Integration

4.整合

With everything in place, we can inject our beans in a controller, so we can test if the wiring is right:

一切就绪后,我们可以在控制器中注入我们的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";
    }
}

Let’s start our Spring server and check some logs. We’ll see the following line, indicating that everything is OK:

让我们启动我们的Spring服务器并检查一些日志。我们会看到下面一行,表明一切正常。

EJBCLIENT000013: Successful version handshake completed

Now, let’s test our stateless bean. We can try some curl commands to verify that they’re operating as expected:

现在,让我们测试一下我们的无状态Bean。我们可以尝试一些curl命令来验证它们是否按预期操作。

curl http://localhost:8081/stateless
Hello Stateless World!

And let’s check our stateful one:

让我们检查一下我们的有状态的那个。

curl http://localhost:8081/stateful
Hello Stateful World called 1 times

curl http://localhost:8081/stateful
Hello Stateful World called 2 times

5. Conclusion

5.结论

In this article, we learned how to integrate Spring to EJB and make remote calls to the JEE container. We created two remote EJB interfaces, and we were able to call those using Spring Beans in a transparent way.

在这篇文章中,我们学习了如何将Spring集成到EJB并对JEE容器进行远程调用。我们创建了两个远程EJB接口,并且能够以透明的方式使用Spring Bean来调用这些接口。

Even though Spring is widely adopted, EJBs are still popular in enterprise environments, and in this quick example, we’ve shown that it’s possible to make use of both the distributed gains of Jakarta EE and the ease of use of Spring applications.

尽管Spring被广泛采用,但EJB在企业环境中仍然很受欢迎,在这个快速的例子中,我们已经表明可以同时利用Jakarta EE的分布式收益和Spring应用程序的易用性。

As always, the code can be found over on GitHub.

一如既往,代码可以在GitHub上找到