- 浏览: 73042 次
- 性别:
- 来自: 杭州
最新评论
-
zhaojian770627:
...
B+树的Java实现 -
kevindude:
包含在了clyde库里
Clyde学习笔记三(Config) -
silverys:
能否提供demo下载学习一下,谢谢
3D MMO Demo -
silverys:
请问下,这个Three Rings Resource Edit ...
Clyde学习笔记三(Config) -
Bactryki:
要用什么包呢?
基于Struts2+Spring+iBatis的web应用最佳实践系列之二(访问控制篇上)
让我们首先来看一下什么是CRUD,CRUD即Create-Read-Update-Delete的英文缩写,不过笔者还要在这里加上个L,即List,合起来即CRUDL。CRUDL这种场景或者说模式在绝大部分的应用当中都很常见,那么,每写一个应用如果都要重复一遍这样的劳动显然显得十分的繁琐,那有没有一种优雅、干净的办法实现简单的CRUDL呢?笔者在此就向大家介绍一种只需要极少量的代码和简单的配置就能实现完整的CRUDL功能。
如果说要使用一种通用的,可复用的方法来实现基本的CRUDL功能的话,那我们就需要分析和观察大部分的应用是如何完成CRUDL的功能,并在此基础上总结出一般性的规律,然后根据这个规律实现一个通用的解决方法。
我们以在博客上发帖为例,当用户点击发帖(add)按钮时,当前页面就跳转到编辑页面<edit>,当用户编辑完成点击发布(save)后,用户编辑的内容会被保存,同时页面跳转到新发布的文章<view>。当用户在浏览博客文章的列表时<browse>,点击编辑(edit)按钮时,当前页面就跳转到编辑页面<edit>,当用户编辑完成点击发布(save)后,用户编辑的内容会被保存,同时页面跳转到用户所有已发布博客文章的列表<browse>。在浏览的时候博客文章或列表<view><browse>的时候,点击删除(remove)后,文章即被删除,同时页面跳转到博客文章的列表页面<browse>。
读者朋友肯定已经注意到了,在上面这段描述中,对于一些关键词,笔者一共使用了两种不同的格式。对了,()表示一种行为,<>表示一个页面。一个行为正好可以映射到Struts2中的一个aciton,而一个页面正好可以映射到Struts2中的一个result。不过这里要注意的是,<view><browse>的页面与<edit>页面有所不同,<view>自身还包括了一个(show)action,即页面本身include一个show aciton和这个aciton所生成的页面;<browse>自身还包括了一个(list)aciton,即页面本身include一个list aciton和这个aciton所生成的页面。
也许上面的解释不一定清楚,不过没关系,我们再来看一下下面这个表格。
Action方法 | Action方法描述 | 返回页面 | 返回页面描述 |
add | 跳转到编辑页面 | edit | 编辑页面 |
edit | 先SELECT出该博客文章,再跳转到编辑页面 | edit | 编辑页面 |
remove | commit DELETE | browse | 包含list action的页面 |
save | commit INSERT或者UPDATE | view | 包含show action的页面 |
list | 根据一个reference id(通常为foreign key,这里是loginId)SELECT 出一个列表 | list | 该reference id(在这里是loginId)下的文章列表 |
show | 根据primary key SELECT出该博客文章 | show | 该博客文章的内容 |
在这里我把browse和list分开,把view和show分开主要是考虑到了页面组件的可重用性,比如菜单栏、导航栏等组件几乎贯穿所有页面,可以放在browse或view页面中,但不应该由remove或者show action方法渲染出。
另外有些aciotn方法名和页面名相重复,但代表着完全不同意思,读者朋友不要搞混了,(以后会考虑重构)。
就象大家在本系列的上几篇中看到的那样,拦截器始终贯穿着Struts2,这次也一样,在介绍如何用一个baseAction实现所有的CRUDL功能之前,我们先来看几个Struts2内置的拦截器。首先是params,这个拦截器相比大家都很熟悉,只要你用Struts2就几乎没有不用params的,它的职责就是把request中的参数注入到action中,另一个我们要引入的拦截器是prepare,prepare拦截器在aciton执行前给用户个机会用来做一些准备工作,比如初始化等。另一个要出场的是modelDriven拦截器,这个拦截器可以把aciton中的property push到valueStack上。Struts2中发行包中本身已经内置了许多拦截器和拦截器堆栈,除了我们大家熟知的defaultStack之外其中还有一个叫做paramsPrepareParamsStack,就如同它的名字一样,在两个params拦截器中夹了一个prepare,当然还有我们的modelDriven,这个拦截器堆栈似乎就是为我们今天的任务所设计的,用好它可以发挥出让人意想不到的威力。
<interceptor-stack name="paramsPrepareParamsStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="params"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="i18n"/> <interceptor-ref name="chain"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel</param> </interceptor-ref> </interceptor-stack>
下面我们来看一下我们的博客action类,是的,大家不要惊讶,就是这么简单。
public class PostAction extends BaseAction<Post, Cookie> implements Preparable, ModelDriven<Post> { private Post model; @Override public Post getModel(){ return model; } public void prepare() throws Exception { if(getRequestId()==null || getRequestId().isEmpty()){ model = new Post(); }else{ Result result = ao.doGet(getRequestId()); model = (Post)result.getDefaultModel(); } } }
这里ao是我们的业务逻辑类,result是以一个工具类,主要用来在aciton层与业务逻辑层之间传递消息用。当拦截器堆栈里的第一个params拦截器被调用时,它会去request当中寻找requestId,并把值注入到aciton中。然后是prepare拦截器被调用,若requestId值为空,它会new一个post类,并把值赋给model;如果值不为空,它会调用ao取得这个post对象,并把它赋给model,当然了如果调用的aciton的list方法,因为list方法也需要一个requestId,但是list方法往往需要的是一个foreign key而非entity id(primary key)所以此requestId非彼requestId会取不到post对象,不过这并不影响什么。然后是modelDriven拦截器会把这个model对象push到valueStack上。最后,当第二个params拦截器被调用的时候,若用户调用的是save方法,则params拦截器会把用户提交的表单内容注入到post对象中。
不过这里要注意的是因为ao在baseAciton中定义,而对于每个不同的aciton类来说,需要对应不同的ao,所以这里我们的action需要用spring来配置,ao也由spring来注入。下面是对于这个action的配置,可以看到aciton对应的class不再是一个类名,而是spring配置中的bean id,另外别忘了要使用ParamsPrepareParamsStack而不是默认的那个,在这里由于我们增加了cookie拦截器,所以用的是自定义的cookieParamsPrepareParamsStack。还可以看到对于show和list两个result来说我们用了freemarker模板;而对于view跟browse两个result来说我们使用了上一篇当中介绍的dispatcherAction,他们实际对应的是两个jsp页面,每个jsp页面中又分别include了show和list两个aciton(方法)。
<action name="post" class="post"> <interceptor-ref name="cookieParamsPrepareParamsStack" /> <result name="show" type="freemarker">/WEB-INF/ftl/demo/showPost.ftl</result> <result name="list" type="freemarker">/WEB-INF/ftl/demo/listPost.ftl</result> <result name="view" type="redirect">/demo/viewPost.action?requestId=${requestId}</result> <result name="browse" type="redirect">/demo/browse.action</result> <result name="edit">/WEB-INF/jsp/demo/editPost.jsp</result> <result name="input">/WEB-INF/jsp/demo/editPost.jsp</result> <result name="error">/WEB-INF/jsp/demo/error.jsp</result> <result name="none">/WEB-INF/jsp/demo/error.jsp</result> </action>
spring对aciton的配置,对于在Struts2中如何用spring来配置aciton这里就不再赘述了,网上入门的资料很多,读者可以自行查阅。
<bean id="post" class="com.meidusa.pirateweb.web.post.PostAction" scope="prototype"> <property name="ao" ref="postAO"/> </bean>
下面我们再来看一下baseAction这个基类。ClientCookie我们在上一篇当中已经介绍过,这里不再重复,pageable接口以及Paging类等是跟分页相关的,笔者会在本系列的下一篇当中介绍。这里需要指出的是这个baseAction使用了泛型模板,T用来指代model类,K用来指代用户自定义的Cookie类,如果读者对于泛型不是很熟悉的话也请自行查阅相关资料,这里不再赘述(下面还会一直用到)。
public abstract class BaseAction<T,K extends ClientCookie> extends ActionSupport implements ClientCookieAware<K>, Pageable{ protected BaseAO<T> ao; protected static Logger logger = Logger.getLogger(BaseAction.class); protected String requestId; protected List<T> list; protected K cookie; protected Paging paging = new Paging(); public Paging getPaging() { return paging; } public void setPaging(Paging paging) { this.paging = paging; } public K getCookie() { return cookie; } public void setClientCookie(K cookie){ this.cookie = cookie; } public Date getCurrDateTime(){ return new Date(); } public List<T> getList() { return list; } public void setList(List<T> list) { this.list = list; } @SkipValidation public String add() { return Constants.EDIT; } @SkipValidation public String edit() { return Constants.EDIT; } @SkipValidation public String show() { if (getModel()==null){ return ERROR; } return Constants.SHOW; } public String save() { Result result = ao.doSave(getModel(),getForeignKey()); if (!result.isSuccess()){ ResultCode resultCode = result.getResultCode(); this.addActionError(resultCode.getMessage().getMessage()); return ERROR; } requestId = (String)result.getModels().get("requestId"); return Constants.VIEW; } @SkipValidation public String remove() { Result result = ao.doRemove(getModel(),getForeignKey()); if (!result.isSuccess()){ ResultCode resultCode = result.getResultCode(); this.addActionError(resultCode.getMessage().getMessage()); return ERROR; } requestId = (String)result.getDefaultModel(); if (!list().equals(ERROR)){ return Constants.BROWSE; }else{ return ERROR; } } @SkipValidation public String list() { Result result = ao.doList(requestId, paging); if (result.isSuccess()){ list = (List)result.getDefaultModel(); return Constants.LIST; }else { ResultCode resultCode = result.getResultCode(); this.addActionError(resultCode.getMessage().getMessage()); return ERROR; } } public String getRequestId() { return requestId; } public void setRequestId(String requestId) { this.requestId = requestId; } public void setAo(BaseAO<T> ao) { this.ao = ao; } protected String getForeignKey(){ return cookie.getLoginId(); } public abstract T getModel(); }
这里还要指出的是public abstract T getModel()方法是抽象的,需要子类来实现。另外每个子类的ao都需要不同实现所以不能要在spring里手工配置来注入而不能直接autowire by name。还有就是对于那些不需要验证的方法使用了@SkipValidation的标注。Struts2中的验证框架是针对单独一个aciton的,而不是aciton中的某个具体的方法,而在我们这个aciton中包含了多个方法,所以对于那些不要验证的方法可以使用框架所提供的标注跳过验证。
下面我们再来看一下baseAO
public interface BaseAO<T> { public Result doSave(T model, String foreignKey); public Result doRemove(T model, String foreignKey); public Result doGet(String id); public Result doList(String foreignKey, Paging paging); public Result doPaging(int rows, String loginId); }
这个baseAO是一个接口,对于CRUDL功能来说,它是action层对业务层调用的规范。一共有5个方法,其中doPaging是跟分读取页相关的,我们会在下一篇中介绍,这里就不再展开。另外4个方法分别对应保存(包括新建和修改)、删除、读取和列表。
下面我们有一个对于这个接口的抽象实现。在这个抽象实现中抽象了基本的CRUDL方法,并给出了默认的实现。
public abstract class AbstractAO<T extends AbstractModel, K extends AbstractExample<? extends AbstractExample.Criteria>> implements BaseAO<T>{ protected AbstractDAO<T,K> dao; public void setDao(AbstractDAO<T, K> dao) { this.dao = dao; } protected AbstractDAO<T, K> getDao(){ return dao; } public Result doSave(T model, String foreignKey){ Result result = new ResultSupport(); result.setSuccess(false); if (model.getPrimaryKey() == null || model.getPrimaryKey().isEmpty()) { model.setPrimaryKey(GuidTool.generateFormattedGUID()); model.setForeignKey(foreignKey); dao.insert(model); }else{ if (!model.getForeignKey().equals(foreignKey)){ result.setResultCode(ResultCode.ACCESS_VIOLATION); return result; } dao.updateByPrimaryKeySelective(model); } result.setDefaultModel("requestId", model.getPrimaryKey()); result.setSuccess(true); return result; } public Result doRemove(T model, String foreignKey){ Result result = new ResultSupport(); result.setSuccess(false); if (!model.getForeignKey().equals(foreignKey)){ result.setResultCode(ResultCode.ACCESS_VIOLATION); return result; } dao.deleteByPrimaryKey(model.getPrimaryKey()); result.setDefaultModel(model.getForeignKey()); result.setSuccess(true); return result; } public Result doGet(String id){ Result result = new ResultSupport(); result.setSuccess(false); T model = dao.selectByPrimaryKey(id); if (model == null){ result.setResultCode(ResultCode.MODEL_OBJECT_NOT_EXIST); return result; } result.setDefaultModel(model); result.setSuccess(true); return result; } public Result doList(String foreignKey, Paging paging){ Result result = new ResultSupport(); result.setSuccess(false); K example = doExample(foreignKey); if (paging.getRows()!=0){ example.setOffset(paging.getOffset()); example.setRows(paging.getRows()); } List<T> list = dao.selectByExampleWithoutBLOBs(example); result.setDefaultModel(list); result.setSuccess(true); return result; } public Result doPaging(int rows, String foreignKey){ Result result = new ResultSupport(); result.setSuccess(false); K example = doExample(foreignKey); int totalRecord = dao.countByExample(example); int totalPage = (int)Math.ceil(totalRecord / (double)rows); result.setDefaultModel("totalRecord", totalRecord); result.setDefaultModel("totalPage", totalPage); result.setSuccess(true); return result; } protected abstract K doExample(String foreignKey); }
而我们的客户客户代码只需要实现一个doExample方法就可以了。
public class PostAO extends AbstractAO<Post, PostExample> { @Override protected PostExample doExample(String foreignKey) { PostExample example = new PostExample(); example.createCriteria().andLoginidEqualTo(foreignKey); return example; } }
在DAO层
抽象model类
public abstract class AbstractModel { public abstract String getPrimaryKey(); public abstract void setPrimaryKey(String primaryKey); public abstract String getForeignKey(); public abstract void setForeignKey(String foreignKey); }
抽象DAO接口
public interface AbstractDAO<T,K> { int countByExample(K example); int deleteByExample(K example); int deleteByPrimaryKey(String id); void insert(T record); void insertSelective(T record); List<T> selectByExampleWithBLOBs(K example); List<T> selectByExampleWithoutBLOBs(K example); T selectByPrimaryKey(String id); int updateByExampleSelective(T record, K example); int updateByExampleWithBLOBs(T record, K example); int updateByExampleWithoutBLOBs(T record, K example); int updateByPrimaryKeySelective(T record); int updateByPrimaryKeyWithBLOBs(T record); int updateByPrimaryKeyWithoutBLOBs(T record); }
抽象example类
public abstract class AbstractExample<T extends AbstractExample.Criteria> { protected String orderByClause; protected List<T> oredCriteria; public AbstractExample() { oredCriteria = new ArrayList<T>(); } protected int offset; protected int rows; protected boolean distinct; public boolean isDistinct() { return distinct; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public void setOffset(int offset){ this.offset = offset; } public int getOffset(){ return offset; } public void setRows(int rows){ this.rows = rows; } public int getRows(){ return rows; } protected AbstractExample(AbstractExample<T> example) { this.orderByClause = example.orderByClause; this.oredCriteria = example.oredCriteria; this.offset = example.offset; this.rows = example.rows; this.distinct = example.distinct; } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public List<T> getOredCriteria() { return oredCriteria; } public void or(T criteria) { oredCriteria.add(criteria); } public T createCriteria() { T criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected abstract T createCriteriaInternal(); public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; offset = 0; rows = 0; } public static abstract class Criteria { protected List<String> criteriaWithoutValue; protected List<Map<String, Object>> criteriaWithSingleValue; protected List<Map<String, Object>> criteriaWithListValue; protected List<Map<String, Object>> criteriaWithBetweenValue; protected Criteria() { super(); criteriaWithoutValue = new ArrayList<String>(); criteriaWithSingleValue = new ArrayList<Map<String, Object>>(); criteriaWithListValue = new ArrayList<Map<String, Object>>(); criteriaWithBetweenValue = new ArrayList<Map<String, Object>>(); } public boolean isValid() { return criteriaWithoutValue.size() > 0 || criteriaWithSingleValue.size() > 0 || criteriaWithListValue.size() > 0 || criteriaWithBetweenValue.size() > 0; } public List<String> getCriteriaWithoutValue() { return criteriaWithoutValue; } public List<Map<String, Object>> getCriteriaWithSingleValue() { return criteriaWithSingleValue; } public List<Map<String, Object>> getCriteriaWithListValue() { return criteriaWithListValue; } public List<Map<String, Object>> getCriteriaWithBetweenValue() { return criteriaWithBetweenValue; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteriaWithoutValue.add(condition); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } Map<String, Object> map = new HashMap<String, Object>(); map.put("condition", condition); map.put("value", value); criteriaWithSingleValue.add(map); } protected void addCriterion(String condition, List<? extends Object> values, String property) { if (values == null || values.size() == 0) { throw new RuntimeException("Value list for " + property + " cannot be null or empty"); } Map<String, Object> map = new HashMap<String, Object>(); map.put("condition", condition); map.put("values", values); criteriaWithListValue.add(map); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } List<Object> list = new ArrayList<Object>(); list.add(value1); list.add(value2); Map<String, Object> map = new HashMap<String, Object>(); map.put("condition", condition); map.put("values", list); criteriaWithBetweenValue.add(map); } public abstract Criteria andForeignKeyEqualTo(String value); } }
我们需要为每个抽象类提供一个实现并且还要配置好ibatis的sqlMapper,看起来工作量比较大,不过还好我们有工具。使用过iBatis的朋友一定知道在Apache基金会iBatis项目下还有一个叫IBator的子项目,它是专门为iBatis自动生成代码的工具,即可以在命令行下运行,也可以作为eclipse的一个插件来运行,不过大家一般都会在eclipse下使用。笔者在IBator的基础上修改了一下,重新做了一个eclipse插件, 笔者叫它ibatorPlus,呵呵。ibatorPlus可以让生成的代码分别自动继承AbstractModel ,AbstractExample,实现AbstractDAO接口。在代码生成完之后只需要实现AbstractModel中的4个抽象方法就可以了。
@Override public String getForeignKey() { return loginid; } @Override public String getPrimaryKey() { return id; } @Override public void setForeignKey(String foreignKey) { this.loginid = foreignKey; } @Override public void setPrimaryKey(String primaryKey) { this.id = primaryKey; }
Ibator与ibatorPlus,使用了这个工具之后,在dao层所有需要手工写的代码只有上面区区4个。
使用ibatorPlus生成的sqlmap还可以自动包含分页的代码limit $offset$, $rows$(mysql数据库)。自动分页功能我们会在本系列的下一篇详细介绍,这里就不在赘述。
<select id="selectByExample" parameterClass="com.meidusa.demo.dal.model.PostExample" resultMap="BaseResultMap"> <!-- WARNING - @ibatorgenerated This element is automatically generated by Apache iBATIS Ibator, do not modify. This element was generated on Thu Feb 04 16:15:41 CST 2010. --> select <isParameterPresent> <isEqual compareValue="true" property="distinct"> distinct </isEqual> </isParameterPresent> <include refid="post.Base_Column_List" /> from post <isParameterPresent> <include refid="post.Example_Where_Clause" /> <isNotNull property="orderByClause"> order by $orderByClause$ </isNotNull> <isNotEqual compareValue="0" property="rows"> limit $offset$, $rows$ </isNotEqual> </isParameterPresent> </select>
至此,本篇总算是介绍完了,使用本篇所介绍的方法来我们只需手工写非常少量的代码就能获得全部的CRUDL功能。下面我们再把需要手工书写的代码罗列一下。
web层action
public class PostAction extends BaseAction<Post, Cookie> implements Preparable, ModelDriven<Post> { private Post model; @Override public Post getModel(){ return model; } public void prepare() throws Exception { if(getRequestId()==null || getRequestId().isEmpty()){ model = new Post(); }else{ Result result = ao.doGet(getRequestId()); model = (Post)result.getDefaultModel(); } } }
业务逻辑层ao
public class PostAO extends AbstractAO<Post, PostExample> { @Override protected PostExample doExample(String foreignKey) { PostExample example = new PostExample(); example.createCriteria().andLoginidEqualTo(foreignKey); return example; } }
数据访问层dao
@Override public String getForeignKey() { return loginid; } @Override public String getPrimaryKey() { return id; } @Override public void setForeignKey(String foreignKey) { this.loginid = foreignKey; } @Override public void setPrimaryKey(String primaryKey) { this.id = primaryKey; }
其它就仅仅只剩下了xml的配置。
发表评论
-
基于Struts2+Spring+iBatis的web应用最佳实践系列之七(Demo篇)
2010-02-08 15:01 3125终于来到了本系列的最 ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之六(验证码篇)
2010-02-05 18:34 1809本篇主要讨论下如何使用Struts2实现一个通用的验证码(ch ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之五(分页篇)
2010-02-05 17:13 2476分页也是一个大家经常讨论的话题,网上也有过很多的分页方法的介绍 ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之三(访问控制篇下)
2010-02-03 18:47 2293在本系列的上一篇中我 ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之二(访问控制篇上)
2010-02-03 15:40 2477访问控制对于一个web应用来说几乎是不可或缺的。当访问web应 ... -
基于Struts2+Spring+iBatis的web应用最佳实践系列之一(自动配置篇)
2010-02-02 00:13 3737由于最近有点时间,便 ...
相关推荐
这个“struts2+spring+iBatis框架包”集成了这三个框架,使得开发者能够快速构建基于MVC(Model-View-Controller)模式的Web应用。 Struts2作为MVC框架,负责处理应用程序的控制逻辑。它通过Action类和配置文件定义...
Struts2、Spring3和iBATIS是Java Web开发中常用的三大框架,它们各自负责不同的职责,协同工作可以构建出高效、松耦合的Web应用。在这个“struts2+spring3+ibatis项目整合案例”中,我们将深入探讨这三个框架如何...
Struts2、Spring和iBatis是Java Web开发中三个非常重要的开源框架,它们的集成应用可以构建出高效、灵活的后端系统。Struts2作为MVC(Model-View-Controller)架构的一部分,主要负责处理HTTP请求和展示视图;Spring...
Struts2、Spring和iBatis是Java Web开发中三个非常重要的框架,它们分别负责表现层、业务层和数据访问层。将这三个框架整合在一起,可以实现MVC(Model-View-Controller)架构,提高应用的灵活性和可维护性。 **...
Struts2+Spring+Hibernate和Struts2+Spring+Ibatis是两种常见的Java Web应用程序集成框架,它们分别基于ORM框架Hibernate和轻量级数据访问框架Ibatis。这两种框架结合Spring,旨在提供一个强大的、可扩展的、易于...
"Struts2+Spring+Ibatis+MySQL" 是一个经典的Java Web开发框架组合,用于构建高效、可扩展的企业级应用程序。这个组合集成了强大的MVC(Model-View-Controller)框架Struts2、依赖注入与面向切面编程的Spring框架、...
开发环境说明 ...本示例完整地结合Struts2+Spring2+iBatis2+MySQL5,演示了一个用户表的增、删、改、查。 想完整学习Struts2+Spring+iBatis的同仁,可以在这个例子中学习或模仿最基本也是最核心的技术要点。
Struts2、Spring和iBatis是Java Web开发中经典的三大框架,它们分别负责MVC模式中的Action层、业务逻辑层和服务数据访问层。这个"Struts2+Spring2.5+iBatis完整增删改查Demo"提供了一个完整的集成示例,包括所有必要...
在Java Web开发中,Spring、Struts2和iBatis是三个非常重要的框架,它们各自在不同的层面上提供了强大的功能。Spring是一个全面的后端应用框架,提供了依赖注入(DI)、面向切面编程(AOP)、事务管理等功能;Struts...
Struts2、Spring和iBatis是Java Web开发中常用的三大框架,它们分别负责MVC模式中的Action层、业务逻辑层和服务数据访问层。本文将详细介绍这三个框架如何整合,以及在实际项目中如何运用。 首先,Struts2作为表现...
Struts2、Spring2 和 iBatis 是三个在Java Web开发中广泛应用的开源框架,它们分别负责MVC架构中的控制层、服务层和数据访问层。这个集成项目,"SSITest",是为了帮助初学者理解和实践这三大框架的协同工作。 **...
struts2 + spring + ibatis 实例 struts2 + spring + ibatis 实例 struts2 + spring + ibatis 实例 struts2 + spring + ibatis 实例 struts2 + spring + ibatis 实例
Struts2、Spring和iBatis是Java Web开发中经典的三大框架,它们组合起来可以构建出高效、可维护的企业级应用程序。在这个“struts2+spring+ibatis”的小demo中,我们将深入探讨这三个框架的核心功能以及它们如何协同...
Struts2、Spring和iBatis是Java Web开发中常用的三个开源框架,它们各自负责不同的职责,协同工作可以构建出高效、松耦合的Web应用。这个整合项目实例旨在展示如何将这三个框架集成到一起,以实现更强大的功能。 1....
Struts2、Spring和iBatis是Java Web开发中经典的三大框架,它们组合起来可以构建出高效、可维护的企业级应用程序。在这个项目实例中,我们将深入探讨这三个框架如何协同工作,以及它们各自的核心功能。 首先,...
struts2+hibernate+spring+ibatis 小实例struts2+hibernate+spring+ibatis 小实例struts2+hibernate+spring+ibatis 小实例struts2+hibernate+spring+ibatis 小实例struts2+hibernate+spring+ibatis 小实例struts2+...
Struts2+Spring+Ibatis整合的简单人事管理系统 没分了,转载过来的,有需要的看看吧,我觉得不错~~
Struts2、Spring2和iBatis是Java Web开发中常用的三大框架,它们结合使用可以构建高效、可扩展的企业级应用程序。在这个简单的登录例子中,我们将深入探讨这三个框架如何协同工作来实现用户身份验证。 首先,Struts...
Struts2、Spring和iBatis是Java Web开发中常用的三大框架,它们分别负责MVC模式中的Action层、业务逻辑层以及数据访问层。将这三个框架整合在一起,可以构建出高效、灵活且易于维护的Web应用程序。下面我们将详细...
Struts2+Spring2.5+Ibatis2.3架构是一种经典的Java Web开发技术栈,广泛应用于企业级应用系统中。这个架构结合了Struts2的MVC框架、Spring的依赖注入(DI)和面向切面编程(AOP)以及Ibatis的持久层解决方案,为...