【Struts2】(7)ModelDriven和类型转换器

时间:2021-09-11 17:49:02

一. ModelDriven

我们可以通过对Action实现ModelDriven接口来做到更方便的传值。这样子,我们甚至不用写set和get方法:
public class UserAction implements ModelDriven {

private User user;
private String username;
private String password;

@Override
public Object getModel() {
if (user == null) {
user = new User();
}
return user;
}

// public User getUser() {
// return user;
// }
//
// public void setUser(User user) {
// this.user = user;
// }
这样,我们在jsp中,也可以省去之前所写的user.xxx的user了:
<form action="/web/test/user.action" method="post">
Id:<input name="user.id" /><br />
UserName:<input name="username" /><br />
Password:<input name="password" type="password" /><br />
Sex:<input name="sex" /><br />
Age:<input name="age" /><br />
home add:<input name="address.homeAddress" /><br />
company add:<input name="address.companyAddress" /><br />
<input type="submit" name="method:add"value="添加" />
</form>

二. ModelDriven的一个问题

我们编写这样子的一个list界面:
<table border="1">
<tr align="center">
<td>ID</td>
<td>用户名</td>
<td>密码</td>
<td>操作</td>
</tr>
<tr align="center">
<td>1</td>
<td>张三</td>
<td>1</td>
<td>
<a href="/web/test/user.action?method:updateInput&id=1">更新</a>
<a href="/web/test/user.action?method:delete&id=1">删除</a>
</td>
</tr>
</table>
点击更新会调用updateInput方法从数据库中查询出一个user,然后显示在jsp页面中:
UserName:<input name="username" value='<s:property value="username"/>'/><br />
Password:<input name="password" type="password" value='<s:property value="password"/>'/><br />
Sex:<input name="sex" value='<s:property value="sex"/>'/><br />
Age:<input name="age" value='<s:property value="age"/>'/><br />
public String updateInput() {
UserManager userManager = new UserManager();
user = userManager.findUserById(user.getId());
return "update_input";
}
还是使用我们之前的getModel方法,运行,但是发现页面中并没有显示出我们所需要查询的user来。这个原因是什么呢?
通过断点调试我们发现:【Struts2】(7)ModelDriven和类型转换器【Struts2】(7)ModelDriven和类型转换器
此时处在值栈顶部的user的id是5303,而我们查出的user的id却是5601,这明显不是同一个user,必然不会再界面上显示出来了。
于是我们修改的方法有以下几种:方法一:
public String updateInput() {
Object obj = ActionContext.getContext().getValueStack()
.findValue("#root");
UserManager userManager = new UserManager();
User find = userManager.findUserById(user.getId());
// 清掉原来的数据,放入新查询到的数据。
ActionContext.getContext().getValueStack().getRoot().remove(obj);
ActionContext.getContext().getValueStack().getRoot().push(find);
return "update_input";
}
我们强制调用值栈的remove方法,将之前的obj对象移除掉,然后在栈顶放入我们查询到的user,这样,处在栈顶的数据必然有数据了。
方法二:
我们可以将查询出来的user对象的各种属性赋给Action的成员变量的user的各个属性,然后user就会有数据了
方法三:
public String updateInput() {
Object obj = ActionContext.getContext().getValueStack()
.findValue("#root");
UserManager userManager = new UserManager();
User find = userManager.findUserById(user.getId());
try {
BeanUtils.copyProperties(user, find);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return "update_input";
}
这里需要导入两个包:commons-beanutils和commons-logging包。  
方法四:
在struts配置文件中加入配置:
<interceptor-stack name="myStack">
<interceptor-ref name="exception" />
<interceptor-ref name="alias" />
<interceptor-ref name="servletConfig" />
<interceptor-ref name="i18n" />
<interceptor-ref name="prepare" />
<interceptor-ref name="chain" />
<interceptor-ref name="scopedModelDriven" />
<interceptor-ref name="modelDriven">
<param name="refreshModelBeforeResult">true</param>
</interceptor-ref>
<interceptor-ref name="fileUpload" />
<interceptor-ref name="checkbox" />
<interceptor-ref name="datetime" />
<interceptor-ref name="multiselect" />
<interceptor-ref name="staticParams" />
<interceptor-ref name="actionMappingParams" />
<interceptor-ref name="params" />
<interceptor-ref name="conversionError" />
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging" />
<interceptor-ref name="deprecation" />
<!-- <interceptor-ref name="defaultStack" /> <interceptor-ref name="LoginInterceptor"
/> -->
</interceptor-stack>
最主要的是给modelDriven配置一个refreshModelBeforeResult为true的参数。
updateInput方法还是用最初的代码:
public String updateInput() {
UserManager userManager = new UserManager();
user = userManager.findUserById(user.getId());
return "update_input";
}
当struts配置文件过大时,可以提取出来一个公共的配置文件,然后引用它,新建common.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.action.extension" value="action,,do,webwork"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>


<package name="common" extends="struts-default" abstract="true">
<interceptors>
<interceptor name="LoginInterceptor"
class="com.thr.struts2.interceptor.LoginInterceptor" />
<interceptor-stack name="myStack">
<interceptor-ref name="exception" />
<interceptor-ref name="alias" />
<interceptor-ref name="servletConfig" />
<interceptor-ref name="i18n" />
<interceptor-ref name="prepare" />
<interceptor-ref name="chain" />
<interceptor-ref name="scopedModelDriven" />
<interceptor-ref name="modelDriven">
<param name="refreshModelBeforeResult">true</param>
</interceptor-ref>
<interceptor-ref name="fileUpload" />
<interceptor-ref name="checkbox" />
<interceptor-ref name="datetime" />
<interceptor-ref name="multiselect" />
<interceptor-ref name="staticParams" />
<interceptor-ref name="actionMappingParams" />
<interceptor-ref name="params" />
<interceptor-ref name="conversionError" />
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging" />
<interceptor-ref name="deprecation" />
<!-- <interceptor-ref name="defaultStack" /> <interceptor-ref name="LoginInterceptor"
/> -->
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack" />
</package>
</struts>
我们的struts文件就会清爽许多:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
<include file="common.xml"></include>
<package name="basicstruts2" extends="common"
namespace="/test">
<default-interceptor-ref name="myStack" />
<global-results>
<result name="login">/login.jsp?id=%{#loginid}</result>
</global-results>
<action name="user" class="com.thr.struts2.action.UserAction">
<result name="add_input">/add_input.jsp</result>
<result name="add_success">/add_success.jsp</result>
<result name="update_input">/update_input.jsp</result>
<result>/success.jsp</result>
<result name="list">/list.jsp</result>
<result name="static">/methodcalls.jsp</result>
</action>
<action name="login" class="com.thr.struts2.action.LoginAction">
<!-- <interceptor-ref name="defaultStack" /> -->
<result>/success.jsp</result>
</action>
</package>
</struts>

三. 类型转换器

我们要自定义转换器要继承类,覆写2个方法:
public class DateConverter extends StrutsTypeConverter {
private SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日");

@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
RuntimeException ex = null;
String dateStr = null;
if (values != null && values.length == 1) {
dateStr = values[0];
try {
return format.parse(dateStr);
} catch (ParseException e) {
ex = new RuntimeException(e.getMessage());
}
}
throw ex;
}

@Override
public String convertToString(Map context, Object o) {
if (o instanceof Date) {
return format.format(o);
}
throw new RuntimeException("不是日期格式,请检查");
}
}
1. 局部转换器
在需要转换的类的包下创建名为:类名-conversion.properties的文件,里面写入我们需要转换的属性:
birthday=com.thr.struts2.converter.DateConverter
2. 全局转换器在src下创建名为:xwork-conversion.properties的文件,里面写我们要转换的类:
java.util.Date=com.thr.struts2.converter.DateConverter
当两种转换器都定义的时候,优先调用局部转换器。