Jpetstore阅读心得
虽然对Spring不熟悉,又不懂iBatis,还是硬着头皮开始阅读Spring包自带的Jpetstore经典J2EE例子。开始的时候,只想从大体上理解它的结构,或者说从某个部分开始了解这个经典的宠物店。
首先可以肯定,Jpetstore是按照MVC的模式设计的。持久化层用iBatis(这个我不懂,我多么希望它是用Hibernate啊!),控制器的servlet有两个选择,一个是用Struts,另一个是Spring。这些具体的以后慢慢看,慢慢理解吧。
今天主要了解到,Jpetstore使用了门面模式、单例模式,对数据厍操作用DAO对象。
门面接口 PetStoreFacade
public interface PetStoreFacade {
Account getAccount(String username);
Account getAccount(String username, String password);
void insertAccount(Account account);
void updateAccount(Account account);
List getUsernameList();
List getCategoryList();
Category getCategory(String categoryId);
List getProductListByCategory(String categoryId);
List searchProductList(String keywords);
Product getProduct(String productId);
List getItemListByProduct(String productId);
Item getItem(String itemId);
boolean isItemInStock(String itemId);
void insertOrder(Order order);
Order getOrder(int orderId);
List getOrdersByUsername(String username);
}
门面接口的实现类 PetStoreImpl
public class PetStoreImpl implements PetStoreFacade, OrderService {
private AccountDao accountDao;
private CategoryDao categoryDao;
private ProductDao productDao;
private ItemDao itemDao;
private OrderDao orderDao;
// -------------------------------------------------------------------------
// Setter methods for dependency injection
// -------------------------------------------------------------------------
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setCategoryDao(CategoryDao categoryDao) {
this.categoryDao = categoryDao;
}
public void setProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public void setItemDao(ItemDao itemDao) {
this.itemDao = itemDao;
}
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
// -------------------------------------------------------------------------
// Operation methods, implementing the PetStoreFacade interface
// -------------------------------------------------------------------------
public Account getAccount(String username) {
return this.accountDao.getAccount(username);
}
public Account getAccount(String username, String password) {
return this.accountDao.getAccount(username, password);
}
public void insertAccount(Account account) {
this.accountDao.insertAccount(account);
}
public void updateAccount(Account account) {
this.accountDao.updateAccount(account);
}
public List getUsernameList() {
return this.accountDao.getUsernameList();
}
public List getCategoryList() {
return this.categoryDao.getCategoryList();
}
public Category getCategory(String categoryId) {
return this.categoryDao.getCategory(categoryId);
}
public List getProductListByCategory(String categoryId) {
return this.productDao.getProductListByCategory(categoryId);
}
public List searchProductList(String keywords) {
return this.productDao.searchProductList(keywords);
}
public Product getProduct(String productId) {
return this.productDao.getProduct(productId);
}
public List getItemListByProduct(String productId) {
return this.itemDao.getItemListByProduct(productId);
}
public Item getItem(String itemId) {
return this.itemDao.getItem(itemId);
}
public boolean isItemInStock(String itemId) {
return this.itemDao.isItemInStock(itemId);
}
public void insertOrder(Order order) {
this.orderDao.insertOrder(order);
this.itemDao.updateQuantity(order);
}
public Order getOrder(int orderId) {
return this.orderDao.getOrder(orderId);
}
public List getOrdersByUsername(String username) {
return this.orderDao.getOrdersByUsername(username);
}
}
暂时不管 OrderService 接口。
PetStoreImpl的那些setter方法正是spring的注入方法。
在配置文件中:
<bean id="petStore" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">
<property name="accountDao" ref="accountDao"/>
<property name="categoryDao" ref="categoryDao"/>
<property name="productDao" ref="productDao"/>
<property name="itemDao" ref="itemDao"/>
<property name="orderDao" ref="orderDao"/>
</bean>
单例模式的实现并没有使用特别的工厂方法。原文这样注释:
* There is one instance of this class in the JPetStore application. In Spring
* terminology, it is a "singleton". This means a per-Application Context
* singleton. The factory creates a single instance; there is no need for a
* private constructor, static factory method etc as in the traditional
* implementation of the Singleton Design Pattern.
使用Spring的BeanFactory,可以轻易实现单例的。在Struts当控制器时,它是这样实现的。
BaseAction 类,整个应用程序的action都不再直接继承自Action类,而是BaseAction 类。
public abstract class BaseAction extends Action {
private PetStoreFacade petStore;
public void setServlet(ActionServlet actionServlet) {
super.setServlet(actionServlet);
if (actionServlet != null) {
ServletContext servletContext = actionServlet.getServletContext();
WebApplicationContext wac = WebApplicationContextUtils
.getRequiredWebApplicationContext(servletContext);
this.petStore = (PetStoreFacade) wac.getBean("petStore");
}
}
protected PetStoreFacade getPetStore() {
return petStore;
}
}
暂时这么多,不知理解是否有误,以后慢慢去理解学习。 对Struts的ActionForm的使用和对Action的使用类似,先写了个继承自ActionForm的类BaseActionForm,作为其它actionform的基类。
public class BaseActionForm extends ActionForm {
/* Public Methods */
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
ActionErrors actionErrors = null;
ArrayList errorList = new ArrayList();
doValidate(mapping, request, errorList);
request.setAttribute("errors", errorList);
if (!errorList.isEmpty()) {
actionErrors = new ActionErrors();
actionErrors.add(ActionErrors.GLOBAL_ERROR, new ActionError("global.error"));
}
return actionErrors;
}
public void doValidate(ActionMapping mapping, HttpServletRequest request, List errors) {
}
/* Protected Methods */
protected void addErrorIfStringEmpty(List errors, String message, String value) {
if (value == null || value.trim().length() < 1) {
errors.add(message);
}
}
}
为了需要安全检查的验证,还特别写了个SecureBaseAction的类。
public abstract class SecureBaseAction extends BaseAction {
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {
AccountActionForm acctForm = (AccountActionForm) request.getSession().getAttribute("accountForm");
if (acctForm == null || acctForm.getAccount() == null) {
String url = request.getServletPath();
String query = request.getQueryString();
if (query != null) {
request.setAttribute("signonForwardAction", url + "?" + query);
} else {
request.setAttribute("signonForwardAction", url);
}
return mapping.findForward("global-signon");
} else {
return doExecute(mapping, form, request, response);
}
}
protected abstract ActionForward doExecute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception;
}
因为它的身份验证是单一(即没有多种身份多种权限),直接在session中检查是否有帐户。
在有比较多字段需要检查的时候,就在doValidate方法内进行验证(在有些地方,作者选择了直接覆盖execute方法),其它一般都是看看是否为空,所以用了一个通用的方法 addErrorIfStringEmpty(这个方法在BaseActionForm中定义)。
分层结构
从分层角度考虑,我把Jpetstore这样分开来看DAO层:DAO接口(操作数据对象)
域模型层:持久化实体类(POJO对象)
业务逻辑层:(调用DAO,提供服务,似乎可以称为服务层)
注:这里没有考虑WEB层。
它的包结构如下图所示: 由于ORM框架使用我不懂的iBatis,我特意去看看它的实现,发现它的JAVABEAN没有像 hibernate 那样与数据库的表一一对应(虽然hibernate有时候不是这样,我还是这样说它)。
比如帐户(Account),在account数据表中的字段并不多,但那个JAVABEAN里属性还是很多的。因为在映射文件作了相应配置。如:
<select id="getAccountByUsername" resultMap="result">
select
signon.username as userid,
account.email,
account.firstname,
account.lastname,
account.status,
account.addr1,
account.addr2,
account.city,
account.state,
account.zip,
account.country,
account.phone,
profile.langpref,
profile.favcategory,
profile.mylistopt,
profile.banneropt,
bannerdata.bannername
from account, profile, signon, bannerdata
where account.userid = #value#
and signon.username = account.userid
and profile.userid = account.userid
and profile.favcategory = bannerdata.favcategory
</select>
如果使用hibernate,通过配置实体的关系,然后使用面向对象的HQL查询就可以,而不用自己写这样的SQL查询语句。
我在想,如果逻辑层的BEAN的属性数据不适合在视图层使用,是否应该使用DTO呢?
虽然这是个经典的J2EE实例,但毕竟算是比较简单的,应该还有好些东西没有用到。
[ 本帖最后由 powerwind 于 2006-10-16 18:57 编辑 ] 今天并没有从Jpetstore的源码中读出什么心得,倒是从《J2EE Development without EJB》这本书的第十六章看到了些对Jpetstore的介绍。发觉我前面发的帖中内容作者都讲到了,可能是太明显重要的东西,也可能因为以前不觉意看了有点印象,然后一看源码就有那种“体会”来了。
一开始我就希望持久层的ORM框架使用Hibernate,而作者说这样做是为了和原来用iBatis做的版本作比较而已。作者还提到:iBatis可以把不同数据表中的字段映射到单个的对象上。如果数据模型给定,这个技术就非常适合了。而Hibernate目前还不支持这种粗粒度对象。
我猜想,如果要改成Hibernate的话,由于是细粒度对象,是不是应该要用到TDO来实现层之间的数据传输呢?我还没有打算花时间去修改练习,只能想一下。
至于作者说到的分布式,目前还没有能力去理解,放着先。 一行一行地读,还是一个类一个类地读?一时间感觉没什么好读。
于是我决定去看看它的WEB显示层,看看JSP文件。结果发现并没有什么特别,都是HTML和JSTL,极少用到Struts或Spring的标签(其实我也偏爱JSTL标签)。分页显示,只分上一页与下一页这样。看来看去,都觉得很普通。
几乎每个JSP页面都包含一个头文件和一个尾文件。
是了,它没有用到 titles 部局。
过两天再想想,再看看,一定还有不少值得学习的地方。 楼主是如何安装PETSTROE的? 很惭愧地告诉楼上的,我安装过,但没成功(登录时出现数据库错误)。
所以现在只是阅读它的代码,没有去执行程序。 楼主你的文章被转载了...
http://java.chinaitlab.com/Spring/529192.html
页:
[1]