Enterprise JavaBeans (EJB) 深度解析
简介
Enterprise JavaBeans(EJB)是Java EE(Java Enterprise Edition)平台的核心组件之一,它为开发分布式企业级应用提供了一个强大的、可伸缩的和事务处理安全的组件模型。EJB 简化了企业级应用开发,让开发者专注于业务逻辑实现,而不必过多关心底层的系统级服务,如事务管理、并发控制、安全管理等。本文将全面介绍 EJB 的基础概念、使用方法、常见实践以及最佳实践。
目录
- EJB 基础概念
- EJB 组件类型
- EJB 容器
- EJB 生命周期
- EJB 使用方法
- 开发无状态会话 Bean
- 开发有状态会话 Bean
- 开发消息驱动 Bean
- EJB 常见实践
- 依赖注入
- 事务管理
- 安全管理
- EJB 最佳实践
- 设计原则
- 性能优化
- 错误处理
- 小结
- 参考资料
EJB 基础概念
EJB 组件类型
EJB 主要有三种组件类型: - 会话 Bean(Session Bean):用于实现业务逻辑,分为无状态会话 Bean(Stateless Session Bean)和有状态会话 Bean(Stateful Session Bean)。无状态会话 Bean 不维护客户端的状态信息,多个客户端可以共享同一个无状态会话 Bean 实例;有状态会话 Bean 则会为每个客户端维护一个独立的状态。 - 实体 Bean(Entity Bean):用于表示企业数据和业务逻辑,通常对应数据库中的表记录。不过在现代开发中,实体 Bean 逐渐被 JPA(Java Persistence API)所取代。 - 消息驱动 Bean(Message Driven Bean):用于异步处理消息,它可以接收来自 JMS(Java Message Service)队列或主题的消息,并在后台进行处理。
EJB 容器
EJB 容器是运行 EJB 组件的环境,它提供了一系列系统级服务,如: - 事务管理:确保 EJB 方法的事务完整性,支持声明式和编程式事务管理。 - 安全管理:对 EJB 组件的访问进行安全控制,基于角色的访问控制(RBAC)模型。 - 生命周期管理:负责创建、销毁和管理 EJB 组件的实例。
EJB 生命周期
不同类型的 EJB 有不同的生命周期: - 无状态会话 Bean:容器在启动时创建一定数量的实例池,客户端调用方法时从池中获取实例,使用后返回池中。 - 有状态会话 Bean:客户端创建有状态会话 Bean 实例后,容器会维护该实例的状态,直到客户端销毁它。 - 消息驱动 Bean:容器在启动时创建实例池,消息到达时,从池中获取实例来处理消息。
EJB 使用方法
开发无状态会话 Bean
- 定义接口:
import javax.ejb.Local;
@Local
public interface HelloWorldService {
String sayHello();
}
- 实现接口:
import javax.ejb.Stateless;
@Stateless
public class HelloWorldServiceImpl implements HelloWorldService {
@Override
public String sayHello() {
return "Hello, World!";
}
}
- 调用无状态会话 Bean:
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/hello")
public class HelloWorldServlet extends HttpServlet {
@EJB
private HelloWorldService helloWorldService;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" + helloWorldService.sayHello() + "</h1>");
out.println("</body></html>");
}
}
开发有状态会话 Bean
- 定义接口:
import javax.ejb.Local;
@Local
public interface ShoppingCartService {
void addItem(String item);
int getCartSize();
}
- 实现接口:
import javax.ejb.Stateful;
import java.util.ArrayList;
import java.util.List;
@Stateful
public class ShoppingCartServiceImpl implements ShoppingCartService {
private List<String> items = new ArrayList<>();
@Override
public void addItem(String item) {
items.add(item);
}
@Override
public int getCartSize() {
return items.size();
}
}
- 调用有状态会话 Bean:
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/cart")
public class ShoppingCartServlet extends HttpServlet {
@EJB
private ShoppingCartService shoppingCartService;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
shoppingCartService.addItem("Book");
shoppingCartService.addItem("Pen");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Cart Size: " + shoppingCartService.getCartSize() + "</h1>");
out.println("</body></html>");
}
}
开发消息驱动 Bean
- 定义消息生产者:
import javax.ejb.Stateless;
import javax.jms.ConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.Queue;
import javax.inject.Inject;
@Stateless
public class MessageProducer {
@Inject
private ConnectionFactory connectionFactory;
@Inject
private Queue myQueue;
public void sendMessage(String message) {
try (JMSContext context = connectionFactory.createContext()) {
context.createProducer().send(myQueue, message);
}
}
}
- 开发消息驱动 Bean:
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@MessageDriven(
activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "myQueue")
}
)
public class MessageConsumer implements MessageListener {
@Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
TextMessage textMessage = (TextMessage) message;
System.out.println("Received message: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
EJB 常见实践
依赖注入
使用 @EJB
注解可以方便地将 EJB 注入到其他组件中,如 Servlet、其他 EJB 等。在前面的代码示例中已经展示了 @EJB
注解的使用。
事务管理
EJB 支持声明式事务管理(通过 @TransactionAttribute
注解)和编程式事务管理(通过 UserTransaction
接口)。
- 声明式事务管理:
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class OrderService {
@PersistenceContext
private EntityManager entityManager;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void placeOrder(Order order) {
entityManager.persist(order);
}
}
- 编程式事务管理:
import javax.ejb.Stateless;
import javax.transaction.UserTransaction;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.logging.Level;
import java.util.logging.Logger;
@Stateless
public class ComplexOrderService {
@PersistenceContext
private EntityManager entityManager;
@javax.annotation.Resource
private UserTransaction userTransaction;
public void complexTransaction() {
try {
userTransaction.begin();
// 执行一些数据库操作
entityManager.persist(new Order());
userTransaction.commit();
} catch (Exception ex) {
try {
userTransaction.rollback();
} catch (Exception e) {
Logger.getLogger(ComplexOrderService.class.getName()).log(Level.SEVERE, null, e);
}
Logger.getLogger(ComplexOrderService.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
安全管理
通过 @RolesAllowed
注解可以限制对 EJB 方法的访问权限:
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.annotation.security.RolesAllowed;
@Stateless
public class AdminService {
@RolesAllowed("admin")
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void performAdminTask() {
// 执行管理员任务
}
}
EJB 最佳实践
设计原则
- 单一职责原则:每个 EJB 应该只负责一项特定的业务功能,避免功能过于复杂。
- 高内聚、低耦合:EJB 之间的依赖应该尽量简单,内部逻辑应该高度凝聚。
性能优化
- 合理使用无状态会话 Bean:对于不需要维护状态的业务逻辑,优先使用无状态会话 Bean,因为它们可以被多个客户端共享,减少资源消耗。
- 缓存策略:对于频繁访问的数据,可以使用缓存机制来提高性能。
错误处理
- 统一异常处理:在 EJB 中应该有统一的异常处理机制,将业务逻辑中的异常转换为合适的异常类型,以便客户端能够正确处理。
- 记录详细日志:在发生错误时,应该记录详细的日志信息,方便调试和排查问题。
小结
Enterprise JavaBeans(EJB)为企业级应用开发提供了丰富的功能和强大的支持。通过理解 EJB 的基础概念、掌握使用方法、熟悉常见实践以及遵循最佳实践,开发者能够开发出高效、可靠、安全的企业级应用。EJB 虽然在现代开发中有一些替代方案,但它仍然在许多大型企业级项目中发挥着重要作用。