深入分析JavaWeb Item45 -- Struts2封装请求参数与类型转换
2016-01-08 16:17
585 查看
作为MVC框架,必须要负责解析HTTP请求参数,并将其封装到Model对象中,Struts2提供了非常强大的类型转换机制用于请求数据 到 model对象的封装。
创建独立model对象,页面通过ognl表达式封装
使用ModelDriven接口,对请求数据进行封装
编写result.jsp
编写实现类PersonAction,重写excute()方法
运行结果为:刘小晨
这种在类的成员变量时,就赋值的方法,亦可以在配置文件中注入动作类的参数值(静态参数设置),Action 本身作为model对象,通过成员setter封装(一个名字为params的拦截器干的)
重新配置struts.xml如下:
运行结果为:王卫星
实际上是由一个叫做staticParams的拦截器做的,可以查看 struts-default.xml配置文件,如果将staticParams这个拦截器删除,则得不到想要的结果
此外还有动态参数注入,同样用动作类作为model对象。
编写result.jsp
配置文件和动作实现类不变,这里要注意: 表单的字段输入域的name,password, age取值要和动作类的写属性名称一致。
运行结果:表单中name输入的结果。
动态参数的注入也是由一个拦截器来做的:params
编写动作类StudentAction
编写模型类Student.java
编写访问的页面addStudent.jsp
这样上述就将模型(Student)和动作类(StudentAction)分开了,在页面中,我们可以用student.name和student.age来封装请求参数了。
主要原理如下:
框架创建了一个Student的实例,通过调用setStudent(Student s),传递对象
框架再调用StudentAction的getStudent(),方法,得到刚刚创建的对象
紧接着,调用student实例的setName和setAge方法,设置值。
编写CustomerAction的动作类
编写实体类Customer.java
编写addCustomer.jsp页面
这里可以直接写name和city来封装请求参数,而不需要些customer.name和customer.city。
注:模型驱动实际上是由一个拦截器来做的。modelDriven拦截器来做。把getModel方法返回的对象,压入一个叫做ValueStack的栈顶。struts框架就会根据表单的name属性,调用对应栈顶对象的setter方法,从而把请求参数封装进来。可以看源码:
注意这一定要new出来! 否则框架调用CustomerAction的getCustomer()方法,得到返回值为null。
类型转换与Collection配合使用
类型转换与Map配合使用
实例:
编写配置struts.xml
编写动作类CollectionAction
编写实体类Employee
编写collectionDemo.jsp
- boolean 和 Boolean
- char和 Character
- int 和 Integer
- long 和 Long
- float 和 Float
- double 和 Double
- Date 可以接收 yyyy-MM-dd格式字符串,java.util.Date<——–>String(中国:Struts2默认按照yyyy-MM-dd本地格式进行自动转换)
- 数组 可以将多个同名参数,转换到数组中
- 集合 支持将数据保存到 List 或者 Map 集合
总结:在使用Struts2时,基本上不用写任何类型转换器。内置的完全够用。
但是因为用户界面传来的数据都是String:String—->其他类型,当一些需求是显示或者是数据回显:其他类型—–>String时,我们就要做自定义类型转换了。
可以看一下源码
从源码可以看出,继承
java.util.Date———>String MM/dd/yyyy
步骤:
1. 编写一个类直接或间接实现:
com.opensymphony.xwork2.conversion.TypeConverter接口。
也可以选择继承:
com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter
我们选择继承
public Object convertValue(Object value, Class toType)
假如在动作类HelloWorldAction 中有一个时间参数createtime
自定义我们的转换器MyDateConvertor ,覆盖convertFromString()和convertToString()方法。
2.注册类型转换器
局部类型转换器:为某个动作类服务的(特服)
在Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应为HelloWorldAction-conversion.properties 。在properties文件中的内容为:
属性名称=类型转换器的全类名
对于本例而言, HelloWorldAction-conversion.properties文件中的内容为:
createtime= cn.itcast.conversion.DateConverter
全局类型转换器:为所有的动作类服务的
在WEB-INF\classes目录下(在src下)建立一个固定名称的配置文件:xwork-conversion.properties
对于本例而言, xwork-conversion.properties文件中的内容为:
java.util.Date= cn.itcast.conversion.DateConverter
3、出现转换失败时的页面转向和错误消息提示
Struts2提供了一个名为conversionError的拦截器,查看struts-default.xml
如果Struts2的类型转换器执行类型转换时出现错误,该拦截器将负责将对应错误封装成表单域错误(FieldError),并将这些错误信息放入ActionContext中
使用类型转换中的错误处理用户定义Action必须继承ActionSupport
在自定义类型转换器中,异常必须抛出不能捕获,conversionError会处理该异常,然后转入名为input的逻辑视图
在Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 : invalid.fieldvalue.属性名= 错误信息
在input逻辑视图所对应jsp页面中,通过
a、配置一个name=”input”的结果视图,一般指向用户输入数据的那个页面
b、在sp中使用struts2的标签显示字段有关的错误(后面的国际化再讲)
c、配置提示信息为中文的
在动作类所在的包中,建立一个名称为:动作类名.properties的配置文件
1、Struts2 提供三种数据封装的方式
Action 本身作为model对象,通过成员setter封装创建独立model对象,页面通过ognl表达式封装
使用ModelDriven接口,对请求数据进行封装
1. 方式一:在动作类中成员变量给予初始值。
在配置文件中struts.xml中[code]<package name="p1" extends="struts-default"> <action name="action1" class="com.itheima.actions.PersonAction"> <result>/result.jsp</result> </action> </package>
编写result.jsp
[code] <body> ${name} </body>
编写实现类PersonAction,重写excute()方法
[code]package com.itheima.actions; import com.opensymphony.xwork2.ActionSupport; public class PersonAction extends ActionSupport { private String name = "刘小晨"; private String password; private int age; public String getName() { return name; } public void setName(String name) { System.out.println("调用了setName方法"); this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String execute() throws Exception { System.out.println(name+":"+password+":"+age); return super.execute(); } }
运行结果为:刘小晨
这种在类的成员变量时,就赋值的方法,亦可以在配置文件中注入动作类的参数值(静态参数设置),Action 本身作为model对象,通过成员setter封装(一个名字为params的拦截器干的)
重新配置struts.xml如下:
[code] <package name="p1" extends="struts-default"> <action name="action1" class="com.itheima.actions.PersonAction"> <!-- 给动作类的实例注入参数值:相当于调用PersonAction.setName("王卫星") --> <param name="name">王卫星</param> <result>/result.jsp</result> </action> </package>
运行结果为:王卫星
实际上是由一个叫做staticParams的拦截器做的,可以查看 struts-default.xml配置文件,如果将staticParams这个拦截器删除,则得不到想要的结果
此外还有动态参数注入,同样用动作类作为model对象。
编写result.jsp
[code]<body> <form action="${pageContext.request.contextPath}/action1" method="post"> 用户名:<input type="text" name="name"/><br/> 密码:<input type="text" name="password"/><br/> 年龄:<input type="text" name="age"/><br/> <input type="submit" value="保存"/> </form> </body>
配置文件和动作实现类不变,这里要注意: 表单的字段输入域的name,password, age取值要和动作类的写属性名称一致。
运行结果:表单中name输入的结果。
动态参数的注入也是由一个拦截器来做的:params
2、方式二:动作类和模型分开(我们以表单请求参数为例)
写配置文件struts.xml[code]<action name="saveStudent" class="com.itheima.actions.StudentAction"></action>
编写动作类StudentAction
[code]package com.itheima.actions; import com.itheima.domain.Student; import com.opensymphony.xwork2.ActionSupport; public class StudentAction extends ActionSupport { private Student student = new Student(); public Student getStudent() { System.out.println("调用了getStudent方法"); return student; } public void setStudent(Student student) { System.out.println("调用了setStudent方法"); this.student = student; } public String execute() throws Exception { System.out.println(student); return NONE; } }
编写模型类Student.java
[code]package com.itheima.domain; import java.io.Serializable; public class Student implements Serializable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } }
编写访问的页面addStudent.jsp
[code] <body> <form action="${pageContext.request.contextPath}/saveStudent" method="post"> 用户名:<input type="text" name="student.name"/><br/> 年龄:<input type="text" name="student.age"/><br/> <input type="submit" value="保存"/> </form> </body>
这样上述就将模型(Student)和动作类(StudentAction)分开了,在页面中,我们可以用student.name和student.age来封装请求参数了。
主要原理如下:
框架创建了一个Student的实例,通过调用setStudent(Student s),传递对象
框架再调用StudentAction的getStudent(),方法,得到刚刚创建的对象
紧接着,调用student实例的setName和setAge方法,设置值。
3. 方式三:模型驱动(面试)(与后面ValueStack值栈有关)
编写struts.xml配置文件[code]<action name="saveCustomer" class="com.itheima.actions.CustomerAction"/>
编写CustomerAction的动作类
[code]package com.itheima.actions; import com.itheima.domain.Customer; import com.opensymphony.xwork2.ActionSupport; import com.opensymphony.xwork2.ModelDriven; //使用模型驱动: public class CustomerAction extends ActionSupport implements ModelDriven<Customer>{ private Customer customer = new Customer();//这里一定要new()出一个实体类。 public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } @Override public String execute() throws Exception { System.out.println(customer); return NONE; } public Customer getModel() { return customer; } }
编写实体类Customer.java
[code]package com.itheima.domain; import java.io.Serializable; public class Customer implements Serializable { private String name; private String city; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "Customer [name=" + name + ", city=" + city + "]"; } }
编写addCustomer.jsp页面
[code] <body> <form action="${pageContext.request.contextPath}/saveCustomer.action" method="post"> 用户名:<input type="text" name="name"/><br/> 城市:<input type="text" name="city"/><br/> <input type="submit" value="保存"/> </form> </body>
这里可以直接写name和city来封装请求参数,而不需要些customer.name和customer.city。
注:模型驱动实际上是由一个拦截器来做的。modelDriven拦截器来做。把getModel方法返回的对象,压入一个叫做ValueStack的栈顶。struts框架就会根据表单的name属性,调用对应栈顶对象的setter方法,从而把请求参数封装进来。可以看源码:
注意这一定要new出来! 否则框架调用CustomerAction的getCustomer()方法,得到返回值为null。
2、 封装数据到集合类型中
Struts2 还允许填充 Collection 里的对象, 这常见于需要快速录入批量数据的场合类型转换与Collection配合使用
类型转换与Map配合使用
实例:
编写配置struts.xml
[code]<action name="collectionAction1" class="com.itheima.actions.CollectionAction"/> <action name="collectionAction2" class="com.itheima.actions.CollectionAction"/> <action name="collectionAction3" class="com.itheima.actions.CollectionAction"/>
编写动作类CollectionAction
[code]package com.itheima.actions; import java.util.Arrays; import java.util.Collection; import java.util.Map; import com.itheima.domain.Employee; import com.opensymphony.xwork2.ActionSupport; public class CollectionAction extends ActionSupport { private String[] hobby;//数组 private Collection<Employee> employees;//集合 private Map<String, Employee> emps;//map public String[] getHobby() { return hobby; } public void setHobby(String[] hobby) { this.hobby = hobby; } public Collection<Employee> getEmployees() { return employees; } public void setEmployees(Collection<Employee> employees) { this.employees = employees; } public Map<String, Employee> getEmps() { return emps; } public void setEmps(Map<String, Employee> emps) { this.emps = emps; } public String execute() throws Exception { // System.out.println(Arrays.asList(hobby)); // System.out.println(employees); if(emps!=null){ for(Map.Entry<String, Employee> me:emps.entrySet()){ System.out.println(me.getKey()+":"+me.getValue()); } } return NONE; } }
编写实体类Employee
[code]package com.itheima.domain; import java.io.Serializable; public class Employee implements Serializable { private String name; private float salary; public String getName() { return name; } public void setName(String name) { this.name = name; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } @Override public String toString() { return "Employee [name=" + name + ", salary=" + salary + "]"; } }
编写collectionDemo.jsp
[code]<body> <form action="${pageContext.request.contextPath}/collectionAction1" method="post"> 爱好: <input type="checkbox" name="hobby" value="吃饭"/>吃饭 <input type="checkbox" name="hobby" value="睡觉"/>睡觉 <input type="checkbox" name="hobby" value="学java"/>学java <input type="submit" value="保存"/> </form> <hr/> <h2>批量添加员工信息</h2> <form action="${pageContext.request.contextPath}/collectionAction2" method="post"> 员工1:姓名:<input type="text" name="employees[0].name"/>薪水:<input type="text" name="employees[0].salary"/><br/> 员工2:姓名:<input type="text" name="employees[1].name"/>薪水:<input type="text" name="employees[1].salary"/><br/> 员工3:姓名:<input type="text" name="employees[2].name"/>薪水:<input type="text" name="employees[2].salary"/><br/> <input type="submit" value="保存"/> </form> <h2>向Map中添加内容</h2> <form action="${pageContext.request.contextPath}/collectionAction3" method="post"> 员工1:姓名:<input type="text" name="emps['e1'].name"/>薪水:<input type="text" name="emps['e1'].salary"/><br/> 员工2:姓名:<input type="text" name="emps.e2.name"/>薪水:<input type="text" name="emps.e2.salary"/><br/> 员工3:姓名:<input type="text" name="emps.e3.name"/>薪水:<input type="text" name="emps.e3.salary"/><br/> <input type="submit" value="保存"/> </form> </body>
3、类型转换
对于大部分常用类型,开发者根本无需创建自己的转换器,Struts2内置了常见数据类型多种转换器,8大基本类型自动转换。- boolean 和 Boolean
- char和 Character
- int 和 Integer
- long 和 Long
- float 和 Float
- double 和 Double
- Date 可以接收 yyyy-MM-dd格式字符串,java.util.Date<——–>String(中国:Struts2默认按照yyyy-MM-dd本地格式进行自动转换)
- 数组 可以将多个同名参数,转换到数组中
- 集合 支持将数据保存到 List 或者 Map 集合
总结:在使用Struts2时,基本上不用写任何类型转换器。内置的完全够用。
但是因为用户界面传来的数据都是String:String—->其他类型,当一些需求是显示或者是数据回显:其他类型—–>String时,我们就要做自定义类型转换了。
可以看一下源码
从源码可以看出,继承
org.apache.struts2.util.StrutsTypeConverter(最简单和方便)
1、自定义类型转换器
String—————–>java.util.Date MM/dd/yyyy—–>能转换java.util.Date———>String MM/dd/yyyy
步骤:
1. 编写一个类直接或间接实现:
com.opensymphony.xwork2.conversion.TypeConverter接口。
也可以选择继承:
com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter
我们选择继承
org.apache.struts2.util.StrutsTypeConverter,并覆盖掉如下方法
public Object convertValue(Object value, Class toType)
假如在动作类HelloWorldAction 中有一个时间参数createtime
[code]import java.util.Date; public class HelloWorldAction { private Date createtime; public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } }
自定义我们的转换器MyDateConvertor ,覆盖convertFromString()和convertToString()方法。
[code]package com.itheima.convertor; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import org.apache.struts2.util.StrutsTypeConverter; //日期转换器: /* * String :12/31/2001 ---->Date * Date---------->String:12/31/2001 */ public class MyDateConvertor extends StrutsTypeConverter { private DateFormat df = new SimpleDateFormat("MM/dd/yyyy"); //从字符串转换成日期 public Object convertFromString(Map context, String[] values, Class toClass) { if(toClass==Date.class){ String value = values[0];//获取用户输入的参数值12/31/2001 try { return df.parse(value); } catch (ParseException e) { e.printStackTrace(); } } return null; } //日期转换成字符串 public String convertToString(Map context, Object o) { if(o instanceof Date){ Date d = (Date)o; return df.format(d); } return null; } }
2.注册类型转换器
局部类型转换器:为某个动作类服务的(特服)
在Action类所在的包下放置ActionClassName-conversion.properties文件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应为HelloWorldAction-conversion.properties 。在properties文件中的内容为:
属性名称=类型转换器的全类名
对于本例而言, HelloWorldAction-conversion.properties文件中的内容为:
createtime= cn.itcast.conversion.DateConverter
全局类型转换器:为所有的动作类服务的
在WEB-INF\classes目录下(在src下)建立一个固定名称的配置文件:xwork-conversion.properties
对于本例而言, xwork-conversion.properties文件中的内容为:
java.util.Date= cn.itcast.conversion.DateConverter
3、出现转换失败时的页面转向和错误消息提示
Struts2提供了一个名为conversionError的拦截器,查看struts-default.xml
[code]<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
如果Struts2的类型转换器执行类型转换时出现错误,该拦截器将负责将对应错误封装成表单域错误(FieldError),并将这些错误信息放入ActionContext中
使用类型转换中的错误处理用户定义Action必须继承ActionSupport
在自定义类型转换器中,异常必须抛出不能捕获,conversionError会处理该异常,然后转入名为input的逻辑视图
在Action所在包中,创建 ActionName.properties,在局部资源文件中配置提示信息 : invalid.fieldvalue.属性名= 错误信息
在input逻辑视图所对应jsp页面中,通过
<s:fielderror/>输出类型转换信息
a、配置一个name=”input”的结果视图,一般指向用户输入数据的那个页面
b、在sp中使用struts2的标签显示字段有关的错误(后面的国际化再讲)
[code]<%@ taglib uri="/struts-tags" prefix="s"%> <s:fielderror/>
c、配置提示信息为中文的
在动作类所在的包中,建立一个名称为:动作类名.properties的配置文件
相关文章推荐
- 关于Java内存模型的解读
- java.sql.SQLException: 无效的列索引
- MyEclipse 2015优化技巧
- java学习之reflection反射
- Java中的堆和栈(百度)
- JAVA基础教程汇总
- Spring MVC - 拦截器实现 和 用户登陆例子
- Little Alchemy 游戏 - 扒答案。。 Java
- Retrofit 和 RxJava 结合使用
- Java SWT 设计RS232/RS485串口接受的界面,用线程读取和发送
- Eclipse 安装反编译插件
- java线程池在web项目中应用
- Java中的继承
- Java enum的用法详解
- java中在写入mysql数据库时出现的乱码问题
- java.lang.NoClassDefFoundError
- JAVA类
- Java日志框架slf4j、jcl、jul、log4j1、log4j2、logback大总结
- struts表单属性设置
- Java三目算法