前言
struts2提供了通过action实现modeldriven接口而自动封装我们需要的实体对象的功能,这个功能十分实用。因此就在想其实现原理是怎么样的,通过查看源码,得到一些答案,以下是我的理解。
使用ModelDriven的示例
首先需要实体对象
public class CrmCourseType {
/**
* CREATE TABLE `crm_course_type` (
`courseTypeId` varchar(255) NOT NULL PRIMARY KEY,
`courseCost` double DEFAULT NULL,
`total` int(11) DEFAULT NULL,
`courseName` varchar(500) DEFAULT NULL,
`remark` varchar(5000) DEFAULT NULL
);
*/
private String courseTypeId;
private Double courseCost;
private Integer total;
private String courseName;
private String remark;
private Set<CrmClass> crmClasses = new HashSet<CrmClass>();
private String totalStart;
private String totalEnd;
private String lessonCostStart;
private String lessonCostEnd;
public String getCourseTypeId() {
return courseTypeId;
}
public void setCourseTypeId(String courseTypeId) {
this.courseTypeId = courseTypeId;
}
public Double getCourseCost() {
return courseCost;
}
public void setCourseCost(Double courseCost) {
this.courseCost = courseCost;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Set<CrmClass> getCrmClasses() {
return crmClasses;
}
public void setCrmClasses(Set<CrmClass> crmClasses) {
this.crmClasses = crmClasses;
}
public String getTotalStart() {
return totalStart;
}
public void setTotalStart(String totalStart) {
this.totalStart = totalStart;
}
public String getTotalEnd() {
return totalEnd;
}
public void setTotalEnd(String totalEnd) {
this.totalEnd = totalEnd;
}
public String getLessonCostStart() {
return lessonCostStart;
}
public void setLessonCostStart(String lessonCostStart) {
this.lessonCostStart = lessonCostStart;
}
public String getLessonCostEnd() {
return lessonCostEnd;
}
public void setLessonCostEnd(String lessonCostEnd) {
this.lessonCostEnd = lessonCostEnd;
}
}
Aciton类
public class CourseTypeAction extends ActionSupport implements ModelDriven<CrmCourseType> {
//
private CrmCourseType crmCourseType = new CrmCourseType();
//ervice
private CourseTypeService courseTypeService;
public void setCourseTypeService(CourseTypeService courseTypeService) {
this.courseTypeService = courseTypeService;
}
public CrmCourseType getModel() {
return crmCourseType;
}
/**
*
* @return
*/
public String findAll(){
List<CrmCourseType> allCourseType = this.courseTypeService.findAll();
ActionContext.getContext().put("allCourseType", allCourseType);
return "findAll";
}
}
另外在JSP表单中的name属性要与实体对象的属性名相同,以上的配置就可以说已经完成了,当访问CourseTypeAction时已经完成了其中的crmCourseType 实例已经封装了参数并且就是当前action中的具体实例,可以拿来直接操作。
实现分析之ModelDriven接口
该接口的作用是返回实例对象,并完成多态,使得在拦截器中可以方便的使用
public interface ModelDriven<T> {
/**
* Gets the model to be pushed onto the ValueStack instead of the Action itself.
*
* @return the model
*/
T getModel();
}
这是该接口中的唯一方法。
实现核心之ModelDrivenInterceptor
其中的参数封装是在ParameterInterceptor拦截器中完成的,暂且不说,返回可以使用的bean对象则是在ModelDrivenInterceptor拦截器中完成的
//核心代码
public String intercept(ActionInvocation invocation) throws Exception {
//首先获取需要访问的action
Object action = invocation.getAction();
//如果action实现了ModelDriven接口
if (action instanceof ModelDriven) {
//通过多态将欲访问的action转化成ModelDriven的实现
ModelDriven modelDriven = (ModelDriven) action;
//获取ValueStack
ValueStack stack = invocation.getStack();
//获取实体对象,这就是必须要在action中new出对象的原因
Object model = modelDriven.getModel();
//如果对象存在,则入栈
if (model != null)
stack.push(model);
}
//刷新实例(下面有具体分析)
if (refreshModelBeforeResult) {
invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
}
}
return invocation.invoke();
}
ModelDriven中有一个静态内部类RefreshModelBeforeResult,其作用就是判别在栈中是否有已存在的model实例,如果有就去除并放入当前实例,没有就放入当前的,简而言之就是刷新
protected static class RefreshModelBeforeResult implements PreResultListener {
//原始实例
private Object originalModel = null;
//实现了ModelDriven 的action对象
protected ModelDriven action;
public RefreshModelBeforeResult(ModelDriven action, Object model) {
this.originalModel = model;
this.action = action;
}
public void beforeResult(ActionInvocation invocation, String resultCode) {
//获取ValueStack
ValueStack stack = invocation.getStack();
//得到ValueStack底层的CompoundRoot实现
CompoundRoot root = stack.getRoot();
//一开始设置需要刷新
boolean needsRefresh = true;
//得到action中的新实例
Object newModel = action.getModel();
// 检查新实例是否已经在栈中
for (Object item : root) {
if (item.equals(newModel)) {
needsRefresh = false;
}
}
// 将新实例插入栈中
if (needsRefresh) {
// 删除原始实例
if (originalModel != null) {
root.remove(originalModel);
}
if (newModel != null) {
stack.push(newModel);
}
}
}
}