跳转至

Enterprise JavaBeans (EJB) 深度解析

简介

Enterprise JavaBeans(EJB)是Java EE(Java Enterprise Edition)平台的核心组件之一,它为开发分布式企业级应用提供了一个强大的、可伸缩的和事务处理安全的组件模型。EJB 简化了企业级应用开发,让开发者专注于业务逻辑实现,而不必过多关心底层的系统级服务,如事务管理、并发控制、安全管理等。本文将全面介绍 EJB 的基础概念、使用方法、常见实践以及最佳实践。

目录

  1. EJB 基础概念
    • EJB 组件类型
    • EJB 容器
    • EJB 生命周期
  2. EJB 使用方法
    • 开发无状态会话 Bean
    • 开发有状态会话 Bean
    • 开发消息驱动 Bean
  3. EJB 常见实践
    • 依赖注入
    • 事务管理
    • 安全管理
  4. EJB 最佳实践
    • 设计原则
    • 性能优化
    • 错误处理
  5. 小结
  6. 参考资料

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

  1. 定义接口
import javax.ejb.Local;

@Local
public interface HelloWorldService {
    String sayHello();
}
  1. 实现接口
import javax.ejb.Stateless;

@Stateless
public class HelloWorldServiceImpl implements HelloWorldService {
    @Override
    public String sayHello() {
        return "Hello, World!";
    }
}
  1. 调用无状态会话 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

  1. 定义接口
import javax.ejb.Local;

@Local
public interface ShoppingCartService {
    void addItem(String item);
    int getCartSize();
}
  1. 实现接口
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();
    }
}
  1. 调用有状态会话 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

  1. 定义消息生产者
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);
        }
    }
}
  1. 开发消息驱动 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 虽然在现代开发中有一些替代方案,但它仍然在许多大型企业级项目中发挥着重要作用。

参考资料