source
code
数据库设计
我们为该范例应用创建了包含4个表的数据库,如图5所示:
类设计
图6显示了JCatalog的类图
“编程到接口”的思想贯穿了整个设计实现中,在表示层,共用到四个backing bean:ProductBean、ProductListBean、UserBean和MessageBean;业务逻辑层包含两个业务服务
(CatalogService和UserService)和三个业务对象(Product、Category和User);Integration层有两个Dao接口和它们相应的Hibernate实现,Spring的application
context用来管理绝大多数的业务逻辑层和integration层的对象;ServiceLocator将JSF和业务逻辑层整合在了一起。
Wire everything up
由于篇幅所限,我们仅举例说明,范例中use case CreateProduct展示了如何装配和构建应用,在详细讲述细节前,我们利用sequence图(图7)来说明所有层的end-tp-end整合。
表示层:
表示层实现包括创建JSP页面、定义页导航、创建和配置backing bean以及将JSF与业务逻辑层整合。
- JSP page:createProduct.jsp是用来创建新产品的页面,它包含UI组件并将组件打包成ProductBean,ValidateItemsRange标签用来验证用户选择的种类数量,对每一个产品至少要有一个种类被选中。
- 页面导航:应用中的导航被定义在应用的配置文件faces-navigation.xml中,CreateProduct的导航准则如下:
*
createProduct
/createProduct.jsp
/createProduct.jsp
success
/uploadImage.jsp
retry
/createProduct.jsp
cancel
/productList.jsp
- Backing bean:ProductBean不仅包含有将数据映射到页面上的UI组件的属性,还包括三个action:createAction、editAction和deleteAction,下面是createAction方法的代码:
public String createAction() {
try {
Product product = ProductBeanBuilder.createProduct(this);
//Save the product.
this.serviceLocator.getCatalogService().saveProduct(product);
//Store the current product id inside
the session bean.
//For the use of image uploader.
FacesUtils.getSessionBean().setCurrentProductId(this.id);
//Remove the productList inside the
cache.
this.logger.debug("remove ProductListBean
from cache");
FacesUtils.resetManagedBean(BeanNames.PRODUCT_LIST_BEAN);
} catch (DuplicateProductIdException de) {
String msg = "Product id already
exists";
this.logger.info(msg);
FacesUtils.addErrorMessage(msg);
return NavigationResults.RETRY;
} catch (Exception e) {
String msg = "Could not save
product";
this.logger.error(msg, e);
FacesUtils.addErrorMessage(msg +
": Internal Error");
return NavigationResults.FAILURE;
}
String msg = "Product with id of " + this.id
+ " was created successfully.";
this.logger.debug(msg);
FacesUtils.addInfoMessage(msg);
return NavigationResults.SUCCESS;
}
- Managed-bean声明:ProductBean必须在JSF配置文件faces-managed-bean.xml中配置:
Backing bean that contains product
information.
productBean
catalog.view.bean.ProductBean
request
id
#{param.productId}
serviceLocator
#{serviceLocatorBean}
- 表示层和业务逻辑层之间的整合: ServiceLocator抽象了查找服务的逻辑,在范例应用中,ServiceLocator被定义为一个接口,该接口实现为一个JSF的
managed bean,即ServiceLocatorBean,它将在Spring的application context中寻找服务:
ServletContext context = FacesUtils.getServletContext();
this.appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
this.catalogService = (CatalogService)this.lookupService(CATALOG_SERVICE_BEAN_NAME);
this.userService = (UserService)this.lookupService(USER_SERVICE_BEAN_NAME);
业务逻辑层
- 业务对象:由于采用Hibernate提供持久化,因此Product和Category两个业务对象需要为它们的所有field提供getter和setter。
- 业务服务:CatalogService接口中定义了所有的与Catalog management相关的服务:
public interface CatalogService
{
public Product saveProduct(Product product) throws
CatalogException;
public void updateProduct(Product product) throws CatalogException;
public void deleteProduct(Product product) throws CatalogException;
public Product getProduct(String productId) throws
CatalogException;
public Category getCategory(String categoryId) throws
CatalogException;
public List getAllProducts() throws CatalogException;
public List getAllCategories() throws CatalogException;
}
- Spring Configuration:这里是CatalogService的Spring配置:
PROPAGATION_REQUIRED,readOnly
PROPAGATION_REQUIRED
PROPAGATION_REQUIRED
PROPAGATION_REQUIRED
- Spring和Hibernate的整合:下面是HibernateSessionFactory的配置:
<!-- Hibernate SessionFactory Definition
-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>catalog/model/businessobject/Product.hbm.xml</value>
<value>catalog/model/businessobject/Category.hbm.xml</value>
<value>catalog/model/businessobject/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
<prop key="hibernate.cache.provider_class">net.sf.hibernate.cache.HashtableCacheProvider</prop>
</props>
</property>
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
CatalogDao
uses HibernateTemplate
to integrate between Hibernate and Spring. Here's the configuration
for HibernateTemplate
:
<!-- Hibernate Template Defintion -->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate.HibernateTemplate">
<property name="sessionFactory"><ref
bean="sessionFactory"/></property>
<property name="jdbcExceptionTranslator"><ref
bean="jdbcExceptionTranslator"/></property>
</bean>
Integration层
Hibernate通过xml配置文件来映射业务对象和关系数据库,在JCatalog中,Product.hbm.xml表示了Product对象的映射,Category.hbm.xml则用来表示Category的映射,Product.hbm.xml如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate
Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping package="catalog.model.businessobject">
<class name="Product" table="product">
<id name="id"
column="ID" unsaved-value="null">
<generator
class="assigned"/>
</id>
<property name="name"
column="NAME" unique="true" not-null="true"/>
<property name="price"
column="PRICE"/>
<property name="width"
column="WIDTH"/>
<property name="height"
column="height"/>
<property name="description"
column="description"/>
<set name="categoryIds"
table="product_category" cascade="all">
<key column="PRODUCT_ID"/>
<element column="CATEGORY_ID"
type="string"/>
</set>
</class>
</hibernate-mapping>
CatalogDao
is wired with HibernateTemplate
by Spring:
<!-- Catalog DAO Definition: Hibernate implementation -->
<bean id="catalogDao" class="catalog.model.dao.hibernate.CatalogDaoHibernateImpl">
<property name="hibernateTemplate"><ref
bean="hibernateTemplate"/></property>
</bean>
结论
本文主要讲述了如何将JSF与Spring、Hibernate整合在一起来构建实际的Web应用,这三种技术的组合提供了一个强大的Web应用开发框架。在Web应用的高层设计中应该采用多层构架体系,JSF非常适合MVC设计模式以实现表示层,Spring可用在业务逻辑层中管理业务对象,并提供事物管理和资源管理等,Spring与Hibernate结合的非常出色,Hibernate是强大的O/R映射框架,它可以在integration层中提供最好的服务。
通过将整个Web应用分割成多层,并借助于“编程到接口”,应用程序的每一层所采用的技术都是可替换的,例如Struts可以用来替换JSF,JDO可替换Hibernate。各层之间的整合不是不值得研究,采用IoC和ServiceLocator设计模式可使得整合非常容易。JSF提供了其它Web框架欠缺的功能,然而,这并不意味着你马上抛弃Struts而开始使用JSF,是否采用JSF取决于项目目前的状况和功能需求,以及开发团队的意见等。