【JavaWeb-16】静态参数封装、3种动态参数封装、数据类型转换、struts标签、验证器

来源:互联网 发布:求质数 c语言 编辑:程序博客网 时间:2024/05/07 17:28

1、Struts静态参数封装。什么叫封装呢?其实就是把用户输入的数据获取到,然后输出或者封装到类里面去。

——静态参数,也就是我们写死的数据,不能算是用户动态提交的数据。我们测试一遍。

——新建一个web project,把jar包导入到lib里面,然后在src下面新建一个struts.xml文件,再在web.xml里面配置名字叫struts2的过滤器。我们在struts.xml里面增加如下代码。

<struts>    <constant name="struts.devMode" value="true"></constant>    <package name="p_name_1" extends="struts-default">        <action name="action1" class="com.hello.web.action.MyAction" method="testParams">            <result name="success" type="redirect">/success.jsp</result>            <param name="username">eric</param>            <param name="password">1234</param>        </action>    </package></struts>

——根据这个配置,我们新建一个继承自ActionSupport的类,叫MyAction,在里面定义两个变量,再写一个testParams方法。如下:

public class MyAction extends ActionSupport {    private String username;    private String password;    public String testParams(){        System.out.println(username+":"+password);        return "success";    }    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;    }}

——我们部署后访问http://localhost:8080/Day01_Params/action1.action,在控制台有输出eric:1234

——这里面需要注意的是:在action里面的param里定义的参数name名称一定要和动作类里面的变量名称一致。而且要有set方法。

——因为在我们执行testParams方法的前后,有很多拦截器interceptor做一些工作,其中有一个叫做staticParams的拦截器,它就是专门负责看看你struts.xml里的action里是否有定义param参数,如果有的话,拿到这个参数的name名字,然后去这个action的对应的类class里面的方法method去找setXXX方法,和setXXX中set后面的XXX比较(这里它会自动转换大小写),如果发现有匹配的,就把值复制给类class里面的变量。然后我们在执行这个方法method的时候,这个变量其实就已经有值了,我们输出到控制台的时候就能看到值了。

2、动态参数封装。核心的例子就是表单数据的提交。

——我们创建一个表单,动作类还是那个动作类,不变。

    <form action="${pageContext.request.contextPath }/action1.action" method="post">        用户名:<input type="text" name="username" /><br>        密码:<input type="password" name="password" /><br>        <input type="submit" value="提交" />    </form>

——我们在action里面什么都没写,就写了个正常的结果成功后跳转。

    <package name="p_name_1" extends="struts-default">        <action name="action1" class="com.hello.web.action.MyAction" method="testParams">            <result name="success">/success.jsp</result>        </action>    </package>

——我们访问http://localhost:8080/Day01_Params/index.jsp,输入参数后,点击提交,浏览器跳转到成功页面,控制台有输出李四:1234。这一切的原理又是什么呢?

——这里的一切都是严格遵循规范的,比如form表单里面的name值一定要和动作类里面的变量名字一致(静态参数封装的要求是param的name值一定要和动作类里面的变量名字一致,更进一步应该是setXXX的XXX一致),然后就没有然后了,就这么一个要求。

——原理是因为这里面又使用了一个默认的拦截器,我们说静态参数封装用的是staticParams的拦截器,而这次动态参数封装用的默认拦截器是params。这个拦截器会检查表单里面参数的name值,然后和action对应的类class的变量(其实是setXXX的XXX)匹配。如果匹配上了,那就赋值。说白了,原理和静态参数是类似的,只是用了不同的拦截器来处理。

3、第二种动态参数封装。我们上面的封装把变量和方法都写在动作类里面了,下面的这种封装就更贴近实际开发,也就是我们的参数是封装在一个类里面的,比如User,所以我们在动作类里面使用的不是零散的username和password变量,而是User类。所以我们得先写一个User类,我们把变量封装在User类里面了:

public class User implements Serializable {    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;    }}

——然后改变一下动作类里面的变量和方法,变量换成了user实例,注意方法里面获取数据的方式也变成了user.getXXX()

public class MyAction extends ActionSupport {    private User user;    public String testParams(){        System.out.println(user.getUsername()+":"+user.getPassword());        return "success";    }    public User getUser() {        return user;    }    public void setUser(User user) {        this.user = user;    }}

