«1. Обзор

В этой статье мы покажем, как интегрировать Spring и удаленные Enterprise Java Beans (EJB).

Для этого мы создадим несколько EJB и необходимые удаленные интерфейсы, а затем запустим их внутри JEE-контейнера. После этого мы запустим наше приложение Spring и, используя удаленные интерфейсы, создадим экземпляры наших компонентов, чтобы они могли выполнять удаленные вызовы.

Если есть какие-либо сомнения относительно того, что такое EJB или как они работают, мы уже публиковали вводную статью по этой теме здесь.

2. Настройка EJB

Нам нужно создать наши удаленные интерфейсы и наши реализации EJB. Чтобы сделать их пригодными для использования, нам также понадобится контейнер для хранения бинов и управления ими.

2.1. Удаленные интерфейсы EJB

Давайте начнем с определения двух очень простых bean-компонентов — одного без состояния и одного с состоянием.

Начнем с их интерфейсов:

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

Теперь давайте реализуем наши удаленные интерфейсы EJB:

Если bean-компоненты с состоянием и без состояния звучат незнакомо, эта вводная статья может оказаться полезной.

<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.3. Контейнер EJB

Мы можем запускать наш код в любом контейнере JEE, но из соображений практичности мы будем использовать Wildfly и плагин Cargo Maven, который сделает всю тяжелую работу за нас:

mvn clean package cargo:run -Pwildfly-standalone

2.4. Запуск EJB

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

После их настройки мы можем запустить контейнер непосредственно из командной строки Maven:

Теперь у нас есть работающий экземпляр Wildfly, на котором размещены наши bean-компоненты. Мы можем подтвердить это строками журнала:

3. Установка Spring

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

Теперь, когда у нас запущен и работает наш JEE-контейнер и развернуты наши EJB, мы можем запустить наше приложение Spring. Мы будем использовать spring-boot-web, чтобы упростить тестирование вручную, но это не обязательно для удаленного вызова.

3.1. Зависимости Maven

Чтобы иметь возможность подключаться к удаленным EJB, нам понадобится клиентская библиотека Wildfly EJB и наш удаленный интерфейс:

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

Последнюю версию wildfly-ejb-client-bom можно найти здесь. .

3.2. Контекст стратегии именования

Имея эти зависимости в пути к классам, мы можем создать экземпляр javax.naming.Context для поиска наших удаленных компонентов. Мы создадим его как Spring Bean, чтобы мы могли автоматически связывать его, когда нам это нужно:

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

Свойства необходимы для информирования как удаленного URL-адреса, так и контекста стратегии именования.

3.3. Шаблон JNDI

Прежде чем мы сможем связать наши удаленные bean-компоненты внутри контейнера Spring, нам нужно знать, как получить к ним доступ. Для этого мы будем использовать их привязки JNDI. Давайте посмотрим на стандартный шаблон для этих привязок:

Имейте в виду, что, поскольку мы развернули простую банку вместо уха и не указали имя явно, у нас нет appName и отдельного имени. . Дополнительные подробности можно найти в нашей статье EJB Intro на случай, если что-то покажется странным.

Мы будем использовать этот шаблон для привязки наших удаленных bean-компонентов к нашим Spring.

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

3.4. Построение наших Spring Beans

Чтобы получить доступ к нашим EJB, мы будем использовать вышеупомянутый JNDI. Помните строки журнала, которые мы использовали для проверки того, были ли развернуты наши корпоративные компоненты?

Теперь мы увидим, что эта информация используется:

@RestController
public class HomeEndpoint {
 
    // ...
 
    @GetMapping("/stateless")
    public String getStateless() {
        return helloStatelessWorld.getHelloWorld();
    }
    
    @GetMapping("/stateful")
    public String getStateful() {
        return helloStatefulWorld.getHelloWorld()
          + " called " + helloStatefulWorld.howManyTimes() + " times";
    }
}

Нам нужно быть очень осторожными с правильной полной привязкой JNDI, иначе контекст не сможет достичь удаленного EJB и создать необходимую базовую инфраструктуру.

EJBCLIENT000013: Successful version handshake completed

Имейте в виду, что поиск метода из Context вызовет исключение NamingException, если он не найдет требуемый компонент.

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

4. Интеграция

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

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

Когда все готово, мы можем внедрить наши bean-компоненты в контроллер, чтобы проверить правильность подключения:

Давайте запустим наш сервер Spring и проверим некоторые журналы. Мы увидим следующую строку, указывающую на то, что все в порядке:

Теперь давайте проверим наш bean-компонент без сохранения состояния. Мы можем попробовать некоторые команды curl, чтобы убедиться, что они работают должным образом:

И давайте проверим нашу команду с сохранением состояния: