为了解决上节的问题
I. 在执行删除的时候, employeeId 不为 null, 但 getModel 方法却从数据库加载了一个对象. 不该加载!
II. 指向查询全部信息时, 也 new Employee() 对象. 浪费!
使用PrepareInterceptor拦截器
可以为每一个 ActionMethod 准备 prepare[ActionMethdName] 方法, 而抛弃掉原来的 prepare() 方法
将 PrepareInterceptor 的 alwaysInvokePrepare 属性置为 false, 以避免 Struts2 框架再调用 prepare() 方法.
在struts.xml中设定不执行接口prepa
<param name="prepare.alwaysInvokePrepare">false</param>
EmployeeAction类
public class EmployeeAction implements RequestAware, ModelDriven<Employee>, Preparable{
private Dao dao = new Dao();
/**
* 使用ModelDriven而不在Aciton中声明employee各个对象
*/
private Employee employee;
public String update(){
dao.update(employee);
return "success";
}
public void prepareUpdate(){
employee = new Employee();
}
public String edit(){
return "edit";
}
public void prepareEdit(){
employee = dao.get(employeeId);
}
public String save(){
dao.save(employee);
return "success";
}
/**
* 实现Preparable接口后,可以设定前缀方法prepare+methodName,来在getModel之前准备
*/
public void prepareSave(){
employee = new Employee();
}
public String delete(){
dao.delete(employeeId);
return "success";
}
public String list(){
request.put("emps", dao.getEmployees());
return "list";
}
private Map<String, Object> request;
@Override
public void setRequest(Map<String, Object> arg0) {
this.request = arg0;
}
//需要在当前EmployeeAction定义id属性,以接收页面请求参数
private Integer employeeId;
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
/**
* 在新建和修改时会用到
*/
@Override
public Employee getModel() {
//若create 就new一个
//若修改,则从数据库取
//判断标准为是否有EmployeeID参数
//所以在进入DrivelModel拦截器前要先执行params拦截器栈
//通过paramsPrepaeParams拦截器栈可以实现
return employee;
}
@Override
public void prepare() throws Exception {
System.out.println("prepare...");
}
}
struts2源代码解析
----------------------------------源代码解析---------------------------------
public String doIntercept(ActionInvocation invocation) throws Exception {
//获取 Action 实例
Object action = invocation.getAction();
//判断 Action 是否实现了 Preparable 接口
if (action instanceof Preparable) {
try {
String[] prefixes;
//根据当前拦截器的 firstCallPrepareDo(默认为 false) 属性确定 prefixes
if (firstCallPrepareDo) {
prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};
} else {
prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};
}
//若为 false, 则 prefixes: prepare, prepareDo
//调用前缀方法.
PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
}
catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof Exception) {
throw (Exception) cause;
} else if(cause instanceof Error) {
throw (Error) cause;
} else {
throw e;
}
}
//根据当前拦截器的 alwaysInvokePrepare(默认是 true) 决定是否调用 Action 的 prepare 方法
if (alwaysInvokePrepare) {
((Preparable) action).prepare();
}
}
return invocation.invoke();
}
PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes) 方法:
public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {
//获取 Action 实例
Object action = actionInvocation.getAction();
//获取要调用的 Action 方法的名字(update)
String methodName = actionInvocation.getProxy().getMethod();
if (methodName == null) {
// if null returns (possible according to the docs), use the default execute
methodName = DEFAULT_INVOCATION_METHODNAME;
}
//获取前缀方法
Method method = getPrefixedMethod(prefixes, methodName, action);
//若方法不为 null, 则通过反射调用前缀方法
if (method != null) {
method.invoke(action, new Object[0]);
}
}
PrefixMethodInvocationUtil.getPrefixedMethod 方法:
public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {
assert(prefixes != null);
//把方法的首字母变为大写
String capitalizedMethodName = capitalizeMethodName(methodName);
//遍历前缀数组
for (String prefixe : prefixes) {
//通过拼接的方式, 得到前缀方法名: 第一次 prepareUpdate, 第二次 prepareDoUpdate
String prefixedMethodName = prefixe + capitalizedMethodName;
try {
//利用反射获从 action 中获取对应的方法, 若有直接返回. 并结束循环.
return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);
}
catch (NoSuchMethodException e) {
// hmm -- OK, try next prefix
if (LOG.isDebugEnabled()) {
LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString());
}
}
}
return null;
}