——如果只是到这一步,表单提交过来,表单里面的name和我们动作类里面的setUser根本对应不上,所以还是不行的?那我们就得修改表单里面的name。改成如下:

    <form action="${pageContext.request.contextPath }/action1.action" method="post">        用户名:<input type="text" name="user.username" /><br>        密码:<input type="password" name="user.password" /><br>        <input type="submit" value="提交" />    </form>

——上面那种把表单里的name改成user.username等形式估计还是第一次见吧。正式这种规范把动态参数封装运作起来了。流程是这样的。拿到表单的name一看是user.username,它就知道是一个封装类,然后拿到name里面user和动作类里面的setXXX匹配(这些步骤还是不变的),匹配到了,如果没有实例就利用getUser创建一个实例user,然后到这个类User里面去找和name后面的username匹配的setXXX,然后匹配到了setUsername就把username赋值给了user.username,最后我们就获得了一个user实例以及里面的数据,可以随心所欲地使用数据了。

4、下面这种就是实际开发中常用的动态参数封装方法。叫模型驱动的动态参数封装方法。我们上面那种方法有个缺点,就是在form表单里面写的name是user.username,这明显不符合一般习惯啊,我们还是习惯写成username,但是写成username的话,在动作类里面它又匹配不到合适的setUsername(这是在User类里面才有的set方法),所以要么修改表单里面的name值,要么修改我们的动作类,上面的一种方法是修改了form表单里的name值。下面这种就是改造了一下动作类。

——实现了ModelDriver接口,泛型就是我们的实体类User。

public class MyAction extends ActionSupport implements ModelDriven<User> {    private User user=new User();    public String testParams(){        System.out.println(user.getUsername()+":"+user.getPassword());        return "success";    }    public User getModel() {        return user;    }}

——我们可以看到上面的动作类里面,连getUser和setUser方法也都没有了,只有一个getModel方法,返回了一个User实例。

——这里的原理是:ModelDriver接口里面的有一个intercept方法,这个方法是判断我们的动作类是否属于(instanceof)ModelDriver,因为我们实现了这个接口,所以答案为是,如果是的话,它就把我们的动作类转换成ModelDriver类型的类,然后调用我们动作类的getModel,也就是我们实现的接口方法,得到返回的实例化对象user。如果这个user不等于null的话,它就把我们的实例化对象user做了stack.push()操作,压到栈里面去了,进行了一系列操作。也就是说,我们的动作类只要实现了ModelDriver接口并实现里面的getModel方法,返回我们自己实例化的User对象,它就会自动获得我们的实例化对象,然后做一系列操作。

——注意,必须我们自己实例化对象。

private User user=new User();

——注意:

  1. 别忘了把form表单里的name改成正常的样子usernamehe password。
  2. 虽然上面我们利用了ModelDriver后可以不写user的set和get方法,但是还是建议写一下。

5、数据类型转换。我们从表单过来的数据要么是String要么是String[],但是类里面的变量可能是其他类型,所以需要转换。

——struts2中基本数据类型会自动转换;日期类型只认识默认的yyyy-MM-dd,结果存在数据库中也是以yyy-MM-dd的形式存储;字符串数组是以逗号+空格隔开存储的,比如吃饭, 睡觉, 打豆豆

——那问题来了。如果我们在表单中输入的日期String是2016-09-30的话,它能够自动处理。但是如果我们输入09/30/2016的话就会报错。如果我们需要纠正的话,就需要重写类型转换器。

——类型转换器的写法是我们定义一个类,继承自StrutsTypeConverter,并且实现convertFromString和convertToString,也就是把获取到的不是默认格式的String转换成日期格式,需要的时候把数据库中的默认的格式转换成用户习惯的格式。也就是在这两个方法里面利用SimpleDateFormat。

——写了这个类型转换器后,需要配置。有两种配置方法,一种是局部类型转换器的配置,也就是给某个特性的javabean使用:在这个bean同级下方新建一个javabean-conversion.properties的文件,在里面写一个key-value,key是需要转换的属性,value就是我们自定义类型转换器类的全路径名称。比如:User-conversion.properties:

birthday=com.hello.web.converter.MyTypeConverter

还有一种配置方法是全局类型转换器的配置。在src下面新建一个xwork-conversion.properties文件,在里面写key-value,key就不是一个属性了,因为是全局的大家都能用,所以不能固定是某个属性,而是某个数据类型,value不变。比如:

