一、前言
本文仅代表作者的个人观点;
本文的内容仅限于技术探讨,不能作为指导生产环境的素材;
本文素材是红帽公司产品技术和手册;
本文分为系列文章,将会有多篇,初步预计将会有9篇。
二、对EJB的访问方式
EJB是一个包含在应用程序服务器上运行的业务逻辑的可移植组件。 如果客户端和EJB是同一应用程序的一部分,则客户端可以在本地访问EJB,如果EJB在远程运行,则客户端可以通过远程接口访问EJB。
如果客户端和EJB是本地的,也就是说,它们在相同的JVM进程中运行,则客户端可以调用EJB中的所有公共方法。 在EJB远程的情况下,必须提供一个远程接口,它是一个公开EJB业务方法的简单Java接口。 EJB类实现远程接口中的方法,其实现细节对客户端是隐藏的。
使用@EJB注释访问本地EJB
假设已经定义了如下的EJB:
@Stateless
public class TodoBean {
public void addTodo(TodoItem item) {
...
}
public void findTodo(int id) {
...
}
...
}
...
}
客户可以通过使用@EJB注释将EJB直接注入到代码中来调用EJB上的方法:
public class TodoClient { @EJB
TodoBean todo;
TodoItem item = new TodoItem();
item.setDescription("Buy milk");
item.setStatus("PENDING");
//invoke EJB methods
todo.addTodo(item);
...
}
访问远程EJB
在客户机在Java EE应用程序服务器的上下文之外运行的情况下,或者在应用程序服务器上运行的Java EE组件需要访问部署在远程应用程序服务器上的另一个EJB的情况下,可以使用JNDI来查找EJB。
为了确保远程客户端可以使用EJB,必须声明一个列出EJB业务方法的接口,并让EJB实现并覆盖这些方法。 例如,假设希望提供执行各种数学操作的EJB,请声明一个接口并列出如下所示的方法:
package com.redhat.training.ejb;public interface Calculator {
public int add(int a, int b);
public int multiply(int a, int b);
...
}
我们现在必须在EJB中提供这些方法的具体实现,并通过使用@Remote注释指出Calculator是EJB的远程接口:
package com.redhat.training.ejb;@Stateless@Remote(Calculator.class)
public class CalculatorBean implements Calculator {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public int multiply(int a, int b) {
return a * b;
}
...
}
...
}
我们的EJB现在可以打包并部署在应用服务器上,并可以为远程客户端提供服务。
三、使用JNDI查找远程EJB
Java EE标准为客户端指定了标准的JNDI查找方案来查找EJB。 示例如下:
/<application-name>/<module-name>/<bean-name>!<fully-qualified-interface-name>
application-name:应用程序名称是部署EJB的EAR的名称(没有.ear扩展名)。 如果EJB JAR没有在EAR中部署,那么这是空白的。 应用程序名称也可以在EAR的application.xml部署描述符中指定。
module-name:默认情况下,模块名称是EJB JAR文件的名称(不带.jar后缀)。 模块名称可以在ejb-jar.xml部署描述符中重写。
bean-name:要调用的EJB的名称(实现类)。
fully-qualified-interface-name:远程接口的完全限定类名。 包括完整的软件包名称。
考虑到上面的代码清单,假设EJB打包在名为calculator-ejb.jar的文件中,该文件被进一步打包到名为myapp.ear的EAR文件中。 客户端可以使用以下查找字符串查找EJB:
myapp/calculator-ejb/CalculatorBean!com.redhat.training.ejb.Calculator
在部署EJB时,应用程序服务器会在服务器日志中列出EJB的不同JNDI绑定。 下面的清单显示了如果将EJB打包并部署为JAR文件,而不是EAR文件,则显示JNDI条目:
INFO [org.jboss.as.ejb3.deployment] (MSC service thread 1-2) WFLYEJB0473: JNDI bindings for session bean named 'CalculatorBean' in deployment unit 'deployment "calculator-ejb.jar"' are as follows:java:global/calculator-ejb/CalculatorBean!com.redhat.training.ejb.Calculator
java:app/calculator-ejb/CalculatorBean!com.redhat.training.ejb.Calculator
java:module/CalculatorBean!com.redhat.training.ejb.Calculator
java:global/calculator-ejb/CalculatorBean
java:app/calculator-ejb/CalculatorBean
java:module/CalculatorBean
使用JNDI命名机制查找远程EJB的示例JNDI客户机程序如下所示:
package com.redhat.training.client;
public class CalculatorClient {
public static void main(String[] args) throws Exception { String JNDI_URL= "myapp/calculator-ejb/CalculatorBean!com.redhat.training.ejb.Calculator";
try {
Context ic = new InitialContext(); Calculator calc = (Calculator) ic.lookup(JNDI_URL);
System.out.println("Response from server = " + calc.add(1,2);
...
}
catch (Exception e) {
// handle the exception
}
}
...
}
我们还需要在客户端程序的类路径中提供一个名为jndi.properties的文件,其中包含运行EJB的远程应用程序服务器的主机名,IP地址,端口和安全详细信息(如果安全用于远程访问)。
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=http-remoting://10.2.0.15:8080
jboss.naming.client.ejb.context=true
JNDI API的InitialContext是一个标准的Java EE通用构造,用于查找部署在应用程序服务器上的组件。 它使用一组属性在类路径中查找jndi.properties。 某些属性对所有应用程序服务器都是通用的,有些属性是针对每个应用程序服务器的。
四、实验展现
实验由两个Maven子项目组成,它们位于hello-remote目录下的主项目文件夹下,其中包含hello-remote-ejb和hello-remote-client。
hello-remote-ejb项目在JBoss EAP中安装可远程访问的EJB,以便通过JNDI查找可用于外部客户端。 hello-remote-client项目是远程访问(从另一个JVM)EJB的Java SE应用程序。
首先,在JBDS中import已经存在的maven项目:
接下来,再import client:
查看hello-remote-ejb的pom.xml:
在源码的如下部分, 使用maven-ejb-plugin为ejb打包:
package已被声明为ejb,这告诉Maven如何打包最终的可部署artifact:
查看业务接口:HelloRemote.java文件:
这是一个简单的Java接口,它带有一个公共方法sayHello(一个class),它接受一个字符串名称参数并返回一个字符串。 在使用EJB时,通常使用接口来定义可用的方法,而不考虑实现。
查看最终执行任务的类的源码:HelloBean.java文件。
注意到这个EJB类实现了HelloRemote接口的sayHello方法,并且注意到标记这个类为无状态EJB的@Stateless注解。
接下来,启动EAP:
接下来,通过运行以下命令来构建和部署EJB到JBoss EAP:
查看EAP日志,hello-ejb-remote.jar已经被部署到EAP中:
JBoss EAP要求将EJB绑定在java:jboss / exported / *名称空间下,以允许外部客户端查找和调用EJB。
注意到没有“导出的”JNDI绑定。 您需要为EJB提供一个远程接口,以便将EJB绑定在该名称空间下。
编辑EJB项目的实现类HelloBean.java以启用远程JNDI查找并重新部署应用程序。
编辑实现类HelloBean.java以启用远程JNDI查找。 将@Remote注释添加到您的实现类并保存该文件,在源码中增加以下两行:
重新编译和部署hello-ejb-remote:
再次观察JNDI绑定。 这次我们可以在JBDS Console选项卡中看到导出的JNDI绑定:
使用Maven将hello-remote-ejb构件安装到本地存储库中,以便在编译期间可供客户端项目使用:
接下来,查看hello-client项目的源代码,并更新它以使用JNDI查找HelloBean。
在JBDS左窗格的Project Explorer选项卡中展开hello-client项目,然后双击pom.xml文件。
单击pom.xml选项卡查看pom.xml,我们可以看到对hello-remote-ejb的依赖(需要远程调用它)依赖关系的类型是ejb-client。 这告诉Maven这个工件是用于代码编译的hello-remote-ejb工件中定义的EJB的客户端。
查看HelloClient.java源码,将下面突出的部分修改:
修改成如下样子:
更新jndi.properties文件(src/main/resources)以使用http-remoting来访问在本地JBoss EAP服务器上运行的EJB。
将java.naming.provider.url属性设置为值http-remoting://127.0.0.1:8080,如以下图例所示:
修改成:
接下来,编译并运行客户端:
最后一行的reponse说明客户端对ejb调用成功!
魏新宇