四、Struts2的其它知识html
4.一、ModelDrivenjava
若是但愿传递对象信息到action,但对象信息较多时,须要传递大量的参数而且要设置这些参数,工做量
很大,因此建议的方式是在action中直接建立相应的对象,此时在jsp页面中能够经过以下一种方式进行参
数的传递:数组
<form action="Message_add.action" method="post"> <!-- 在传递值的name中,加上action的对象msg的名称 --> ID:<input type="text" name="msg.id"/><br/> Title:<input type="text" name="msg.title"/><br/> Content:<input type="text" name="msg.content"/><br/> <input type="submit"/> </form>
可是使用这样的方式带来的问题是在表单中的名称不能按照咱们开发人员的习惯来定义,此时就须要经过
ModelDriven来实现以下传值:服务器
一、让action实现ModelDriven接口。app
二、覆盖getModel()方法jsp
@Override public Message getModel() { if(msg==null) msg = new Message(); return msg; }
三、当一个类实现了ModelDriven以后,就会将这个model放入到root中,因此咱们的input中不用加任何的
对象信息就能够直接放置到msg对象中。ide
特别注意:使用ModelDriven存在的问题:更新时获取的是root中的新对象,全部的值都为空。函数
能够经过以下方式解决:post
public class MessageDao { public Message load() { Message msg = new Message(); msg.setId(12); msg.setTitle("办证"); msg.setContent("专业办证二十年"); return msg; }
public String updateInput() { try { //展现页面须要经过value='<s:property value="msg.id"/>'来获取值,若是但愿直接经过id来获取值, //能够经过如下两种方式来实现 MessageDao md = new MessageDao(); Message tmsg = md.load(); //第一种方式:将root中的对象经过取到的值从新赋值 /*msg.setId(tmsg.getId()); msg.setTitle(tmsg.getTitle()); msg.setContent(tmsg.getContent());*/ //第二种方式:经过BeanUtil实现赋值 BeanUtils.copyProperties(msg, tmsg); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return "success"; }
<s:debug/> <form action="Message_add.action" method="post"> <!-- action中从新赋值后就可直接经过id来获取值(value='<s:property value="id"/>') --> ID:<input type="text" name="id" value='<s:property value="id"/>'/><br/> <!-- 若是没有在action中赋值,则需经过action中的对象msg来获取值(msg.title) --> Title:<input type="text" name="title" value='<s:property value="msg.title"/>'/><br/> Content:<input type="text" name="content" value='<s:property value="msg.content"/>'/><br/> <input type="submit"/> </form>
4.二、类型转换ui
当使用ModelDriven来实现参数接收时,可能因为类型的不一致(特别是一些复杂类型)致使接收参数报
错,此时就须要使用类型转换器。
类型转换器有两种模式:一种全局模式(全部的action均有做用),另一种是局部模式(针对某个特定
的action起做用)。
全局模式实现步骤:
一、写一个类继承StrutsTypeConverter
/** * 要为某个对象增长相应的类型转换器,首先得建立一个类继承StrutsTypeConverter * @author PM * */ public class PointConverter extends StrutsTypeConverter { /** * 完成字符串到对象的转换 */ @Override public Object convertFromString(Map context, String[] values, Class toClass) { Point p = null; if(values.length<=1) { String str = values[0]; //12,88 p = new Point(); String[] cods = str.split(","); p.setX(Integer.parseInt(cods[0])); p.setY(Integer.parseInt(cods[1])); } return p; } /** * 完成对象到字符串的转换 */ @Override public String convertToString(Map context, Object o) { Point p = (Point)o; return p.getX()+","+p.getY(); }
二、在类路径建立xwork-conversion.properties文件,在这个文件中说明要转换的对象
org.struts.model.Point = org.struts.converter.PointConverter
org.struts.model.Point:表示要转换的对象
org.struts.converter.PointConverter:表示使用哪个转换器来转换。
以上的全局转换器是针对全部的须要转换的类来指定,可是有时候可能会根据不一样的action来进行转换,
这个时候就会使用到局部转换器。
一、建立一个类继承StrutsTypeConverter
public class DateConverter01 extends StrutsTypeConverter { //定义了日期的格式 private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); @Override public Object convertFromString(Map context, String[] values, Class toClass) { try { if(values.length<=1) { String d = values[0]; return sdf.parse(d); } } catch (ParseException e) { e.printStackTrace(); } return null; } @Override public String convertToString(Map context, Object o) { return sdf.format((Date)o); }
public class DateConverter02 extends StrutsTypeConverter { //定义了日期的格式 private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); @Override public Object convertFromString(Map context, String[] values, Class toClass) { try { if(values.length<=1) { String d = values[0]; return sdf.parse(d); } } catch (ParseException e) { e.printStackTrace(); } return null; } @Override public String convertToString(Map context, Object o) { return sdf.format((Date)o); }
二、在action所在的包中建立ActionName-conversion.properties(如:MessageAction-
conversion.properties)
msg.createDate = org.struts.converter.DateConverter01 endDate = org.struts.converter.DateConverter02
msg.createDate、endDate:表示action中的要转换的对象的名称,此处等于要转换该action中的msg的
createDate和action中的endDate两个属性。
在action所在的包中建立ActionName-conversion.properties来设置转换器。
4.三、文件上传
Struts2对文件上传提供了天生的支持,只要设置一个表单域为file而且将form的类型提供为
multipart/form-data,Struts2会自动完成普通表单和文件表单的识别。
一、定义一个文件提供相应的上传form
<s:debug/> <form action="Message_file.action" method="post" enctype="multipart/form-data"> <!-- 有一组file只要在Action中设定一个数组来接收, 若是只有一个file,就设定一个对象来接收 --> Title:<input type="text" name="title"/><br/> File:<input type="file" name="photo"/><br/> File:<input type="file" name="photo"/><br/> File:<input type="file" name="photo"/><br/> File:<input type="file" name="photo"/><br/> File:<input type="file" name="photo"/><br/> <input type="submit"/> </form>
二、在Action中定义相应的属性来接收file对象,若是是一组属性定义一个数组,若是是一个属性定义一个
对象。属性的名称必须是表单中的name名称。
//photo名称必须和表单中的名称同样,可是这个file中没有相应的文件名 private File[] photo; //文件名须要经过定义其它的参数来接收,关键是须要两个set方法, //一个用来设置文件名称 private String[] photoFileName; //一个用来设置文件类型 private String[] photoContentType; /** * 会把上传的文件名经过该方法获取 * @return */ public String[] getPhotoFileName() { return photoFileName; } /** * 上传的文件名会经过该方法设置 * 用来接收文件名的方法,若是表单的名称是photo,这个名称必须是setPhotoFileName() * @param photoFileName */ public void setPhotoFileName(String[] photoFileName) { this.photoFileName = photoFileName; } public String[] getPhotoContentType() { return photoContentType; } /** * 设置文件类型,不管是文件名仍是文件类型,所使用的格式是固定的 * xxxFileName,xxxContentType * 用来接收上传文件的类型的,若是名称是photo,这个函数的名称必须是setPhotoContentType() * @param photoContentType */ public void setPhotoContentType(String[] photoContentType) { this.photoContentType = photoContentType; } public File[] getPhoto() { return photo; } public void setPhoto(File[] photo) { this.photo = photo; }
特别注意:Struts2默认上传文件的大小是有限制的(default.properties),能够经过struts.xml来配置修改
<!-- 设置上传文件的大小(字节数) --> <constant name="struts.multipart.maxSize" value="10240000"/>
4.四、拦截器
拦截器是Struts2中很是重要的一种控制手段,在开发中可使用拦截器来拦截用户的请求,而且进行相应
的权限控制。
一、建立拦截器:写一个类继承AbstractInterceptor
public class HelloInterceptor extends AbstractInterceptor { @Override public String intercept(ActionInvocation invocation) throws Exception { System.out.println("heelo interceptor"); //拦截请求完成以后,须要继续向下走,调用invocation.invoke() return invocation.invoke(); }
二、配置拦截器
2.一、先建立拦截器
<package name="default" namespace="/" extends="struts-default"> <interceptors> <!-- 建立了一个拦截器的配置,可是此时配置并无生效,须要在action中配置以后才能生效 --> <interceptor name="helloInterceptor" class="org.struts.interceptor.HelloInterceptor"/> </interceptors>
2.二、在action中使用拦截器
<action name="*_*" class="org.struts.action.{1}Action" method="{2}"> <!-- 在action中引入相应的拦截器 --> <interceptor-ref name="helloInterceptor"/>
问题:当时用了这个拦截器以后,出现了一个问题,原来应该传的值都变为null了?
是由于当咱们设定了拦截器以后,默认拦截器就不起做用了(默认拦截器是在struts-default.xml中设定
的),因此在建立新的拦截器时必须能够拥有原有的默认拦截器,可使用拦截器栈来处理。
<package name="default" namespace="/" extends="struts-default"> <interceptors> <!-- 建立了一个拦截器的配置,可是此时配置并无生效,须要在action中配置以后才能生效 --> <interceptor name="helloInterceptor" class="org.struts.interceptor.HelloInterceptor"/> <!-- 定义了一个拦截器栈,包含有默认的拦截器和本身定义的拦截器 --> <interceptor-stack name="helloStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="helloInterceptor"/> </interceptor-stack> </interceptors>
<action name="*_*" class="org.struts.action.{1}Action" method="{2}"> <!-- 引用时就引用本身的拦截器栈 --> <interceptor-ref name="helloStack"/>
4.五、国际化(I18N)
一、国际化的概念:
ResourceBundle能够支持国际化,经过配置文件(.properties)完成国际化的支持的。
二、建立配置文件的要求:
Message_zh_CN.properties(Message表示基础名称,zh表示中文,_CN表示的是国家的编码,能够
省略)。
Message_en_UK.properties(针对美国英语的配置文件,一样也能够省略_UK)。
三、使用java.util中ResourceBundle能够实现
public class TestI18n { public static void main(String[] args) { //建立ResourceBundle对象,而且说明区域名称(CHINESE或者ENGLISH等) ResourceBundle rb = ResourceBundle.getBundle("Message", Locale.CHINESE); //根据key来获取值,此时就会根据ResourceBundle建立时的区域名称来相应的properties文件中取数据 System.out.println(rb.getObject("hello")+","+rb.getObject("world")); }
四、Struts2的实现:局部的、全局的
局部的通常不使用。
全局的有两种方式:
4.一、针对package的全局设定
在Action的所在包的任何路径下建立package_zh.properties和package_en.properties。
在这两个文件中加入相应的key和value。
message.id=标识 message.title=标题 message.content=内容
message.id=ID message.title=Title message.content=Content
让须要进行国际化的Action实现ActionSupport类以后在页面中使用<s:text name=""/>完成调用。
<s:text name="message.id"/>:<input type="text" name="id"/><br/> <s:text name="message.title"/>:<input type="text" name="title"/><br/> <s:text name="message.content"/>:<input type="text" name="content"/><br/> <input type="submit"/>
4.二、全局国际化
在类路径下建立相应的资源文件。
在struts.xml配置中加入全局资源文件的名称.
<!-- 设置国际化,value="Message"指定国际化文件的baseName(基本名称) --> <constant name="struts.custom.i18n.resources" value="Message"/>
4.六、Struts2的经常使用标签
Struts2提供了一组很是好用的Form来帮助咱们完成开发。
一、Struts2的theme:Struts2提供一组主题帮助开发者来实现界面操做。默认主题是xhtml。
修改主题能够在struts.xml配置文件中设置。
<!-- 设置显示主题样式 --> <!-- <constant name="struts.ui.theme" value="simple"/> -->
二、经常使用标签
public class GroupAction implements ModelDriven<Group> { private Group group; public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } public String addInput() { group.setId(1); group.setName("财务部"); List<String> interest = new ArrayList<String>(); // interest.add("football"); interest.add("pingpong"); List<Group> groups = new ArrayList<Group>(); groups.add(new Group(1,"财务部")); groups.add(new Group(2,"文章审核人员")); groups.add(new Group(3,"超级管理员")); groups.add(new Group(4,"文章发布人员")); ActionContext.getContext().put("username", "张三"); ActionContext.getContext().put("groups", groups); ActionContext.getContext().put("interest", interest); return "success"; } @Override public Group getModel() { if(group==null) group = new Group(); return group; }
<s:debug/> <s:form action="Group_add" method="post"> <!-- s:textfield会显示文本输入框,而且会自动将root中的值根据name设置进来 --> <s:textfield label="组标识" name="id"/> <s:textfield label="组名称" name="name"/> <!-- 在Struts2中要在Struts2的标签中引入相应的ActionContext中的值,要使用%{xxx} --> <s:textfield label="用户名" name="username1" value="%{username}"/> <!-- 对于列表而言,在新版本中已经可使用#xxx来访问,可是依然建议使用%{}来访问 --> <s:checkboxlist label="兴趣" name="interest" list="#{'football':'足球','basketball':'篮球','pingpong':'乒乓球' }" listKey="key" listValue="value" value="%{interest}"/> <!-- 建立了一组单选框 --> <s:radio list="#{'0':'男','1':'女' }" value="0" name="gender" label="性别"/> <!-- 建立了一个下拉列表框 --> <s:select list="#groups" listKey="id" listValue="name" label="选择组" headerKey="-1" headerValue="请选择相应的组" value="2"/> <s:submit value="添加"/> </s:form>
4.七、Struts2提供了大量的服务器端验证方法(XML、Annotation等)。
/** * 在执行某个方法以前若是有validateXX,都会先执行这个方法 */ public void validateAdd() { if(msg.getId()<0) { this.addFieldError("id", "标识必须大于0"); } if(msg.getTitle()==null||"".equals(msg.getTitle())) { this.addFieldError("title", "标题不能为空"); } }
<!-- 错误信息在一处显示 --> <s:fielderror/> <!-- 显示错误信息必须使用Struts的标签 --> <s:form action="Message_add" method="post"> <s:textfield key="message.id" name="id"/> <s:textfield key="message.title" name="title"/> <s:textfield key="message.content" name="content"/> <s:submit/> </s:form> <!-- 只在一处显示id的错误信息 --> <s:fielderror fieldName="id"/>
4.八、Struts2异常处理
<global-results> <result name="error">/WEB-INF/inc/error.jsp</result> <!-- 异常信息页面 --> <result name="exception">/WEB-INF/inc/exception.jsp</result> <result name="loginInput">/WEB-INF/Login/input.jsp</result> </global-results> <!-- 定义异常处理 --> <global-exception-mappings> <exception-mapping result="exception" exception="org.struts.exception.MyException"/> </global-exception-mappings>
<h1 style="color: #f00">发生错误</h1> <!-- 获取异常信息 --> ${exception.getMessage() }