Java EE 5.0 核心框架為 EJB 3.0 與 JSF 1.2。EJB 3.0 是基於 POJO 的服務端業務服務與資料庫持久化組件模型。JSF 是基於 MVC 的 Web 應用框架。大多數應用包含業務邏輯的 EJB3 組件與前端顯示的 JSF 組件。表面上 EJB3 與 JSF 互補,但依各自理念設計為獨立框架,無法獨自解決所有計算問題。例如 EJB3 用註解配置服務,JSF 用 XML 檔案。兩者在框架層面互不敏感,最好結合使用,但 Java EE 5 規範未提供整合標準方法。開發者必須手動將業務組件 (EJB) 與 Web 組件 (JSF) 連結以跨框架呼叫方法。
Spring 作為輕量級容器,常被視為 EJB 替代品。對許多應用,採用 Spring 容器並借助其事務與 ORM 支援,是採用 EJB 容器實現同樣功能的另一選擇。但用 Spring 不代表不能用 EJB。實際上 Spring 使存取與實現 EJB 更方便。Spring 分別提供整合 JSF 與 EJB 的方法。本文使用 Eclipse 開發範例演示此過程。
實現產品資訊增刪改查基本操作,單一領域模型 Product,UML 圖 (圖 1)。

平台:Windows Vista。安裝 JDK (jdk1.6.0_01)、Eclipse for Java EE Developers 3.4+ (eclipse3.4.1)、JBoss Application Server 4.2+ (jboss-4.2.2.GA)、Spring Framework 2.5+ (spring-framework-2.5.4)。
設定 JRE (圖 2),設定 Server Runtime Environments (圖 3),新建 simple EAR 專案,包含 JPA Project、EJB Project、WEB Project (圖 4)。

新建 simpleJPA JPA 專案 (圖 5),編輯 persistence.xml (清單 1):使用 JBoss 預設資料源 java:/DefaultDS,Hibernate hbm2ddl.auto 設 update,顯示格式化 SQL。

<persistence-unit name="simpleJPA">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
新建 Product 實體類別 (圖 6),id 為主鍵,表名 product,修改主鍵生成策略為自增 @GeneratedValue(strategy = GenerationType.AUTO) (清單 2)。

@Entity
@Table(name = "product")
public class Product implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
private String description;
private Double price;
private Integer inventory;
// ... getter/setter
}
新建 simpleEJB EJB 專案 (圖 7),新建 ProductDAOBean Session Bean (圖 8),實作 Local/Remote 介面 (清單 3)。

@Stateless
@Remote(ProductDAORemote.class)
@Local(ProductDAOLocal.class)
public class ProductDAOBean implements ProductDAORemote, ProductDAOLocal {
@PersistenceContext(unitName = "simpleJPA")
protected EntityManager em;
public void add(Product product) { em.persist(product); }
@SuppressWarnings("unchecked")
public List<Product> findAll() {
Query query = em.createQuery("select o from Product o");
return query.getResultList();
}
}
@Stateless 標示無狀態 Session Bean,@Remote/@Local 指定介面。同一應用伺服器部署時,Local 介面效能優於 Remote (無分散式物件協定開銷)。@PersistenceContext 動態注入 EntityManager,由 EJB 容器自動管理生命週期。
新建 simpleWEB Dynamic Web Project,啟用 JSF 1.1 (圖 9),URL Mapping 設 *.jsf (圖 10)。

複製 spring.jar 至 WEB-INF/lib,建立 applicationContext.xml (清單 4),web.xml 設定 ContextLoaderListener 載入 Spring 上下文。Spring 透過 jndi-lookup 存取 EJB,可在本地 EJB、遠端 EJB、POJO 間透明切換實現方式。
<jee:jndi-lookup id="productDAO" jndi-name="simple/ProductDAOBean/remote"
proxy-interface="org.zhouxing.simple.ProductDAORemote" />
新建 ProductBean 作為 JSF Managed Bean (清單 5),屬性 productDAO 透過 JSF 設定檔用 Spring 注入,product 作為表單 Form。faces-config.xml 指定 Spring DelegatingVariableResolver (清單 6),配置 Managed Bean 注入 productDAO (清單 7),配置導航規則導向 index.jsp (清單 8)。
public class ProductBean {
private ProductDAORemote productDAO;
private Product product = new Product();
public void setProductDAO(ProductDAORemote productDAO) { this.productDAO = productDAO; }
public String add() { productDAO.add(product); return ""; }
public List<Product> getProducts() { return productDAO.findAll(); }
public Product getProduct() { return product; }
}
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
<managed-bean>
<managed-bean-name>productBean</managed-bean-name>
<managed-bean-class>org.zhouxing.simple.ProductBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>productDAO</property-name>
<value>#{productDAO}</value>
</managed-property>
</managed-bean>
index.jsp 顯示表單與查詢結果 (清單 9),使用 JSF 標籤繫結 productBean 屬性與方法。
<h:form>
名稱 <h:inputText value="#{productBean.product.name}"/><p>
存貨 <h:inputText value="#{productBean.product.inventory}"/><p>
單價 <h:inputText value="#{productBean.product.price}"/><p>
描述 <h:inputTextarea value="#{productBean.product.description}"/>
<h:commandButton value=" 添加 " action="#{productBean.add}"/>
</h:form>
<h:dataTable var="entry" value="#{productBean.products}">
<h:column><h:outputLabel value="#{entry.name}"/></h:column>
<h:column><h:outputLabel value="#{entry.inventory}"/></h:column>
<h:column><h:outputLabel value="#{entry.price}"/></h:column>
<h:column><h:outputLabel value="#{entry.description}"/></h:column>
</h:dataTable>
確認 application.xml 包含三模組 (清單 10),右鍵 simple 專案 Run as > Run on Server,選擇 JBoss v4.2,瀏覽器訪問 https://localhost:8080/simpleWEB/。
<module><web><web-uri>simpleWEB.war</web-uri><context-root>simpleWEB</context-root></web></module>
<module><ejb>simpleEJB.jar</ejb></module>
<module><ejb>simpleJPA.jar</ejb></module>
Spring 使程式碼更簡潔易擴展,不僅整合 JSF 與 EJB,對 JDBC、Java Mail、JCA 及開源框架都提供諸多便利。