深入分析JavaWeb 45 -- Struts2封装请求参数与类型转换
Posted 官小飞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入分析JavaWeb 45 -- Struts2封装请求参数与类型转换相关的知识,希望对你有一定的参考价值。
作为MVC框架,必须要负责解析HTTP请求参数,并将其封装到Model对象中,Struts2提供了非常强大的类型转换机制用于请求数据 到 model对象的封装。
1、Struts2 提供三种数据封装的方式
- Action 本身作为model对象,通过成员setter封装
- 创建独立model对象,页面通过ognl表达式封装
- 使用ModelDriven接口,对请求数据进行封装
1. 方式一:在动作类中成员变量给予初始值。
在配置文件中struts.xml中
<package name="p1" extends="struts-default">
<action name="action1" class="com.itheima.actions.PersonAction">
<result>/result.jsp</result>
</action>
</package>
编写result.jsp
<body>
${name}
</body>
编写实现类PersonAction,重写excute()方法
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如下:
<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
<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
<action name="saveStudent" class="com.itheima.actions.StudentAction"></action>
编写动作类StudentAction
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
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
<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配置文件
<action name="saveCustomer" class="com.itheima.actions.CustomerAction"/>
编写CustomerAction的动作类
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
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页面
<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
<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
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
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
<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
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()方法。
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 以上是关于深入分析JavaWeb 45 -- Struts2封装请求参数与类型转换的主要内容,如果未能解决你的问题,请参考以下文章
深入分析JavaWeb 45 -- Struts2封装请求参数与类型转换
深入分析JavaWeb Item47 -- Struts2拦截器与文件上传下载