EJB 简介
1. 概述
在本文中,我们将讨论如何开始进行 Enterprise JavaBean (EJB) 开发。
Enterprise JavaBeans 用于开发可扩展的分布式服务器端组件,通常封装应用程序的业务逻辑。
我们将使用WildFly 10.1.0 作为我们首选的服务器解决方案,但是,您可以自由使用您选择的任何 Java Enterprise 应用程序服务器。
2. 设置
让我们首先讨论 EJB 3.2 开发所需的 Maven 依赖项,以及如何使用 Maven Cargo 插件或手动配置 WildFly 应用程序服务器。
2.1. Maven 依赖
为了使用 EJB 3.2 ,请确保将最新版本添加到pom.xml文件的dependency项部分:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
您将在Maven 存储库 中找到最新的依赖项。这种依赖关系确保所有 Java EE 7 API 在编译期间都可用。提供的范围确保一旦部署,依赖项将由部署它的容器提供。
2.2. 使用 Maven Cargo 设置 WildFly
让我们谈谈如何使用 Maven Cargo 插件来设置服务器。
以下是配置 WildFly 服务器的 Maven 配置文件的代码:
<profile>
<id>wildfly-standalone</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>${cargo-maven2-plugin.version</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.0</cargo.hostname>
<cargo.jboss.management-http.port>
9990
</cargo.jboss.management-http.port>
<cargo.servlet.users>
testUser:admin1234!
</cargo.servlet.users>
</properties>
</configuration>
</configuration>
</plugin>
</plugins>
</build>
</profile>
我们使用插件直接从 WildFly 的网站下载WildFly 10.1 zip。然后通过确保hostname是127.0.0.1并将端口设置为 9990 来配置它。
然后我们使用cargo.servlet.users属性创建一个测试用户,用户 ID为 testUser,密码为admin1234!。
现在插件的配置已经完成,我们应该能够调用一个 Maven 目标并下载、安装、启动服务器并部署应用程序。
为此,导航到ejb-remote目录并运行以下命令:
mvn clean package cargo:run
首次运行此命令时,它将下载 WildFly 10.1 zip 文件,解压缩并执行安装,然后启动它。它还将添加上面讨论的测试用户。任何进一步的执行都不会再次下载 zip 文件。
2.3. WildFly 的手动设置
为了手动设置 WildFly,您必须自己从wildfly.org 网站下载安装 zip 文件。以下步骤是 WildFly 服务器设置过程的高级视图:
将文件内容下载并解压到要安装服务器的位置后,配置以下环境变量:
JBOSS_HOME=/Users/$USER/../wildfly.x.x.Final
JAVA_HOME=`/usr/libexec/java_home -v 1.8`
然后在bin目录中,针对基于 Linux 的操作系统运行*./standalone.sh或针对 Windows 运行./standalone.bat*。
在此之后,您将必须添加一个用户。该用户将用于连接到远程 EJB bean。要了解如何添加用户,您应该查看“添加用户”文档 。
有关详细的设置说明,请访问 WildFly 的入门文档 。
通过设置两个配置文件,项目 POM 已配置为与 Cargo 插件和手动服务器配置一起使用。默认情况下,Cargo 插件被选中。但是,要将应用程序部署到已安装、配置和运行的 Wildfly 服务器,请在ejb-remote目录中执行以下命令:
mvn clean install wildfly:deploy -Pwildfly-runtime
3. Remote与Local
bean 的业务接口可以是Local的或Remote的。
只有在与进行调用的 bean 位于同一应用程序中时,才能访问带有 @Local 注解的 bean,即,如果它们位于相同的*.ear或.war*中。
可以从不同的应用程序(即驻留在不同JVM或应用程序服务器中的应用程序)访问带有*@Remote*注解的 bean。
在设计包含 EJB 的解决方案时,需要牢记以下几点:
- 当使用*@Local或@Remote声明 bean 时,始终排除java.io.Serializable*、java.io.Externalizable和javax.ejb包定义的接口
- 如果一个 bean 类是远程的,那么所有实现的接口都是远程的
- 如果一个 bean 类不包含注解或者如果指定了*@Local*注解,那么所有实现的接口都被假定为本地的
- 为不包含接口的 bean 显式定义的任何接口都必须声明为*@Local*
- EJB 3.2 版本倾向于为需要显式定义本地和远程接口的情况提供更多粒度
4. 创建Remote EJB
让我们首先创建 bean 的接口并将其命名为HelloWorld:
@Remote
public interface HelloWorld {
String getHelloWorld();
}
现在我们将实现上述接口并将具体实现命名为HelloWorldBean:
@Stateless(name = "HelloWorld")
public class HelloWorldBean implements HelloWorld {
@Resource
private SessionContext context;
@Override
public String getHelloWorld() {
return "Welcome to EJB Tutorial!";
}
}
请注意类声明上的*@Stateless*注解。它表示这个 bean 是一个无状态会话 bean。这种 bean 没有任何关联的客户端状态,但它可以保留其实例状态,通常用于进行独立操作。
@Resource注解将会话上下文注入远程 bean。
SessionContext接口提供对容器为会话 bean 实例提供的运行时会话上下文的访问。然后,容器在创建实例后将SessionContext接口传递给实例。会话上下文在其生命周期内保持与该实例相关联。
EJB 容器通常会创建一个无状态 bean 对象池,并使用这些对象来处理客户端请求。由于这种池化机制,实例变量值不能保证在查找方法调用中得到维护。
5. 远程设置
在本节中,我们将讨论如何设置 Maven 以在服务器上构建和运行应用程序。
让我们一一看一下插件。
5.1. EJB 插件
下面给出的 EJB 插件用于打包 EJB 模块。我们已将 EJB 版本指定为 3.2。
以下插件配置用于设置 bean 的目标 JAR:
<plugin>
<artifactId>maven-ejb-plugin</artifactId>
<version>2.4</version>
<configuration>
<ejbVersion>3.2</ejbVersion>
</configuration>
</plugin>
5.2. 部署远程 EJB
要在 WildFly 服务器中部署 bean,请确保服务器已启动并正在运行。
然后要执行远程设置,我们需要对ejb-remote项目中的 pom 文件运行以下 Maven 命令:
mvn clean install
然后我们应该运行:
mvn wildfly:deploy
或者,我们可以从应用服务器的管理控制台以admin用户身份手动部署它。
6. 客户端设置
创建远程 bean 后,我们应该通过创建客户端来测试部署的 bean。
首先,让我们讨论一下客户端项目的 Maven 设置。
6.1.客户端 Maven 设置
为了启动 EJB3 客户端,我们需要添加以下依赖项:
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<type>pom</type>
<scope>import</scope>
</dependency>
我们依赖此应用程序的 EJB 远程业务接口来运行客户端。所以我们需要指定 EJB 客户端 JAR 依赖项。我们在父 pom 中添加以下内容:
<dependency>
<groupId>com.blogdemo.ejb</groupId>
<artifactId>ejb-remote</artifactId>
<type>ejb</type>
</dependency>
<type>被指定为ejb。
6.2. 访问远程 Bean
我们需要在src/main/resources下创建一个文件并将其命名为jboss-ejb-client.properties,其中将包含访问部署的 bean 所需的所有属性:
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.connect.options.org.xnio.Options
.SASL_POLICY_NOPLAINTEXT = false
remote.connection.default.connect.options.org.xnio.Options
.SASL_DISALLOWED_MECHANISMS = ${host.auth:JBOSS-LOCAL-USER}
remote.connection.default.username=testUser
remote.connection.default.password=admin1234!
7. 创建客户端
将在 com.blogdemo.ejb.client 包中的EJBClient.java中创建将访问和使用远程HelloWorld bean的类。
7.1. 远程 Bean URL
远程 bean 通过符合以下格式的 URL 定位:
ejb:${appName}/${moduleName}/${distinctName}/${beanName}!${viewClassName}
- *${appName}*是部署的应用程序名称。这里我们没有使用任何 EAR 文件而是一个简单的 JAR 或 WAR 部署,因此应用程序名称将为空
- ${moduleName}是我们之前为部署设置的名称,所以它是ejb-remote
- ${distinctName}是一个特定的名称,可以选择分配给部署在服务器上的部署。如果部署不使用distinct-name,那么我们可以在 JNDI 名称中使用空字符串作为distinct-name,就像我们在示例中所做的那样
- ${beanName} 变量是 EJB 实现类的简单名称,所以在我们的示例中它是HelloWorld
- *${viewClassName}*表示远程接口的全限定接口名
7.2. 查找逻辑
接下来,我们来看看我们简单的查找逻辑:
public HelloWorld lookup() throws NamingException {
String appName = "";
String moduleName = "remote";
String distinctName = "";
String beanName = "HelloWorld";
String viewClassName = HelloWorld.class.getName();
String toLookup = String.format("ejb:%s/%s/%s/%s!%s",
appName, moduleName, distinctName, beanName, viewClassName);
return (HelloWorld) context.lookup(toLookup);
}
为了连接到我们刚刚创建的bean,我们需要一个可以输入上下文的 URL。
7.3. 初始上下文
我们现在将创建/初始化会话上下文:
public void createInitialContext() throws NamingException {
Properties prop = new Properties();
prop.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
prop.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.naming.remote.client.InitialContextFacto[ERROR]
prop.put(Context.PROVIDER_URL, "http-remoting://127.0.0.1:8080");
prop.put(Context.SECURITY_PRINCIPAL, "testUser");
prop.put(Context.SECURITY_CREDENTIALS, "admin1234!");
prop.put("jboss.naming.client.ejb.context", false);
context = new InitialContext(prop);
}
要连接到远程 bean,我们需要一个 JNDI 上下文。上下文工厂由 Maven 工件org.jboss:jboss-remote-naming 提供,这将创建一个 JNDI 上下文,它将在查找方法中构造的 URL 解析为远程应用程序服务器进程的代理。
7.4. 定义查找参数
我们使用参数Context.INITIAL_CONTEXT_FACTORY定义工厂类。
Context.URL_PKG_PREFIXES用于定义一个包以扫描额外的命名上下文。
参数org.jboss.ejb.client.scoped.context = false告诉上下文从提供的映射而不是类路径配置文件中读取连接参数(例如连接主机和端口)。如果我们想要创建一个能够连接到不同主机的 JAR 包,这将特别有用。
参数Context.PROVIDER_URL定义连接模式并且应该以http-remoting:// 开头。
8. 测试
要测试部署并检查设置,我们可以运行以下测试以确保一切正常:
@Test
public void testEJBClient() {
EJBClient ejbClient = new EJBClient();
HelloWorldBean bean = new HelloWorldBean();
assertEquals(bean.getHelloWorld(), ejbClient.getEJBRemoteMessage());
}
随着测试通过,我们现在可以确定一切都按预期工作。