WildFly应用服务器上的EJB JNDI查找介绍

2023/05/11

1. 概述

Enterprise Java Beans(EJB)是Java EE规范的核心部分,旨在简化分布式企业级应用程序的开发。EJB的生命周期由应用服务器处理,例如JBoss WildFlyOracle GlassFish

EJB提供了一个健壮的编程模型,有助于企业级软件模块的实现,因为它取决于应用程序服务器来处理非业务逻辑相关的问题,例如事务处理、组件生命周期管理或依赖注入。

此外,我们已经发表了两篇涵盖EJB基本概念的文章,所以请随时在此处此处查看它们。

在本教程中,我们将展示如何在WildFly上实现基本的EJB模块,以及如何通过JNDI从远程客户端调用EJB。

2. 实现EJB模块

业务逻辑由一个或多个本地/远程业务接口(也称为本地/远程视图),或直接通过不实现任何接口的类(非视图接口)实现。

值得注意的是,当要从驻留在相同环境中的客户端(即相同的EAR或WAR文件)访问bean时,将使用本地业务接口,而当从不同环境访问bean时(即不同的JVM或应用程序服务器),则需要远程业务接口。

让我们创建一个基本的EJB模块,该模块仅由一个bean组成。bean的业务逻辑将很简单,仅限于将给定的String转换为其大写版本。

2.1 定义远程业务接口

让我们首先定义一个单一的远程业务接口,用@Remote注解修饰。根据EJB 3.x规范,这是强制性的,因为要从远程客户端访问bean:

@Remote
public interface TextProcessorRemote {
    String processText(String text);
}

2.2 定义无状态Bean

接下来我们通过实现上述远程接口来实现业务逻辑:

@Stateless
public class TextProcessorBean implements TextProcessorRemote {
    public String processText(String text) {
        return text.toUpperCase();
    }
}

TextProcessorBean类是一个简单的Java类,用@Stateless注解标注。

根据定义,无状态bean不维护与客户端的任何对话状态,即使它们可以跨不同请求维护实例状态。它们的对应物有状态bean会保留它们的对话状态,例如为应用程序服务器创建它们的成本更高。

在这种情况下,上面的类没有任何实例状态,所以它可以成为无状态的。如果它有一个状态,那么在不同的客户端请求中使用它根本就没有意义。

bean的行为是确定性的,即它没有副作用,就像一个设计良好的bean应该的那样:它只需要一个输入字符串并返回它的大写版本。

2.3 Maven依赖项

接下来,我们需要将javaee-api Maven工件添加到模块中,它提供所有Java EE 7规范API,包括EJB所需的API:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>7.0</version>
    <scope>provided</scope>
</dependency>

至此,我们已经成功地创建了一个基本的但功能齐全的EJB模块。为了让所有潜在客户端都可以使用它,我们必须将工件作为JAR文件添加到我们的本地Maven仓库中。

2.4 将EJB模块安装到本地仓库

有几种方法可以实现这一点。最直接的一个包括执行Maven生命周期clean – install构建阶段:

mvn clean install

此命令将EJB模块作为ejbmodule-1.0.jar(或pom.xml文件中指定的任意工件ID)安装到我们的本地仓库中。有关如何使用Maven安装本地JAR的更多信息,请查看这篇文章

假设EJB模块已正确安装到我们的本地仓库中,下一步是开发一个使用我们的TextProcessorBean API的远程客户端应用程序。

3. 远程EJB客户端

我们将使远程EJB客户端的业务逻辑非常简单:首先,它执行JNDI查找以获取TextProcessorBean代理。之后,它调用代理的processText()方法。

3.1 Maven依赖项

我们需要包含以下Maven工件,以便EJB客户端按预期工作:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>7.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.wildfly</groupId>
    <artifactId>wildfly-ejb-client-bom</artifactId>
    <version>10.1.0.Final</version>
</dependency>
<dependency>
    <groupId>cn.tuyucheng.taketoday.ejbmodule</groupId>
    <artifactId>ejbmodule</artifactId>
    <version>1.0</version>
</dependency>
虽然我们包含javaee-api工件的原因非常明显,但包含[wildfly-ejb-client-bom](https://search.maven.org/classic/#search ga 1 wildfly-ejb-client-bom)却不是。在WildFly上执行远程EJB调用需要该工件

最后但同样重要的是,我们需要使以前的EJB模块对客户端可用,因此我们也添加了ejbmodule依赖项。

3.2 EJB客户端类

考虑到EJB客户端调用TextProcessorBean的代理,我们将非常务实并将客户端类命名为TextApplication:

public class TextApplication {

    public static void main(String[] args) throws NamingException {
        TextProcessorRemote textProcessor = EJBFactory
                .createTextProcessorBeanFromJNDI("ejb:");
        System.out.print(textProcessor.processText("sample text"));
    }

    private static class EJBFactory {
        private static TextProcessorRemote createTextProcessorBeanFromJNDI(String namespace) throws NamingException {
            return lookupTextProcessorBean(namespace);
        }

        private static TextProcessorRemote lookupTextProcessorBean(String namespace) throws NamingException {
            Context ctx = createInitialContext();
            String appName = "";
            String moduleName = "EJBModule";
            String distinctName = "";
            String beanName = TextProcessorBean.class.getSimpleName();
            String viewClassName = TextProcessorRemote.class.getName();
            return (TextProcessorRemote) ctx.lookup(namespace
                                                    + appName + "/" + moduleName
                                                    + "/" + distinctName + "/" + beanName + "!" + viewClassName);
        }

        private static Context createInitialContext() throws NamingException {
            Properties jndiProperties = new Properties();
            jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
            jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
            jndiProperties.put(Context.PROVIDER_URL, "http-remoting://localhost:8080");
            jndiProperties.put("jboss.naming.client.ejb.context", true);
            return new InitialContext(jndiProperties);
        }
    }
}

简而言之,TextApplication类所做的就是检索bean代理并使用示例字符串调用它的processText()方法。

实际查找由嵌套类EJBFactory执行,它首先创建一个JNDI InitialContext实例,然后将所需的JNDI参数传递给构造函数,最后使用它来查找bean代理。

请注意,查找是使用WildFly专有的“ejb:”命名空间执行的。这优化了查找过程,因为客户端延迟到服务器的连接,直到显式调用代理。

还值得注意的是,根本不用求助于“ejb”命名空间就可以查找bean代理。但是,我们会失去惰性网络连接的所有额外好处,从而使客户端的性能大大降低

3.3 设置EJB上下文

客户端应该知道要与哪个主机和端口建立连接以执行bean查找。为此,客户端需要设置专有的WildFly EJB上下文,该上下文由放置在其类路径中的jboss-ejb-client.properties文件定义,通常位于src/main/resources文件夹下:

endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=127.0.0.1
remote.connection.default.port=8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.default.username=myusername
remote.connection.default.password=mypassword

该文件不言自明,因为它提供了与WildFly建立连接所需的所有参数,包括远程连接的默认数量、默认主机和端口以及用户凭据。在这种情况下,连接未加密,但可以在启用SSL时加密。

最后要考虑的是,如果连接需要身份验证,则有必要通过add-user.sh/add-user.bat实用程序将用户添加到WildFly

4. 总结

在WildFly上执行EJB查找非常简单,只要我们严格遵守概述的流程即可。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章