jav.util.Date=com.hello.web.converter.MyTypeConverter

6、我们其实更关心的是如果数据类型错误,怎么处理?给struts.xml里的action增加一个结果视图,类型是input,意思是如果有字段值错误,返回到index.jsp页面,这种解决办法叫做回显视图来提示。

<result name="input">/index.jsp</result>

——然后我们就请出我们用到的struts标签。先在jsp页面添加库。
这里写图片描述

——然后使用,在任何地方增加一个字段错误的标签。如果出现input错误然后返回index.jsp页面的时候,这个<s:fielderror />会自动条用一句话,就是报错的信息。

    <s:fielderror />    <form action="${pageContext.request.contextPath }/action1.action" method="post">        用户名:<input type="text" name="username" /><br>        密码:<input type="password" name="password" /><br>        <input type="submit" value="提交" />    </form>

——但,其实我们可以用标签重写我们整个form表单,如下。它默认method就是post所以省略,而action不需要contextpath,所以直接写action1.action即可。里面有很多标签,比如必须填写、回显密码等。这时候我们就不需要单独写<s:fielderror />了,因为我们这样写表单的时候已经内置了这样的功能,它会在对应错误字段的上面显示错误信息。

    <s:form action="action.action">        <s:textfield name="username" label="用户名" requiredLabel="true"></s:textfield>        <s:password name="password" label="密码" showPassword="true"></s:password>        <s:submit value="提交"></s:submit>    </s:form>

——但是,到这里还有一个问题,就是显示的错误信息,是系统默认的。如果我们需要修改成我们自己的信息,就需要配置一个文件。与表单对应的一般都有一个javabean实体类,比如User,我们在于User同级的下方新建一个User.properties文件(在属性比如birthday所在类的同级下方新建一个名字与所在类User相同的properties文件),在里面写如下代码即可:

invalid.fieldvalue.birthday=格式错误,请输入yyyy-MM-dd格式

7、编程式验证。因为我们的ActionSupport实现了Validateable和ValidationAware等接口,Validateable里面有个validate方法。

    public void validate(){        if(StringUtils.isEmpty(user.getUsername())){            addFieldError("username", "用户名没有哦!");        }    }

——这个validate方法会在action的动作方法之前执行。

—— 它会拦截所有的action在它们之前进行验证,也就是说所有的如果你不配置的话,写一个就相当于是给所有的action都添加了相同的验证。解决的办法有2种,一种是在其他不需要验证的action对应的方法之前增加一个修饰符叫@SkipValidation

    @SkipValidation    public String testParams()        return "success";    }

——还有一种方式不要写一个统一的validate方法,而是遵循一种规范为每一个action对应的方法写一个验证方法,规范是validate +首字母大写的动作方法名,如validateTestParams

8、与上面编程式验证对应的就是不编程,只利用配置文件进行配置,叫做声明式验证。

——新建一个与动作类名称对应的xml文件,名字规范是动作类名-validation.xml
这里写图片描述

——里面的约束文档类型去源码里面找去。
这里写图片描述

——里面写的就是验证表单的什么name?然后是验证哪一种,比如例子中的是否为空?然后是出错时候的提醒信息。一层套一层。

——但遇到了同样的问题,就是这种方式对所有的action都做了验证。解决的话还是2种方式,第一种和之前说过的一样,就是在其他不需要验证的action对应的方法前面增加@SkipValidation

——第二种方式就是改这个xml,把名字变成动作类名字-动作action名字-validation.xml。比如我们这里变成MyAction-action1-validation.xml。注意这里的第二个是action名字,而不是对应的方法名。

——下面第一种是基于一个个字段来验证的,第二种是基于验证器来验证的。

<validators>    <field name="username">        <field-validator type="requiredstring">            <message>用户名飞走了!</message>        </field-validator>    </field>    <validator type="requiredstring">        <param name="filedName">username</param>        <message>用户名呢?</message>    </validator></validators>

——关于验证器的type,按照下列路径在struts源码里面找,可以看到有很多验证的type。
这里写图片描述

9、验证器这一块,有很多小的使用技巧。其中有包含正则和表达式的一些用法。这里有一个用户注册结合验证器的案例。

0 1