| |
作者:大峡 来源:www.easyjf.com 发布时间:2006-05-09 | |
(本故事除了部分点明道姓并具有故事详细发生的具体时间点地等部分情节以外,其它内容纯属虚构,若有雷同,纯属巧合。)
谈到B/S,谈到J2EE,特别涉及到Java Web的部分,我们就会想到三层或多层构架,为什么要多层,肯定是因为一两层已经无法达到用户的需求,技术进步了,三层或多层很多时候其系统的健壮性、可维护性等都较之提升了很多。
在本系列文章的前两篇中,我们基本上没有涉及到Spring技术的部分,因此,今天换换味,主要给出一个比较简单、实用,也是最庸俗的spring示例。也即实现一个数据表的添删改查操作,笔者采用struts+hibernate+spring黄金庸俗组合来实现。示例比较简单,因此spring的高手及高高手可以忽略示例中的技术细节部分,当然,也请有时间的前辈不吝赐教有关代码的书写方法及技术。
主演:Spring、系统设计师、数据库开发工程师、Web开发工程师、主程序员、系统集成及部署工程师。
配角:数据库管理员、美工及页面设计MM、html,JSP、Struts、My Sql、hibernate。
下面就是直杀正题吧!
第一步:系统设计
现在你扮演的是系统设计师角色,系统设计师通常都不用管也不想知道你要用什么方法去实现他的东西,他只给你画出草图,制订规矩、条款等,他甚至可以不用知道Java中还有Class这个东西存在。他也不用管你用什么方式存放数据,用什么数据库中间件来操作数据。 但他站得高,看得远,为了以后的可维护性、灵活性,考虑到会使用多种数据库,以及多种数据库中间件等,因此会先对系统设计建模,通过UML等方式来描述系统流程、组织结构等。同时还会通过使用接口定义出相应的商业逻辑。下面是本例子中的两个业务逻辑层的接口。 第一个是“用户”这一实体的描述,IUser.java,代码如下: package com.easyjf.example.business;
public interface IUser {
public String getBirthday();//生日 public void setBirthday(String birthday); public String getCid();//主键cid public void setCid(String cid); public String getEmail();//用户邮件email public void setEmail(String email); public String getIntro();//用户简介intro public void setIntro(String intro); public String getPassword();//用户密码password public void setPassword(String password); public String getTel() ;//用户电话tel public void setTel(String tel); public String getUserName();//用户名userName public void setUserName(String userName); }
第二个是围绕“用户”这一实体所提供的有关服务描述,IUserService.java,代码如下:
package com.easyjf.example.business;
import java.util.Collection;
import java.util.List; public interface IUserService { public IUser newUser(); //创建一个新的用户 public IUser read(String cid);//根据主键cid读取一个用户 public List query(String scope,Collection paras);//用户查询 public IUser readByName(String userName);//根据用户名读取一个用户 public IUser login(String userName,String password,String ip);//用户登录 public boolean save(IUser user);//保存用户 public boolean update(IUser user);//修改用户 public boolean del(IUser user);//删除用户 }
除了上面这些,系统设计师还会根据系统需求给出一些具体的设计方法,对系统各层之间的接口规范,Action层使用的框架或模式等。他的工作就算完成了,下面就该程序员、数据库管理员以及页面设计人员等表演了。这些角色在我们实际项目中很多时候是同步进行的,可谓花开N朵。然而本文为了能让新同学把这个示例能跑起来,所以就简单分了一下步骤,以便新同学按步骤进行。
第二步:数据库设计及建表
(数据库管理员)
随便选择一个数据库My SQL,MS SQL或Oracle均可,先建设一张表,表名叫User,字段如下:
cid,代表主键,username-用户名,password-密码,email-电子邮件,tel-电话,birthday-生日,intro-简介,为了演示方便,我们全部字段设置成字符型。 下面是在My SQL中的表结构脚本: CREATE TABLE `user` ( `cid` char(16) NOT NULL, `username` char(30) NOT NULL, `password` char(30) NOT NULL, `email` varchar(50) default NULL, `tel` varchar(50) default NULL, `birthday` varchar(20) default NULL, `intro` varchar(500) default NULL, PRIMARY KEY (`cid`) ) ENGINE=InnoDB DEFAULT CHARSET=gbk;
第三步:随便选择一种MVC框架写Web部份(Web开发工程师)
晕,类Class都没看见一个就要写Action了啊? 新手别叫,这里演示的是一种现代、先进、高雅、华丽、贵族式的编程及设计方法!(晕,不准吐!),J2EE中提倡的是面向接口编程,所以,系统设计师都给了你UML图,甚至连详细的Java接口都给你了,已经够多了,快动手吧。 本例MVC使用的Struts框架,因此整个开发流程大致有下面的几个步骤。 1、 先根据IUser接口,写一个ActionForm,代码如下: package com.easyjf.struts.form;
import org.apache.struts.action.ActionForm;
public class UserForm extends ActionForm { private String cid; private String userName; private String password; private String email; private String tel; private String birthday; private String intro; public String getCid() { return cid; } public void setCid(String cid) { this.cid = cid; } … //其它的getter、stter方法在此略过。 }
2、写Action
根据用户的需求说明及设计师的相关模式或规范建议等,写一个处理“添删改查”等操作的Action,本文全部写到一个UserManageAction.java 中,代码如下: package com.easyjf.struts.action;
import java.util.ArrayList;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils;
import com.easyjf.example.business.IUser;
import com.easyjf.example.business.IUserService; import com.easyjf.struts.form.UserForm; import com.easyjf.util.CommUtil; import com.easyjf.web.tools.IPageList; import com.easyjf.web.tools.ListQuery; import com.easyjf.web.tools.PageList;
public class UserManageAction extends Action {
private IUserService userService; public IUserService getUserService() { return userService; } public void setUserService(IUserService userService) { this.userService=userService; } public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { WebApplicationContext wac =WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServlet().getServletContext()); this.userService = (IUserService) wac.getBean("userService"); String command=request.getParameter("easyJWebCommand"); if(command==null || "".equals(command)||"list".equals(command)||"query".equals(command)) { return doQuery(mapping,form,request,response); } else if("new".equals(command)) { return mapping.findForward("edit"); } else if("add".equals(command)) { return doAdd(mapping,form,request,response); } else if("edit".equals(command)) { return doEdit(mapping,form,request,response); } else if("update".equals(command)) { return doUpdate(mapping,form,request,response); } else if("del".equals(command)) { return doDel(mapping,form,request,response); } return super.execute(mapping,form,request,response); } public ActionForward doEdit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { UserForm vo=(UserForm)form; IUser user=null; if(vo.getCid()!=null && (!vo.getCid().equals("")))user=userService.read(vo.getCid()); if(user==null)user=userService.newUser(); if(user!=null) { vo.setCid(user.getCid()); vo.setUserName(user.getUserName()); vo.setPassword(user.getPassword()); vo.setTel(user.getTel()); vo.setEmail(user.getEmail()); vo.setIntro(user.getIntro()); vo.setBirthday(user.getBirthday()); return mapping.findForward("edit"); } else { request.setAttribute("msg","找不到数据!"); return doQuery(mapping,form,request,response); } } public ActionForward doUpdate(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response) { IUser obj=(IUser)form2Po(form); if(userService.update(obj)) { request.setAttribute("msg","数据修改成功!"); return doQuery(mapping,form,request,response); } else { request.setAttribute("msg","数据修改失败"); return mapping.getInputForward(); } } public ActionForward doAdd(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { IUser obj=form2Po(form); if(obj==null) { request.setAttribute("msg","无法创建要保存的对象,添加失败!"); return mapping.getInputForward(); } if(userService.save(obj)) { request.setAttribute("msg","数据添加成功!"); return doQuery(mapping,form,request,response); } else { request.setAttribute("msg","数据添加失败"); return mapping.getInputForward(); } } public ActionForward doQuery(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { int currentPage=CommUtil.null2Int(request.getParameter("page")); int pageSize=CommUtil.null2Int(request.getParameter("pageSize")); if(currentPage<1)currentPage=1; if(pageSize<1)pageSize=15; String scope="1=1"; Collection paras=new ArrayList(); String orderType=CommUtil.null2String(request.getParameter("orderType")); String orderField=CommUtil.null2String(request.getParameter("orderField")); String userName=CommUtil.null2String(request.getParameter("queryUserName")); String tel=CommUtil.null2String(request.getParameter("queryTel")); if(!userName.equals("")) { scope+=" and userName like ?"; paras.add("%"+userName+"%"); } if(!tel.equals("")) { scope+=" and tel like ?"; paras.add("%"+tel+"%"); } if(orderField.equals(""))//默认按用户名排序 { orderField="userName"; orderType="desc"; } if(!orderField.equals("")) { scope +=" order by "+orderField; if(!orderType.equals(""))scope+=" "+orderType; } IPageList pList=new PageList(new ListQuery(userService.query(scope,paras))); pList.doList(pageSize,currentPage,"",""); if(pList!=null){ request.setAttribute("list",pList.getResult()); request.setAttribute("pages",new Integer(pList.getPages())); request.setAttribute("rows",new Integer(pList.getRowCount())); request.setAttribute("page",new Integer(pList.getCurrentPage())); request.setAttribute("gotoPageHTML",CommUtil.showPageHtml(pList.getCurrentPage(),pList.getPages())); } request.setAttribute("orderType",orderType); request.setAttribute("orderField",orderField); return mapping.findForward("list"); } public ActionForward doDel(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { UserForm vo=(UserForm)form; IUser user=null; if(vo.getCid()!=null && (!vo.getCid().equals("")))user=userService.read(vo.getCid()); if(user!=null && userService.del(user)) { request.setAttribute("msg","数据删除成功!"); } else { request.setAttribute("msg","数据修改失败"); } return doQuery(mapping,form,request,response); } public IUser form2Po(ActionForm form) { UserForm vo=(UserForm)form; IUser user=null; if(vo.getCid()!=null && (!vo.getCid().equals("")))user=userService.read(vo.getCid()); if(user==null)user=userService.newUser(); user.setUserName(vo.getUserName()); user.setBirthday(vo.getBirthday()); user.setEmail(vo.getEmail()); user.setTel(vo.getTel()); user.setPassword(vo.getPassword()); user.setIntro(vo.getIntro()); return user; } }
注意了,Action中使用的都是IUser,IUsrService等接口,其中execute方法中有一句有点不一样:
WebApplicationContext wac =WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServlet().getServletContext()); this.userService = (IUserService) wac.getBean("userService");
这就是传说中强大的Spring IOC的使用。因为有了他,我们的Web开发人员也不用知道这个IUserService究竟是什么东西了,有时候也无法知道。你想想,你在做这一步的时候,可能另外分工做后台商业逻辑层的伙伴还没有把IUserService的实现写出一个来呢。Web开发人员需要用的IUserService的时候,直接通过类似的方法找Spring取就是了。Spring一个角色是扮演IOC容器中间件嘛,他都没有谁还有呢?
本例中有关com.easyjf.web.tools、com.easyjf.util等包只是使用了EasyJWeb的分页业务引擎及一些实用工具,没有其它功能,否则代码就会更多了。大家可以下载EasyJWeb的源码看到详细实现方式。
3、美工及设计制作人员做界面,完成后交由我们Web开发人员加JSP代码。
JSP部分主要有两个页面,userEdit.jsp主要是一个用户信息录入表单。userList.jsp是用户信息列表页面。这一部分大家一定都轻车熟路了吧,由于篇幅、页面、版面等都有限,就不把详细代码扔出来污染环境了。请新手直接到EasyJF开源团队官网下载完整的示例代码,其中包括了这两个JSP页面。下载地址:
4、配置Struts配置文件struts-config.xml
struts-config.xml的内容如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" " http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <data-sources /> <form-beans > <form-bean name="userForm" type="com.easyjf.struts.form.UserForm" /> </form-beans> <action-mappings > <action attribute="userForm" input="/userEdit.jsp" name="userForm" path="/userManage" scope="request" type="com.easyjf.struts.action.UserManageAction"> <forward name="edit" path="/userEdit.jsp" /> <forward name="list" path="/userList.jsp" /> </action> </action-mappings> <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"> <set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" /> </plug-in> </struts-config>
5、最后还差一步,就是配置web.xml文件。
Web.xml文件内容如下 <?xml version="1.0" encoding="UTF-8"?> <web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
工作都完了,自己在检查检查,可以作一些单元测试等工作,并把接口用说明文档写好交给老大吧。
下面我们再看看其它角色的表演! |
| |
作者:大峡 来源:www.easyjf.com 发布时间:2006-05-09 | |
第四步,商业逻辑层(数据库开发工程师以及主程序员等)
本来还可以分成很多层的,笔者怕把新手给晕得一个不剩了,所以把N层先暂时合并到一层,其实这也是我们在一般的实际项目中经常使用的方式。
本例中采用的是hibernate来做数据库访问中间件,因此,请新同学按以下步骤进行: 1、设计一个实现IUser接口的类,用来做VO(PO)。 User.java的内容全部如下: package com.easyjf.example.business.hibernate; import com.easyjf.example.business.IUser; public class User implements IUser,java.io.Serializable { private static final long serialVersionUID=10083494895498l; // Fields private String cid; private String userName; private String password; private String email; private String tel; private String birthday; private String intro; // Constructors /** default constructor */ public User() { } /** constructor with id */ public User(String cid) { this.cid = cid; } public String getCid() { return cid; } public void setCid(String cid) { this.cid = cid; } …….//更多的垃圾getter及setter代码省略 }
2、写hibernate的配置文件
然后结合User.java及数据库管理员建的表结构,写hibernate映射配置文件User.hbm.xml,全部内容如下:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" " http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.easyjf.example.business.hibernate.User" table="User"> <id name="cid" type="string"> <column name="cid" length="16" /> <generator class="com.easyjf.example.business.hibernate.HibIdGenerator"> <param name="column">cid</param> <param name="length">16</param> </generator> </id> <property name="userName" type="string"> <column name="username" length="16" not-null="true" /> </property> <property name="password" type="string"> <column name="password" length="16" not-null="true" /> </property> <property name="email" type="string"> <column name="Email" length="50" /> </property> <property name="tel" type="string"> <column name="tel" length="50" /> </property> <property name="birthday" type="string"> <column name="birthday" length="23" /> </property> <property name="intro" type="string"> <column name="intro" length="500" /> </property> </class> </hibernate-mapping>
注意其中有关主键生成的部份配置如下:
<generator class="com.easyjf.example.business.hibernate.HibIdGenerator"> 笔者这里使用的是自己的主键值生成器。你也可以使用hibernate自带的默认主键生成器,怎么用就不说了。大家可以从本示例完整代码下载中获得HibIdGenerator.java类的源代码,下载地址:
3、 写IUserService的实现
package com.easyjf.example.business.hibernate; import java.util.Collection; import java.util.List; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import com.easyjf.example.business.IUser; import com.easyjf.example.business.IUserService; public class UserDao extends HibernateDaoSupport implements IUserService { public boolean del(IUser user) { boolean ret=true; try{ this.getHibernateTemplate().delete(user); } catch(Exception e) { ret=false; } return ret; } public List query(String scope,Collection paras) { return this.getHibernateTemplate().find("from User where "+scope,paras.toArray()); } public IUser login(String userName, String password, String ip) { IUser user=readByName(userName); if(user==null || user.getPassword()==null ||(!user.getPassword().equals(password)))//用户不存在或者密码不正确 { return null; } return user; } public IUser newUser() { return new User(); } public IUser read(String cid) { return (IUser)this.getHibernateTemplate().get(User.class,cid); }
public IUser readByName(String userName) {
List list= this.getHibernateTemplate().find("from User where userName='"+userName+"'"); if(list!=null)return (IUser)list.get(0); else return null; }
public boolean save(IUser user) {
boolean ret=true; try{ this.getHibernateTemplate().save(user); } catch(Exception e) { ret=false; } return ret; } public boolean update(IUser user) { boolean ret=true; try{ this.getHibernateTemplate().update(user); } catch(Exception e) { e.printStackTrace(); ret=false; } return ret; } }
从代码中我们看到,UserDao类继承于Spring的HibernateDaoSupport类,目的在于方便通过hibernate操作数据库,同时实现了系统设计师所设计的IUserService接口中的相关功能。
是对是错,可以自己单元测试一下。
完了吗?还没有呢。下面是最后一步,也是本文所要介绍的重点,主角Spring正式登场,大家鼓掌。(咦,刚才在Struts即hibernate部分不是都提到Spring的吗?哦,那只是客串!)
第五步:配置Spring
大家可以看到,上面我们所做的各部分都是各自为阵、分开的,特别是系统设计师的接口、Web工程师的MVC、数据库开发及主程序员的商业逻辑层的实现,都相互没有关联、整合到一起。甚至就连数据库工程师还不知道数据源从哪儿来,数据库的访问用户名密码等通通不知道。
那么这些东西怎么相互集成起来,并组合成一个完整的应用拿给用户跑呢?这就是Spring扮演的角色之一了,Spring不是一直号称自己提供了这样那样的支持吗?下面我们来看看是怎么支持的。下面看看Spring的配置文件内容,本例中是applicationContext.xml,全部内容如下: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" " http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName"> <value>org.gjt.mm.mysql.Driver</value> </property> <property name="url"> <value>jdbc:mysql://127.0.0.1:3306/easyjf2</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>mysql</value> </property> </bean> <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" singleton="true"> <property name="dataSource"> <ref local="dataSource" /> </property> <property name="mappingResources"> <list> <value>com/easyjf/example/business/hibernate/User.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local="mySessionFactory" /> </property> </bean> <bean id="userService" class="com.easyjf.example.business.hibernate.UserDao"> <property name="sessionFactory"> <ref local="mySessionFactory" /> </property> </bean> </beans>
其中dataSource部分是配置的数据源,如用户名及密码等,mySessionFactory部分主要针对hibernate的配置文件,userService部分针对hibernateDao的配置,事务的部分没用到。
当然,要让Spring跟Java Web应用集成,还需要配置一下web.xml文件,前面的web.xml已经有了,这里再拿出来大家观摩一下: <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
看到了吧,通过配置,就可以把刚才我们的几个部分集成到了一起。换一句话着,就是一旦有一天(真有这么一天吗?小样的,别插嘴!)我们需要改系统的某一部份(比如数据库中间件不能用hibernate,而要用iBatis、EasyDBO或者是自己的“轮子”时),也只需要直接在配置文件中修改即可, 而系统的其它部分仍然保留不变。而且各部分的测试也相对独立,因此可以测试到更多的东西(咦,这么说测试工作量不就大了吗?是啊,但出错的机会就少,维护成本就低了,笨)。这即是传说中配置编程,也是面向接口编程乃至面向对象编程的精华之所在。
本示例中,我们主要涉及到的只是Spring最简单的一个IOC容器功能。因此,其值不值得我们HC(喝彩、花痴―凉粉语),我想每人心中都会有一杆秤。对于Spring与struts、hibernate,毕竟是庸俗的黄金组合嘛,他们之间还有更多的合作招式可供大家选择,这里就不一一介绍了,以后会介绍一些有关hibernate组合的!
OK,全文结束。还没睡着或晕的同学请到EasyJF开源团队官网下载本例源码。笔者认识的程序员不少,不过还没看见有谁仅仅靠“观摩”、“理解”就能写出程序的,还要多动手实践,程序跑起来才是硬道理。
(
备注:由于笔者不想拐弯抹角浪费大家玩的时间,有些“表白”难免过于直接,还请不喜欢Spring或者过分喜欢Spring的同行多多见谅! 本文中的“我们”,仅指与笔者有着同样成长经历的80后人,对于文章提到的观点,多数皆属于笔者个人观点,不代表任何人。
本文作者: EasyJF开源团队 大峡 版权归 EasyJF开源团队所有,欢迎转载,转载请保留作者版权声明,谢谢!)
附
关于玩玩Spring系列,玩的过程及结果将会不定期在以下三个网站全球同步公布:
1.EasyJF开源团队官方网站,网址: http://www.easyjf.com 2.笔者的Blog,网址: http://www.blogjava.net/daxia/ 3.Java研究组织(JR) ,网址: http://www.javaresearch.org 欢迎更多跟我一样爱玩的哥哥姐姐弟弟妹妹一起来玩。 |