struts2 ModelDriven & Prepareable 拦截器
前面对于 Struts2 的开发环境的搭建、配置文件以及 Struts2 的值栈都已经进行过叙述了!这次博文我们讲解利用 Struts2 进行 CURD 操作,体验和 Servlet 的不一样,以此案例理解 ModelDriven 和 Prepareable 拦截器!
案例流程
- 获取数据库数据并将之显示在 employee-show.jsp 页面上
- 在 employee-show.jsp 页面可以添加员工信息到数据库,对现有的员工信息进行编辑以及删除操作
- 在将删除或编辑请求传到 action 方法时且将要操作的员工的 empId 以参数的形式传入
- 对现有员工信息进行编辑的时候需要先将其信息回显到表单再进行编辑
解决思路
Employee-show.jsp
- 首先将所有员工信息获取到并于页面展示,如下 JSP 页面发送请求经由 Action 类处理将所有的员工信息传回页面,在页面进行显示(为了方便我们将所有数据存入一个 Map 对象中,使用 Dao 类进行处理)
- emp-show.jsp 页面中利用 标签处理从 action 方法传回的员工信息的 List,并显示。
- 如上显示页面中每个员工信息行尾都会添加两个操作的超链接分别为 Edit 和 Delete
Employee-edit.jsp
- 点击某员工行后的 edit 超链接,其流程如上显示页面所述。struts.xml 文件中使用的是通配符映射,所以其经过 Action 方法处理将会到达 Employee-edit.jsp 页面。
- 点击edit超链接的时候会将所操作的员工的 id 传入 action 方法,即 edit() 方法,edit() 方法将会从现有的员工信息中获得对应的员工的信息将其回显在 Employee-edit.jsp 的表单上
- 在回显的页面上可以进行修改员工信息,点击提交执行 update() 方法,将更新存入 Map,并跳转到显示页面,实时显示更改的员工信息
emp-delete.action
- 点击员工行后的 delete 超链接,由于删除不需要任何页面,所以执行完删除操作之后跳转到 emp-show.action 显示操作后的员工信息。
- 点击 delete 超链接的时候会将所操作的员工的 Id 传入 action 方法,即 delete() 方法,delete() 方法从员工信息库中删除对应的员工信息然后将重定向到 emp-show.action,显示删除后的员工信息
emp-add.action
- 在 employee-show.jsp 页面的添加表单上填写将要添加的员工信息点击提交后执行 emp-show.action 获取新的员工信息列表并显示
- 点击 submit 后,将员工信息保存到一个新的对象中,执行 add() 方法将新的对象添加到存放用户列表中,再重定向到 emp-show.action,显示新的员工信息
案例目录
如上目录,其代码如下
- Employee.java
- EmployeeCurd.java
- Employee-edit.jsp
思考
- 如上代码,我们在EmployeeCurd.java 中以 show()、add()、delete() 方法为例,其中在 add() 方法时所使用的 Employee 对象以该类的属性初始化,那么该类的属性是如何被初始化的?执行 delete 方法时传入的 empId 又是如何给对应的属性赋值?
- 这些操作看似我们没有对其进行任何处理,但实际上 struts2 的 params 拦截器为我们将这些都做了,params 拦截器的功能是将表单属性值为栈顶对象的对应的属性赋值,即 add() 方法执行前将表单中对应的字段值赋值给栈顶对象(栈顶对象默认为 Action 类的对象,即 EmployeeCurd 对象)。
- 删除操作时执行 delete() 方法会根据 empId 而去操作对象,这就使得我们需要在执行 delete() 方法前获取到传入的 empId,我们知道 params 拦截器会根据栈顶属性去赋值,但是在默认拦截器栈中 params于 ModelDriven 拦截器其后,这时就需要使用 paramsPrepareParamsStack 拦截器栈,相比于默认拦截器此拦截器会在 ModelDriven 拦截器执行前先去执行一次 params 拦截器,在其后再执行一次 params 拦截器,这样的话 getModel 拦截器就会用到传入的 empId 参数,而我们也可以利用 empId 是否为空压入栈顶对应的对象,即添加操作时需要一个空的对象,更新操作时需要根据 empId 获取到已有对象压入栈顶以便回显
- 在 struts.xml 文件中配置使用 paramsPrepareParamsStack 拦截器栈,如下配置需要在 <default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>
回显问题
- 对现有员工信息进行编辑的时候在编辑表单会进行回显,是因为 struts2 的表单标签会自动在值栈中寻找匹配的属性值进行回显,例如执行了 emp-edit.action 执行的后 getModel() 方法会根据 empId 将从 Map 集合中获取到的对象压入栈顶,那么在显示页面的时候会从栈顶获取对应的对象为 struts2 的表单标签赋值
缺点
- EmployeeCurd 和 Employee 类中属性存在冗余,那么我们该如何解决?Struts2 默认的拦截器栈为我们提供了 ModelDriven 拦截器以解决此问题
- 实现
- Action 类实现 ModelDriven
- 使用 ModelDriven 拦截器的优点
- Action 类和 Model 类将不存在冗余,Action 类更加简洁
实现 ModelDriven 接口的 Action 类
代码语言:javascript复制public class EmployeeCurd implements RequestAware, ModelDriven<Employoee> {
private Dao dao = new Dao();
private Map<String, Object> requestMap;
private Employoee employoee;
private Integer empId;
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String delete() {
dao.deleteEmp(String.valueOf(empId));
return "delete";
}
public String update() {
dao.updateEmp(employoee);
return "update";
}
public String add() {
dao.addEmp(employoee);
return "add";
}
public String show() {
List<Employoee> employees = dao.getEmployee();
requestMap.put("empList", employees);
return "show";
}
public String edit() {
return "edit";
}
@Override
public void setRequest(Map<String, Object> map) {
this.requestMap = map;
}
@Override
public Employoee getModel() {
/*
* 判断调用该拦截器的时候是 edit 还是 show,这取决于是否有参数 empId,决定了 Employee 对象是新建还是依据 empId 获取
* */
if (empId != null) {
employoee = dao.getEmployee(String.valueOf(empId));
} else {
employoee = new Employoee();
}
return employoee;
}
}
- 详解
- ModelDriven 拦截器使用 getModel() 方法将对应的对象压入栈顶,例如 add() 方法执行的时候 getModel() 方法执行后其栈顶为 employee 对象,这样便可以利用 params 拦截器将表单对应的字段属性值赋给栈顶对象对应的属性值
- 源码解析(ModelDriven 拦截器的 intercept 方法)
CURD 操作进阶之 PrepareInterceptor 拦截器
- 以上实现存在的问题:
- 在进行删除操作的时候会传入 empId,而 getModel() 方法判断到 empId 不为空,会从 Map 集合中去获取一个 Employee 对象置于栈顶,而对于 delete 操作不需要对象
- 在显示所有员工的时候 getModel() 方法会创建一个空的 Employee 对象置于栈顶,而对于此操作也是没有必要的
- 解决方案 - 使用 PrepareInterceptor 拦截器
- 实现
- Action 类实现 Preparable 接口
- 查看源码
结论
- 阅读源码可以得知若 Action 实现了 Preparable 接口, 则 Struts 将尝试执行 prepare[ActionMethodName] 方法, 若 prepare[ActionMethodName] 不存在, 则将尝试执行 prepareDo[ActionMethodName] 方法.若都不存在, 就都不执行
- 若 PrepareInterceptor 的 alwaysInvokePrepare 属性为 false, 则 Struts2 将不会调用实现了 Preparable 接口的 Action 的 prepare() 方法,即 prepare() 可以不去实现而为每一个 Action 方法准备一个 prepareXxx 或 prepareDoXxx 方法,然后将 alwaysInvokePrepare 属性设为 false,那么每次执行就不会触发 prepare 方法
- 若实现了此接口,那么每个 prepareXxx 方法就会为对应的 Xxx 方法准备一个 Model,利用 getModel() 方法将其置于栈顶,而不需要 empId 去判断,影响程序效率。说白了就是 prepareXxx 方法是为 getModel 方法准备返回对象的
- 最终代码(EmployeeCurd.java)
- 此时的 Action 类的 action 方法就非常的简洁,不会在有其他的冗余问题。
- 在 struts.xml 文件中配置 alwaysInvokePrepare 属性为 false,如下:
至此就是有关 ModelDriven 和 prepare 拦截器的案例,中间存在任何问题和表述不足的还望大神指出,谢谢!