struts2 中的参数封装
静态参数封装
什么是静态参数?
静态参数就是硬编码的,不可随意改变。
例子:
我们首先创建一个Action类,里面有两个参数,用来封装请求参数
public class User extends ActionSupport {
private String username; //用户名
private int age; //年龄
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String adduser(){
System.out.println(username+":"+age);
return null;
}
}
我们在 struts.xml 中配置静态参数
<package name="p1" extends="struts-default">
<action name="action1" class="com.cad.struts2.User" method="adduser">
<!--通过注入的方式封装静态参数-->
<param name="username">张三</param>
<param name="age">18</param>
</action>
</package>
我们访问action1动作时,会输出 张三:18
我们配置的静态参数怎么就被传递到动作类里呢?
我们在struts2的运行流程中提到过,我们动作类的动作方法执行之前,会有一系列的拦截器帮我们执行一些操作。
在struts-default.xml中,有很多拦截器,拦截器又分为很多拦截器栈,我们默认是使用defaultStack的拦截器栈。
这个拦截器栈中有一个叫做staticParams的拦截器帮我们完成静态参数的封装,将配置的静态方法通过action类中的set方法设置进去。
动态参数封装
什么是动态参数?
像我们用表单提交的数据,就是动态数据。数据是可变的。
例子:
第一步:我们先创建一个添加用户的表单
<form action="${pageContext.request.contextPath }/action1" method="post">
用户名:<input type="text" name="username"><br>
年 龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
第一种方式:数据参数和动作类写在一起
要求是表单中的name必须和我们类中的set方法后面的名称一致,例如表单中的username对应类中的setUsername,和参数并没有太大关系,和set方法有关系。
public class User extends ActionSupport {
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String adduser(){
System.out.println(username+":"+age);
return null;
}
}
配置struts.xml文件
<package name="p1" extends="struts-default">
<action name="action1" class="com.cad.struts2.User" method="adduser"></action>
/package>
我们访问添加用户页面,添加数据,会输出我们添加的数据。
这一系列操作是由拦截器栈中的名为 params 的拦截器帮我们完成的
上面这种方式将参数和动作类写在一起,看起来太过混乱,我们能不能用一个JavaBean将参数给封装起来。 答案是能。
第二种方式,将参数数据和动作类分开写
第一步,我们创建一个javaBean,User类,用来封装请求参数
public class User implements Serializable{
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
第二步:创建一个添加用户的动作类,里面有一个user对象
public class AdduserAction extends ActionSupport {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String adduser(){
System.out.println(user.getUsername()+":"+user.getAge());
return null;
}
}
第三步:我们得改变一些我们的jsp页面的写法,因为表单的name要和类中的数据相对应,
但类中只有一个user ,username去找动作类中的setUsername,肯定找不到。
我们这样写,他就会先找类中的user对象,然后去找user对象的username和age
<body>
<form action="${pageContext.request.contextPath }/action1" method="post">
用户名:<input type="text" name="user.username"><br>
年 龄:<input type="text" name="user.age"><br>
<input type="submit" value="提交">
</form>
</body>
原理:
这一系列操作还是拦截器params帮我们完成。我们在getUser和setUser里打印两句话来看看他到底是怎么执行的
public User getUser() {
System.out.println("getuser");
return user;
}
public void setUser(User user) {
System.out.println("setuser");
this.user = user;
}
输出
getuser :先判断对象是否存在
setuser :如果判断不存在,调用set方法,通过反射创建一个对象,并且设置给该类
getuser :然后再获取该对象,调用该对象的get和set方法对参数赋值
模型驱动和属性驱动
出于结构清晰的考虑,应该采用单独的Model实例来封装请求参数和处理结果,这就是所谓的模型驱动,
所谓模型驱动,就是使用单独的JavaBean来贯穿整个MVC流程。
所谓属性驱动,就是使用属性来作为贯穿MVC流程的信息携带者,当然属性必须依附于对象,这个对象就是Action实例。
简单说,模型驱动就是使用单独的javaBean封装请求参数。
属性驱动就是把属性写在Action类中。
我们发现上面的 jsp 中的 name 必须前面得加 user.username。。太过麻烦。我们使用模型驱动来解决这个问题。实际开发中使用这种方式
模型驱动的要求:
1.动作类实现ModelDriven接口
2.实现接口中的getModel方法,返回我们的数据对象
3.数据模型对象必须由我们实例化。
我们改进动作类
public class AdduserAction extends ActionSupport implements ModelDriven<User>{
//数据模型对象由我们实例化
private User user=new User();
public User getUser() {
System.out.println("getuser");
return user;
}
public void setUser(User user) {
System.out.println("setuser");
this.user = user;
}
public String adduser(){
System.out.println(user.getUsername()+":"+user.getAge());
return null;
}
//实现接口方法,返回我们的数据模型对象
public User getModel() {
// TODO Auto-generated method stub
return user;
}
}
我们在jsp上就能像以前那样 ,name只用我们的参数相同即可
<body>
<form action="${pageContext.request.contextPath }/action1" method="post">
用户名:<input type="text" name="username"><br>
年 龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
</body>
原理:
其实这时候表单的name已经不仅仅是一个简单的字符串了。
这一系列的操作是由ModelDriven和params拦截器帮我们做的 。
params拦截器负责提取请求参数,如果是属性驱动模式,则还负责将请求参数传给Action类的属性
模型驱动的话就只提取请求参数。
ModelDriven拦截器会先判断我们的动作类是否属于ModelDriven类型
属于的话,就调用我们实现的getModel方法,获取我们传入的对象
然后将我们的对象给压入栈中
struts2 的异常处理机制
成熟的 MVC 框架应该提供成熟的异常处理机制。当然可以在方法中手动捕捉异常,当捕捉到特定异常时,返回特定逻辑视图名。
这种方式非常繁琐,需要在方法中写大量 try catch 块,最大的缺点还是一旦需要改变异常处理方法时,需要修改代码。
最好的方式是通过声明式的方式管理异常处理。struts2 提供了一种声明式的异常处理方式。
struts2 的异常处理
我们看Action接口中的execute方法声明。
public String execute() throws Exception
这就意味着我们重写该方法时,无需进行任何异常处理,而是把异常抛给struts2框架处理.
struts2框架接收到Action抛出的异常后,根据struts.xml文件配置的异常映射,转入指定的视图资源。
异常映射功能是由 exception的拦截器帮我们做的。
struts2的异常处理机制是通过在struts.xml中配置<exception-mapping..../>元素完成。
属性:
exception:异常类型
result:出现该异常时,系统转入result指定的结果
局部异常映射和全局异常映射
全局异常映射对所有的 Action 都有效,但局部异常映射只对该异常映射所在的Action有效。
如果全局异常映射和局部异常映射配置了同一个异常类型,在该 Action 内,局部覆盖全局。
局部异常映射:将<exception-mapping..../>元素作为<action.../>的子元素配置
全局异常映射:将<exception-mapping..../>元素作为<global-exception-mappings>元素的子元素配置
异常处理案例
我们做一个简单的登陆应用
第一步:我们编写我们的Action类
public class LoginAction implements Action{
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String execute()throws Exception{
//当用户名为monster时,抛出我们的自定义异常
if(getUsername().equals("monster")){
throw new MyException("自定义异常");
}
//当用户名为sql时,抛出sql异常
if(getUsername().equalsIgnoreCase("sql")){
throw new SQLException("用户名不能为sql");
}
if(getUsername().equals("cad")&&getPassword().equals("123456")){
return "success";
}else
{
return "error";
}
}
}
第二步:我们编写我们的登陆jsp页面
<body>
<form action="${pageContext.request.contextPath }/login" method="post">
用户名:<input type="text" name="username"><br>
密 码:<input type="password" name="password"><br>
<input type="submit" value="提交">
</form>
</body>
第三步:我们编写我们的配置文件struts.xml
<package name="p2" extends="struts-default">
//全局结果视图定义
<global-results>
<result name="sql">/exception.jsp</result>
</global-results>
//全局异常定义
<global-exception-mappings>
//出现sql异常,就转入到视图名为sql的视图结果
<exception-mapping result="sql" exception="java.sql.Exception"></exception-mapping>
</global-exception-mappings>
<action name="login" class="com.cad.struts2.LoginAction" >
//局部异常定义
<exception-mapping result="my" exception="conm.cad.struts2.MyException"></exception-mapping>
<result name="my">/exception.jsp</result>
<result name="success">/welcome.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
struts2 的未知处理器
从struts2.1开始,struts2增加了未知处理器。
当用户请求未知Action,或指定Action里的未知方法,或Action处理结束后返回一个未知的result。struts2允许使用未知处理器来处理这些情况。
未知处理器需要实现UnknownHandler接口,该接口包含三个方法
-handleUnknownAction:用户请求未知Action时,该方法会被回调
-handleUnknownActionMethod:用户请求指定Action的未知方法时,该方法会被回调
-handleUnknownResult:Action处理结束后返回一个未知Result时,该方法会被回调
一旦实现了自定义的未知处理器,就可以在struts.xml中通过<bean../>元素来配置未知处理器
例如
<bean name="myHandler" class="com.cad.struts2.MyUnknownHandler" type="com.opensymphony.xwork2.UnknownHandler"></bean>
未知处理器案例
第一步:我们实现我们简单的未知处理器
public class MyUnknownHandler implements UnknownHandler {
public ActionConfig handleUnknownAction(String namespace, String actionName) throws XWorkException {
System.out.println(actionName+"不存在");
return null;
}
public Result handleUnknownResult(ActionContext actionContext, String actionName, ActionConfig actionConfig,
String resultCode) throws XWorkException {
System.out.println(resultCode+"不存在");
return null;
}
public Object handleUnknownActionMethod(Object action, String methodName) throws NoSuchMethodException {
System.out.println(methodName+"不存在");
return null;
}
}
第二步:在struts.xml中配置未知处理器
//定义我们的未知处理器
<bean name="myHandler" class="com.cad.struts2.MyUnknownHandler" type="com.opensymphony.xwork2.UnknownHandler"></bean>
<package name="p3" extends="struts-default">
<action name="myAction"></action>
</package>
//定义未知处理器栈
<unknown-handler-stack>
<unknown-handler-ref name="myHandler"></unknown-handler-ref>
</unknown-handler-stack>
然后我们访问myAction,会输出success不存在。
是因为不指定action的class属性时,默认使用ActioSupport
一般未知处理器主要处理Action返回未知result时有用。
了解即可。
struts2 的类型转换
所有的 MVC 框架,都属于表现层的解决方案,都需要负责收集用户请求参数,并将请求参数传给应用的控制器组件。
这时问题出现了,所有的请求参数都是字符串类型数据,因此 MVC 框架必须具备将这些字符串请求参数转换成相应的数据类型。
struts2 提供了非常强大的类型转换机制,struts2 的类型转换是基于 OGNL 表达式。
struts2 提供了很好的扩展性,开发者可以开发出自己的类型转换器。完成字符串到自定义类型之间的转换。
如果类型转换中出现未知异常,开发者无须关心异常处理,struts2 的 conversionError 拦截器会自动处理该异常,并且在页面上生成提示信息。
servlet 中的类型转换
表单中提交的所有数据都是字符串类型
例如我们有一个User 类,name 为 String 类型,age 为 int 类型,birthday 为Date 类型,我们必须在 servlet 中获取表单传入的参数,然后将其进行类型转换,然后封装到 User 对象中。
上述需要程序员自己进行类型转换操作,比较繁琐。
对于一个 MVC 框架而言,一样需要将请求参数封装成对象,也必须将请求参数转换成对象属性的数据类型,这就是类型转换的意义。
Struts2 内建的类型转换器
struts 内建的类型转换器能自动将我们的表单数据(字符串)转换成对应的数据类型。
完成字符串和日期类型之间的转换时,日期格式必须使用请求用户本地的格式。一般是 yyyy-MM-dd,如果输入的日期格式不是本地的日期格式,例如我们输入1996/01/31,就会出现错误,类型转换失败。
自定义类型转换器
需求:
当我们在表单中输入的日期格式不是本地的格式时,就会出现类型转换错误,我们也经常需要将字符串转换成其他的格式,例如字符串转换成对象之类的操作,这时我们就需要自定义类型转换器。
struts2的类型转换器实际上是基于OGNL实现的。xwork集成了OGNL。
实现类型转换器必须实现TypeConverter接口。这个接口的方法太过复杂,所以还提供了一个该接口的实现类DefaultTypeConverter。
我们重写DefaultTypeConverter类的convertValue方法即可。
我们基于DefaultTypeConverter类实现类型转换器时,将字符串转换成我们需要的类型通过convertValue方法实现,将我们的类型转换成字符串也是通过convertValue方法实现,因此我们必须判断转换的类型来实现不同的逻辑。
为了简化类型转换器的实现,struts2提供了一个StrutsTypeConverter抽象类,这个类是DefaultTypeConverter类的子类。
我们看下这个类的源码 :
继承DefaultTypeConverter
public abstract class StrutsTypeConverter extends DefaultTypeConverter {
//重写DefaultTypeConverter类的convertValue方法
public Object convertValue(Map context, Object o, Class toClass) {
//如果要转换的类型是字符串类型,也就是把我们的类型转换成字符串,调用convertToString方法
if (toClass.equals(String.class)) {
return convertToString(context, o);
}
//如果参数是字符串数组,也就是将字符串转换成我们需要的类型,调用convertFromString方法
else if (o instanceof String[]) {
return convertFromString(context, (String[]) o, toClass);
}
//如果参数是字符串,也就是将字符串转换成我们需要的类型,调用convertFromString方法
else if (o instanceof String) {
return convertFromString(context, new String[]{(String) o}, toClass);
} else {
return performFallbackConversion(context, o, toClass);
}
}
protected Object performFallbackConversion(Map context, Object o, Class toClass) {
return super.convertValue(context, o, toClass);
}
//将字符串转换成我们需要的类型的方法
public abstract Object convertFromString(Map context, String[] values, Class toClass);
//将我们的类型转换成字符串的方法
public abstract String convertToString(Map context, Object o);
}
三个参数 :
Map context:OGNL的上下文。暂时还没学,后面会学到,暂时不用管。
value:需要转换的参数。
toClass:转换后的类型
自定义类型转换器案例
需求:我们将我们前面的注册案例中的生日改成 yyyy/MM/dd类型
第一步:创建自定义类型转换器
public class MyConverter extends StrutsTypeConverter {
//日期转换器,转换成指定的类型
private DateFormat format=new SimpleDateFormat("yyyy/MM/dd");
//将字符串转换成日期类型
public Object convertFromString(Map context, String[] values, Class toClass) {
//判断参数是否为空
if(values==null||values.length==0){
return null;
}
//我们只有一个参数,就是表单的birthday
String date=values[0];
//判断目标类型是否是Date
if(toClass==java.util.Date.class){
try {
//进行转换
return format.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
//将日期类型转换成字符串
public String convertToString(Map context, Object o) {
//判断当前参数是否是日期类型
if(o instanceof java.util.Date){
return format.format(o);
}
return null;
}
}
第二步:注册类型转换器
局部类型转换器
按照属性来注册
如果属性都在action中,那么应该创建一个文件 Action名字-conversion.properties ,例如LoginAction-conversion.properties
如果属性放到了javaBean中,那么创建一个文件 javaBean名称-conversion.properties 例如 User-conversion.properties
文件由键值对组成。
键为需要转换的属性名字,值为自己实现的类型转换器全类名。
我们创建 User-conversion.properties
内容 birthday=com.cad.web.convert.MyConverter
这时我们注册时使用 1996/01/24这种格式进行注册就不会出现类型转换错误。
用户提交请求时,请求中的birthday参数会先被该类型转换器处理。
全局类型转换器
所有的Action都能用。我们需要在src目录下创建一个 xwork-conversion.properties 的文件
因为是全局的,就不存在只对birthday这个属性进行转换。
这里的键是要转换的类型,值还是类型转换器类。
我们创建 xwork-conversion.properties
内容 java.util.Date=com.cad.web.convert.MyConverter
这样当我们输入日期的表单时,就可以使用我们自定义的日期格式。
类型转换中的错误处理
struts2 提供了一个名为 conversionError 的拦截器,这个拦截器被注册在默认拦截器栈中。
当类型转换器执行类型转换出现错误时,该拦截器负责将对应错误封装成表单域错误 (fieldError),并将错误信息放入 ActionContext 中。
当拦截器对转换异常进行处理后,系统会跳转到名为 input 的逻辑视图。
我们在struts.xml中配置 <result name="input">/regist.jsp</result>
当类型转换失败后,再跳转到注册页面
跳转到input视图以后,我们发现没有任何错误提示信息。我们前面讲过conversionError拦截器会将转换错误封装成fieldError,并放在ActionContext中。
为了在页面中输出错误信息,我们需要使用struts的标签。我们先使用一些,后面会详细介绍。
我们在页面添加<s:fielderror></s:fielderror>标签
当我们类型转换失败后,就会输出错误信息。
我们发现输出的错误信息是英文的,我们希望能变为中文的提示信息。
我们只需要在创建一个properties文件 文件名为 javabean名称.properties
键为invalid.fieldvalue.属性名称 例如 :invalid.fieldvalue.birthday
值为要输出的内容 例如 invalid.fieldvalue.birthday=生日格式不正确
PS:本文转载 https://blog.csdn.net/c99463904/article/details/72231558,如需了解 Struts 的输入校验或者更多,可 点击这里