本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用
内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系。
本人互联网技术爱好者,互联网技术发烧友
微博:伊直都在0221
QQ:951226918
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
使用 paramsPrepareParamsStack 拦截器的运行流程
1)paramsPrepareParamsStack 和 defaultSstack 一样都是拦截器栈,而struts-defalt包默认使用的是defaultStack
2)可以通过在struts.xml 中配置默认的拦截器栈
<!-- 配置使用paramsPrepareParamsStack 作为默认的拦截器栈 -->
<default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>
3)paramsPrepareParamsStack 拦截器在的运行过程: params -> modelDriven -> params
可以先把请求参数赋给 Action 对应的属性,再根据赋给Action 的属性值 决定压入值栈栈顶的对象,最后再为栈顶对象的属性赋值
4)使用 paramsPrepareParamsStack 拦截器栈:
Struts 2.0的设计上要求 modelDriven 在 params 之前调用,而业务中prepare要负责准备model,准备model又需要参数,
这就需要在 prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。
1.修改默认的拦截器在struts.xml 中
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 6 <struts> 7 8 <constant name="struts.action.extension" value="action,do,"></constant> 9 <package name="default" namespace="/" extends="struts-default"> 10 11 <!-- 配置使用paramsPrepareParamsStack 作为默认的拦截器栈 --> 12 <default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref> 13 <action name="emp-*" class="com.jason.strut2.curd.EmployeeAction" method="{1}"> 14 <result name="{1}">/emp-{1}.jsp</result> 15 <result name="success" type="redirectAction">emp-list</result> 16 </action> 17 18 19 </package> 20 </struts>
2.重构 acntion代码
对于edit 操作而言
1)先为 EmployeeAction 的 employeeId 赋值
2)再根据employeeId 从数据库中加载对应的对象,并且压入到值栈的栈顶
3)再为栈顶对象的employeeId 赋值
4)把栈顶对象的属性回显在表单中
关于回显:
struts2 表单标签会从值栈中获取对应的属性值进行回显
存在问题:
public EmployeeBean getModel() {
if(employeeId == null){
employeeBean = new EmployeeBean();
} else {
employeeBean = dao.get(employeeId);
}
return employeeBean;
}
1)在执行删除的时候,employeeId 不为null,但getModel 方法却从数据库中加载了一个对象,多余
2)在查询全部的时候,new Empployee() 对象,多余
解决问题方案:
使用 PrepareIntercepter 和 Preparable 接口
1 package com.jason.strut2.curd; 2 3 import java.util.Map; 4 5 import org.apache.struts2.interceptor.RequestAware; 6 7 import com.opensymphony.xwork2.ActionContext; 8 import com.opensymphony.xwork2.ActionInvocation; 9 import com.opensymphony.xwork2.ModelDriven; 10 import com.opensymphony.xwork2.util.ValueStack; 11 12 public class EmployeeAction implements RequestAware ,ModelDriven<EmployeeBean>{ 13 14 private Dao dao = new Dao(); 15 private Map<String, Object> requestMap; 16 private EmployeeBean employeeBean; 17 18 // 需要在当前的 EmployeeAction 中定义employeeId 属性,以接收页面请求参数:删除操作 19 20 21 public String list() { 22 23 requestMap.put("emps", dao.getEmployees()); 24 return "list"; 25 } 26 27 //删除 28 public String delete() { 29 30 dao.delete(employeeId); 31 // 返回结果的类型应为 :redirectAction,也可以是chain:实际chain 是没有必要 32 // 还有,若使用chain 则到达目标页面后,地址栏显示的依然是 删除 的那个连接,刷新时 会有重复提交 33 return "success"; 34 } 35 36 37 public String save(){ 38 39 40 41 //1.获取请求参数:通过定义属性的方式 42 //2.调用DAO的 svae 方法 43 44 dao.save(employeeBean); 45 //3.通过redirectAction 的方式响应结果给 emp-list 46 return "success"; 47 } 48 49 50 public String update(){ 51 dao.update(employeeBean); 52 return "success"; 53 } 54 55 public String edit(){ 56 57 //1.获取传入的employeeId:employee.getEmployeeId() 58 //2.根据employeesId 获取employee对象 59 //EmployeeBean empl = dao.get(employeeBean.getEmployeeId()); 60 61 //3.把栈顶的对象的属性装配好:此时的栈顶对象是employee 62 //目前的employee 对象只有 employeeId 属性,其他属性为 null 63 64 /* 65 *Struts2表单回显:从值栈栈顶开始查找匹配的属性,若找到就添加到value 属性中。 66 */ 67 /*employeeBean.setEmail(empl.getEmail()); 68 employeeBean.setFirstNmame(empl.getFirstName()); 69 employeeBean.setLastName(empl.getLastName());*/ 70 71 //不能进行表单的回显,因为经过重写赋值的employee 对象已经不再是栈顶对象 72 // employeeBean = dao.get(employeeBean.getEmployeeId()); 73 74 //手动的把从该数据库中获取的Employee 对象放到值栈的栈顶 75 //但是此时值栈栈顶及第二个对象均为employee 对象 76 //ActionContext.getContext().getValueStack().push(dao.get(employeeBean.getEmployeeId())); 77 78 79 return "edit"; 80 } 81 82 @Override 83 public void setRequest(Map<String, Object> requestMap) { 84 this.requestMap = requestMap; 85 } 86 87 private Integer employeeId; 88 public void setEmployeeId(Integer employeeId) { 89 this.employeeId = employeeId; 90 } 91 92 @Override 93 public EmployeeBean getModel() { 94 //判断Create 还是 edit 95 //若为Create,则:employeeBean = new EmployeeBean(); 96 //若为edit ,则从数据库中获取 employeeBean = dao.get(employeeBean.getEmployeeId()); 97 //判断标准为:是否有employeeId。若有则视为 edit,若没有则视为 Create 98 //若通过employeeId 来判断,则需要在modelDriven 拦截器之前先执行一个params拦截器 99 //可以通过使用paramsPrepareParams 拦截器实现 100 //需要在 struts.xml 文件中配置 paramsPrepareParams 为默认的拦截器栈 101 102 if(employeeId == null){ 103 employeeBean = new EmployeeBean(); 104 } else { 105 employeeBean = dao.get(employeeId); 106 } 107 return employeeBean; 108 } 109 110 }
3.其他 代码
EmployeeBean Dao
web.xml emp-list.jsp emp-edit.jsp inde.jsp 参考
[原创]java WEB学习笔记65:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) ModelDriven拦截器 paramter 拦截器
4. Preparable 拦截器
1)Struts 2.0 中的 modelDriven 拦截器负责把 Action 类以外的一个对象压入到值栈栈顶
2)而 prepare 拦截器负责准备为 getModel() 方法准备 model
3)关于PrepareIntercepter 源码结论
① 若Action 实现了Preparable 接口,则 Struts 将尝试执行prepare[ActionMethodName]方法;
若prepare[ActionMethodName] 不存在,则将尝试执行prepareDo[ActionMethodName] 方法;
若都不存在,就都不执行;
② 若PrepareIntercepter 的 alwaysInvokePrepare 属性为false,则struts2 将不会调用实现了Preparable 接口的 Action 的prepare 方法
4)PrepareInterceptor 拦截器用方法
①若 Action 实现 Preparable 接口,则 Action 方法需实现 prepare() 方法
② PrepareInterceptor 拦截器将调用 prepare() 方法 prepareActionMethodName()方法 或 prepareDoActionMethodName ()方法
③PrepareInterceptor 拦截器根据 firstCallPrepareDo 属性决定获取 prepareActionMethodName 、prepareDoActionMethodName的顺序。默认情况下先获取 prepareActionMethodName (), 如果没有该方法,就寻找prepareDoActionMethodName()。如果找到对应的方法就调用 该方法
④PrepareInterceptor 拦截器会根据 alwaysInvokePrepare 属性决定是否执行prepare()方法
5)源码分析
1 @Override 2 public String doIntercept(ActionInvocation invocation) throws Exception { 3 Object action = invocation.getAction(); 4 5 if (action instanceof Preparable) { 6 try { 7 String[] prefixes; 8 //根据当前拦截器的 firstCallPrepareDo 确定 前缀数组;firstCallPrepareDo 默认为false 9 if (firstCallPrepareDo) { 10 prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX}; 11 } else { 12 prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX}; 13 } 14 //若为false: 则prefixex 为 prepare ,prepareDo 15 //调用前缀方法 16 PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); 17 } 18 catch (InvocationTargetException e) { 19 20 Throwable cause = e.getCause(); 21 if (cause instanceof Exception) { 22 throw (Exception) cause; 23 } else if(cause instanceof Error) { 24 throw (Error) cause; 25 } else { 26 27 throw e; 28 } 29 } 30 31 //根据当前拦截器的 alwaysInvokePrepare(默认是true) 决定是否调用 Action 的 prepare 方法 32 if (alwaysInvokePrepare) { 33 ((Preparable) action).prepare(); 34 } 35 } 36 37 return invocation.invoke(); 38 } 39 40 41 42 public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException { 43 //过去Action 实例 44 Object action = actionInvocation.getAction(); 45 //获取要调用的Action 方法的名字(update) 46 String methodName = actionInvocation.getProxy().getMethod(); 47 48 if (methodName == null) { 49 // if null returns (possible according to the docs), use the default execute 50 methodName = DEFAULT_INVOCATION_METHODNAME; 51 } 52 //获取前缀方法 53 Method method = getPrefixedMethod(prefixes, methodName, action); 54 if (method != null) { 55 //若方法不为 null,通过反射调用前缀方法 56 method.invoke(action, new Object[0]); 57 } 58 } 59 60 public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) { 61 assert(prefixes != null); 62 //把方法的名字变为大写 63 String capitalizedMethodName = capitalizeMethodName(methodName); 64 //遍历前缀数组 65 for (String prefixe : prefixes) { 66 //通过拼接的方式,得到前缀方法名:第一次 prepare + capitalizedMethodName ;第二次 prepareDo +c apitalizedMethodName 67 String prefixedMethodName = prefixe + capitalizedMethodName; 68 try { 69 //利用反射从aciton 中获取对应的方法,若有,直接返回并结束循环 70 return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY); 71 } 72 catch (NoSuchMethodException e) { 73 // hmm -- OK, try next prefix 74 if (LOG.isDebugEnabled()) { 75 LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString()); 76 } 77 } 78 } 79 return null; 80 }
6)解决上述上述问题的方法:
方案:为每一个ActionMethod 准备一个prepare[ActionMethodName] 方法,而抛弃原来的prepare()方法;将PrepareIntercepter 的 alwaysInvokePrepare 属性设为false,避免Struts2 框架在嗲用prepare()方法
1 package com.jason.strut2.curd; 2 3 import java.util.Map; 4 5 import org.apache.struts2.interceptor.RequestAware; 6 7 import com.opensymphony.xwork2.ActionContext; 8 import com.opensymphony.xwork2.ActionInvocation; 9 import com.opensymphony.xwork2.ModelDriven; 10 import com.opensymphony.xwork2.Preparable; 11 import com.opensymphony.xwork2.util.ValueStack; 12 13 public class EmployeeAction implements RequestAware, ModelDriven<EmployeeBean>, 14 Preparable { 15 16 private Dao dao = new Dao(); 17 private Map<String, Object> requestMap; 18 private EmployeeBean employeeBean; 19 20 // 需要在当前的 EmployeeAction 中定义employeeId 属性,以接收页面请求参数:删除操作 21 22 public String list() { 23 24 requestMap.put("emps", dao.getEmployees()); 25 return "list"; 26 } 27 28 // 删除 29 public String delete() { 30 31 dao.delete(employeeId); 32 // 返回结果的类型应为 :redirectAction,也可以是chain:实际chain 是没有必要 33 // 还有,若使用chain 则到达目标页面后,地址栏显示的依然是 删除 的那个连接,刷新时 会有重复提交 34 return "success"; 35 } 36 37 public String save() { 38 // 1.获取请求参数:通过定义属性的方式 39 // 2.调用DAO的 svae 方法 40 41 dao.save(employeeBean); 42 // 3.通过redirectAction 的方式响应结果给 emp-list 43 return "success"; 44 } 45 46 public void prepareSave() { 47 employeeBean = new EmployeeBean(); 48 } 49 50 public void prepareUpdate() { 51 employeeBean = new EmployeeBean(); 52 } 53 54 public String update() { 55 dao.update(employeeBean); 56 return "success"; 57 } 58 59 public void prepareEidt() { 60 employeeBean = dao.get(employeeId); 61 } 62 63 public String edit() { 64 return "edit"; 65 } 66 67 @Override 68 public void setRequest(Map<String, Object> requestMap) { 69 this.requestMap = requestMap; 70 } 71 72 private Integer employeeId; 73 74 public void setEmployeeId(Integer employeeId) { 75 this.employeeId = employeeId; 76 } 77 78 @Override 79 public EmployeeBean getModel() { 80 81 return employeeBean; 82 } 83 84 /* 85 * prepare 方法的主要作用:为getModel() 方法准备 model 的 86 */ 87 @Override 88 public void prepare() throws Exception { 89 System.out.println("prepare ... "); 90 } 91 92 }