Java 企业开发:LDAP 与 EJB 技术全解析
1. 轻量级目录访问协议(LDAP)
LDAP 服务器是专门的软件产品,它以层次树的形式存储目录条目,并且针对读取操作进行了高度优化。这使得它非常适合用于组织中的员工列表或电话目录等目录服务,因为这些目录主要是用于读取,而不是频繁更改,这正是 LDAP 服务器的优势所在。
从企业 Java 的角度来看,当需要快速查找某些 Java 对象时,如 JMS 连接工厂、队列或主题,就应该考虑使用 LDAP 解决方案。Java 开发人员使用 JNDI API 来查找和更新 LDAP 中的条目,就像 JDBC 之于数据库管理系统(DBMS)一样,JNDI 之于 LDAP 服务器。
以下是一些流行的 LDAP 服务器:
| 服务器名称 | 所属公司 | 开源情况 |
| ---- | ---- | ---- |
| Oracle Directory Server | Oracle | 否 |
| Microsoft Active Directory | Microsoft | 否 |
| OpenLDAP | 社区开发 | 是 |
| ApacheDS | Apache | 是 |
| OpenDS | Oracle | 是 |
LDAP 目录树有一个根条目,由一个或多个可分辨名称(唯一标识符)组成。通常,层次结构的顶部是一个以“o”(组织)为前缀的对象,下一级是以“ou”(组织单位)为前缀,“cn”表示通用名称,依此类推。与其他命名服务不同,搜索字符串从层次结构的最底层条目开始,根条目必须最后指定。例如,可用于搜索的可分辨名称如下:
cn=jsmith, ou=accounting, o=oracle.com
这个例子对应于 LDAP 树中的以下层次结构:
o = oracle.com
ou = accounting
cn = jsmith
以下代码片段展示了如何准备 JNDI 属性、连接到 LDAP 服务器并查找名为
CustomerHome
的对象:
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");
env.put(Context.SECURITY_CREDENTIALS, "myPassword");
DirContext ctx = new InitialDirContext(env);
CustomerHome custHome = (CustomerHome) ctx.lookup("cn=CusomerHome, ou=RealBigProject, o=trump.com");
在实际的分布式应用中,可以有多种构建方式,例如:
1. 计算机 #1 运行 LDAP 服务器。
2. 计算机 #2 运行一个应用服务器,该服务器已将一些对象注册(发布)到计算机 #1 上的 LDAP 服务器。
3. 计算机 #3 有一个客户端程序,它在计算机 #1 上查找对象引用,并在计算机 #2 上调用这些对象的方法。
4. 计算机 #4 有一个数据库管理系统,供计算机 #2 上运行的应用服务器使用。
5. 计算机 #5 发布金融市场数据,计算机 #2 订阅此服务。
graph LR
A[计算机 #1: LDAP 服务器] --> B[计算机 #2: 应用服务器]
B --> C[计算机 #3: 客户端程序]
B --> D[计算机 #4: 数据库管理系统]
E[计算机 #5: 金融数据发布] --> B
2. 实践:创建消息接收器
可以创建一个名为
MessageReceiver
的类来消费消息。它需要执行 JNDI 查找并开始监听
TestQueue
。当消息到达时,
MessageReceiver
应显示该消息。向特定 Servlet 发出请求时,应向队列发送消息,
MessageReceiver
接收并打印其内容。
具体步骤如下:
1. 创建一个新类
MessageReceiver
,实现
MessageListener
接口,代码类似于之前的示例,但这次要执行对
MyTestConnectionFactory
和
MyJMSTestQueue
的 JNDI 查找。
2. 了解如何配置 GlassFish 启动类,并实现该类,以便在服务器启动时实例化
MessageReceiver
。
3. 使用特定的 Servlet 测试发送/接收周期。
3. 企业 JavaBeans(EJB)简介
EJB 是 Java EE 技术之一,可用于在分布式应用中实现业务层。随着 Java EE 6 的发布,EJB 3.1 和 Java 持久化 API(JPA)2.0 为实现业务逻辑和数据持久化提供了简洁的解决方案。支持 EJB 3.1 的应用服务器包括 GlassFish v3、JBoss 6 和 WebSphere 8 等。
为什么需要 EJB 容器呢?虽然可以在普通 Java 对象(POJO)中实现业务逻辑,但可能需要手动编写多线程环境,并且应用可能需要事务支持。例如,如果业务逻辑分布在 POJO 1 和 POJO 2 中,当第二个 POJO 失败时,需要回滚第一个 POJO 已完成的操作。此外,使用 JMS 编写消息接收器时,要实现可扩展性也比较困难。
EJB 容器可以解决这些基础设施相关的问题,无需手动编程。可以简单地配置消息驱动 bean(MDB)池以实现多个消息监听器,根据需要开启事务支持,并且无需担心多线程问题。配置过程只需在 EJB 类中添加 Java 注解,而 bean 之间的通信可以通过依赖注入或单例 bean 实现。
EJB 容器还提供了异步方法调用功能,无需 JMS 或 MOM 即可实现异步处理。此外,EJB 3.1 支持嵌入式容器,可以在 Java SE 环境中运行 EJB 应用,方便进行测试。创建嵌入式容器只需一行代码:
EJBContainer myContainer = EJBContainer.createEJBContainer();
4. EJB 的类型
EJB 主要有两种类型:会话 bean 和消息驱动 bean。消息驱动 bean 专门用于从 JMS 队列或主题中检索消息,而会话 bean 则包含应用的业务逻辑。会话 bean 又分为三种类型:
-
无状态会话 bean
:包含业务逻辑,但不支持状态,即不会“记住”特定于客户端的任何数据。例如,同一个客户端连续调用无状态 bean
FindBooks
的两个方法时,容器可能会使用
FindBooks
bean 的两个不同实例。
-
有状态会话 bean
:包含业务逻辑和状态。EJB 容器会为客户端分配一个特定的会话 bean 实例,并可以在后续方法调用之间存储结果。例如,可以使用有状态 bean 实现购物车功能。
-
单例会话 bean
:在容器中保证只有一个实例。可以将其视为全局存储库,一个 bean 可以将数据放入其中供另一个 bean 使用,并且可以确保并发访问时没有竞争条件。
早期的 EJB 规范定义了用于数据持久化的实体 bean,但在 EJB 3.1 规范中,它们正在被逐步淘汰,很可能会在未来的 Java EE 版本中消失。
5. 无状态会话 bean
下面通过一个简单的示例介绍无状态会话 bean,该 EJB 包含返回消息“Hello World”的业务逻辑。
Bean 代码示例 :
@Stateless
public class HelloWorldBean {
public String sayHello() {
return "Hello World!";
}
}
基本上,只需创建一个 POJO 并使用一个或多个 Java 注解进行标注,无需实现特殊接口即可将 POJO 转换为 EJB。还可以使用可选的配置文件
ejb-jar.xml
来指定 EJB 的元数据。
客户端视角
:
该 bean 在服务器上运行,调用
sayHello()
方法的客户端可以在同一个 JVM 中(如 Servlet 或另一个 bean)运行,也可以在另一个 JVM 中(如独立的 Java SE 应用程序或部署在另一个容器中的 Java EE 类)运行。
如果
HelloWorldBean
仅由在同一 JVM 中运行的客户端使用,可以使用
@LocalBean
注解将其标记为无接口 bean。如果要向本地客户端公开某些业务方法,可以创建一个标记为
@Local
的接口,让 bean 类实现该接口;如果要向远程客户端公开 bean,则创建一个标记为
@Remote
的接口并让 bean 实现它。
本地无接口 bean 示例 :
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
@LocalBean
@Stateless
public class HelloWorldBean {
public String sayHello() {
return "Hello World!";
}
}
具体操作步骤如下:
1. 在 Eclipse 中创建一个新的动态 Web 项目,命名为
Lesson32
。
2. 在
com.practicaljava.lesson32.client
包中创建一个新的 Servlet 类
HelloWorldServlet
,该 Servlet 将作为客户端与 EJB 通信。
3. 在
com.practicaljava.lesson32.ejb
包中创建一个 Java 类
HelloWorldBean
,选择“File -> New -> Other -> EJB -> Session Bean (EJB 3.x)”,不选择任何本地或远程业务接口,并取消选中与 EJB 2.x 相关的对象。添加
sayHello()
方法,完成 EJB 的创建。
4. 使用
@EJB
注解将
HelloWorldBean
注入到 Servlet 代码中:
@EJB HelloWorldBean myBean;
如果 Eclipse 标记该行有错误,右键单击并选择“Quick Fix”,自动插入两个导入语句。也可以使用 JNDI 查找来替代注解注入:
Context ctx = new InitialContext();
HelloWorldBean myBean = (HelloWorldBean) ctx.lookup("java:global/Lesson32/HelloWorldBean");
-
在 Servlet 的
doGet()方法中添加以下两行代码,调用 EJB 的sayHello()方法:
PrintWriter out = response.getWriter();
out.println(myBean.sayHello());
完整的 Servlet 代码示例 :
import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.practicaljava.lesson32.ejb.HelloWorldBean;
@WebServlet("/HelloWorldServlet")
public class HelloWorldServlet extends HttpServlet {
@EJB HelloWorldBean myBean;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println(myBean.sayHello());
}
}
6. 异步方法
无状态 bean 还有一个重要特性是异步方法。假设
sayHello()
是一个执行长时间计算的方法,希望在调用该方法后继续执行其他操作,而不必等待其完成。在 EJB 容器中,不需要在应用代码中创建和启动线程,因为 EJB 容器会处理所有多线程问题。可以使用
@Asynchronous
注解标记方法,并让其返回
javax.ejb.AsyncResult
对象:
@Asynchronous
public <Future>String sayHello() {
// 一些长时间的计算代码
//...
return new AsyncResult<String>("Hello World!");
}
客户端调用
sayHello()
方法后,可以继续执行其他代码,在需要结果时进行阻塞调用
get()
:
// 异步调用
Future<String> myFutureGreeting = myBean.sayHello();
// 其他代码
//...
// 稍后进行阻塞调用,等待结果
String myGreeting = myFutureGreeting.get();
如果客户端需要在 EJB 上启动多个方法并行运行,这些方法甚至不需要返回值,只需将方法声明为异步并从客户端调用即可。
7. 有状态会话 bean
与无状态会话 bean 不同,有状态会话 bean 会为客户端分配更长时间,并且会记住之前方法执行的结果。例如,可以使用有状态 bean 实现购物车功能,用户在浏览公司目录时可以向购物车中添加多个商品。当客户端结束会话时,有状态 bean 可以分配给另一个客户端。
假设存在一个有状态 EJB
MyShoppingCart
,客户端使用 JNDI 查找该 bean 并调用
addItem()
方法添加商品,继续浏览后添加另一个商品,最后调用
placeOrder()
方法结账。所有这些方法调用都在同一个
MyShoppingCart
实例上进行。
MyShoppingCart myCart = (MyShoppingCart) ctx.lookup("java:global/OnlineStore/MyShoppingCart");
// 客户端浏览目录,找到第一个要购买的商品
...
myCart.addItem(myFirstItem);
// 客户端继续浏览目录,找到第二个要购买的商品
...
综上所述,LDAP 和 EJB 是 Java 企业开发中非常重要的技术,它们分别在目录服务和业务逻辑实现方面发挥着关键作用。通过合理使用这些技术,可以构建出高效、可扩展的分布式应用。
Java 企业开发:LDAP 与 EJB 技术全解析
8. 本地与远程 Bean 的区别与应用
在 EJB 开发中,本地 Bean 和远程 Bean 有着不同的应用场景和实现方式。
8.1 本地 Bean
本地 Bean 主要用于在同一 JVM 内的组件之间进行交互。为了向本地客户端暴露业务方法,需要声明一个标记为
@Local
的接口,并让 Bean 类实现该接口。以下是一个示例:
@Local
public interface Greeting {
public String sayHello();
}
@Stateless
public class HelloWorldBeanL implements Greeting {
public String sayHello() {
return "Hello World!";
}
}
在这个示例中,
Greeting
接口被标记为
@Local
,
HelloWorldBeanL
类实现了该接口。这样,在同一 JVM 内的其他组件就可以通过该接口调用
HelloWorldBeanL
的业务方法。
8.2 远程 Bean
远程 Bean 则用于不同 JVM 之间的交互。对于可能远程访问 Bean 的客户端,需要声明一个标记为
@Remote
的接口,并让 Bean 类实现该接口。示例如下:
@Remote
public interface Greeting {
public String sayHello();
}
@Stateless
public class HelloWorldBeanL implements Greeting {
public String sayHello() {
return "Hello World!";
}
}
客户端通过 JNDI 查找来定位远程 Bean。由于客户端和服务器可能运行在不同的 JVM 中,远程方法的所有参数必须是可序列化的。
下面是本地 Bean 和远程 Bean 的对比表格:
| 类型 | 应用场景 | 接口标记 | 调用方式 | 参数要求 |
| ---- | ---- | ---- | ---- | ---- |
| 本地 Bean | 同一 JVM 内组件交互 |
@Local
| 直接调用 | 无特殊要求 |
| 远程 Bean | 不同 JVM 间交互 |
@Remote
| JNDI 查找 | 所有参数必须可序列化 |
9. EJB 部署与测试
在开发完 EJB 后,需要将其部署到服务器上进行测试。以 GlassFish 服务器为例,具体步骤如下:
1.
部署项目
:在 Servers 视图中右键单击 GlassFish 服务器,将项目
Lesson32
添加到服务器以部署该 Web 应用。
2.
开启自动部署
:为了避免每次代码更改都手动重新部署应用,可以开启自动部署功能。双击 Servers 视图中的 GlassFish 服务器,将 Publishing 首选项更改为“Automatically publish when resources change”。
graph LR
A[选择 GlassFish 服务器] --> B[添加项目 Lesson32]
B --> C[双击服务器]
C --> D[更改 Publishing 首选项]
D --> E[自动发布资源更改]
-
启动服务器并测试
:启动 GlassFish 服务器(如果未运行),运行
HelloWorldServlet。可以在浏览器中输入http://localhost:8080/Lesson32/HelloWorldServlet查看 EJB 产生的输出。
10. EJB 容器的优势总结
EJB 容器在 Java 企业开发中具有诸多优势,具体如下:
-
基础设施管理
:EJB 容器负责处理多线程、事务支持、消息监听池配置等基础设施相关的问题,开发者无需手动编程,减少了开发工作量和出错的可能性。
-
可扩展性
:通过配置消息驱动 Bean 池和提供集群与故障转移支持,EJB 容器能够轻松应对分布式应用的扩展需求。
-
安全性
:安全授权由 EJB 容器负责,确保了应用的安全性。
-
异步处理
:支持异步方法调用,无需额外的 JMS 或 MOM 即可实现异步处理,提高了应用的性能和响应速度。
-
嵌入式容器
:EJB 3.1 支持嵌入式容器,可在 Java SE 环境中运行 EJB 应用,方便进行测试。
11. 总结与实践建议
在 Java 企业开发中,LDAP 和 EJB 技术是构建高效、可扩展分布式应用的重要工具。LDAP 服务器适用于存储和快速查找目录信息,如员工列表、电话目录等;而 EJB 则用于实现分布式应用的业务逻辑,通过 EJB 容器可以解决多线程、事务管理、消息处理等复杂问题。
为了更好地应用这些技术,建议开发者:
1.
深入学习 JNDI
:掌握 JNDI 的使用方法,它是连接 LDAP 服务器和 EJB 客户端的桥梁。
2.
合理选择 EJB 类型
:根据业务需求选择合适的 EJB 类型,如无状态会话 Bean 适用于多用户并发请求的场景,有状态会话 Bean 适用于需要保存客户端状态的场景。
3.
利用 EJB 容器特性
:充分利用 EJB 容器提供的基础设施管理、可扩展性、安全性等特性,减少开发工作量,提高应用的质量和性能。
4.
多进行实践
:通过实际项目的开发,加深对 LDAP 和 EJB 技术的理解和掌握。
总之,通过合理运用 LDAP 和 EJB 技术,开发者可以构建出更加高效、稳定、安全的 Java 企业应用。
超级会员免费看
101

被折叠的 条评论
为什么被折叠?



