Struts2学习(二)数据封装机制

课程概要:

        讲解Struts2中数据封装的三种方式以及具体实现原理

 

一、Struts2数据封装机制之属性驱动

 

我们先来看一下传统的servlet是如何处理从页面传递过来的数据的。

首先我们在表单发送了对应的数据到servlet中去

<form action="<%=path%>/loginservlet"method="post">
       账号:<inputtype="text"name="username"/><br>
       密码:<inputtype="password"name="password"/><br>
       <inputtype="submit"value="提交"/>
</form>

protected voiddoPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
        String username=request.getParameter("username");
        String password=request.getParameter("password");
        User user=newUser();
        user.setUsername(username);
        user.setPassword(password);
}


可以看到在Servlet中是使用Web中的request对象来获取其中的Parameter元素的值来获得对应的值,然后将其封装到对应的对象中去。

 

但是在Struts2中并没有servlet类而是使用过滤器配合Action对象实现的。

那么在Struts2中是如何完成上面的工作的呢?

public class LoginActionextends ActionSupport{
   //此处的属性值应与表单中的name值一致
    //并且必须得有对应的set和get方法
    private String username;
   private String password;
   @Override
   public Stringexecute()throws Exception{
        System.out.println("username="+username);
        System.out.println("password="+password);
       return"success";
   }
   public StringgetUsername() {
       return username;
   }
   publicvoidsetUsername(String username) {
       this.username= username;
   }
   public StringgetPassword() {
       return password;
   }
   publicvoidsetPassword(String password) {
       this.password= password;
   }
}

上面的就是我们编写的登录验证的Action类,在编写完成后我们需要将其配置到struts.xml文件当中去,使其可以被框架所使用。配置信息如下:

<action name="userLogin"class="cn.lovepi.chapter02.action.LoginAction">
           <resultname="success">/chapter02/index.jsp</result>
</action>

在配置完成后便可以将表单中的action改为当前的actionname(注意后缀是.action,也可自行设置,使用constant属性)

然后启动项目转到相应页面中输入用户名和密码后可以看到在控制台中已经打印出来了相关信息。

但是我们却并没有在代码中做过任何相关的数据封装操作,

但是对应的数据却已经被封装到属性当中了。

 

这便是struts2的数据封装中的属性封装机制,只需在Action中设置当前表单中要提交的数据的属性(注意这里可能会存在属性类型的转换问题,这个以后的课程中讲到。现在的演示中只涉及到String类型的属性,同时表单中提交的数据也是String类型的。所有不牵扯到类型转换问题)待表单提交过来数据时便直接会被封装到对应的属性中去。

 

那么这个机制到底是如何实现的呢?

 

在讨论这个问题之前首先先来看一下在这个过程中的具体步骤:

  1. 首先表单要提交数据,那么接收数据的必定是一个Servlet
  2. 既然是Servlet那么其中执行相应操作的必须是doPost方法。
  3. 但是执行相关操作的类却不是一个Servlet
  4. 在这种情况下还想要具有Servlet的方法的话就只有一个办法
  5. 继承父类,让父类成为一个Servlet
  6. 而子类却不复写父类中的方法
  7. 这样当表单数据传送过来的时候就回去执行父类中的doPost方法
  8. 在父类执行时想要对子类中的数据进行操作,这时便需要使用到java中的核心机制----反射机制

 

那么接下来我们便使用代码来实现对应的操作:

首先我们先编写父类BaseServlet,并且继承HttpServlet

package cn.lovepi.chapter02.servlet;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Enumeration;
/**
* Created by icarus on 2016/7/8.
* 模拟实现属性封装和的实现原理
*/
public class BasicServletextends HttpServlet{
   /**
     * 子类在运行中执行到这里
     * 所以这里的当前对象还是子类对象
     * 利用反射来获取子类中的属性列表和对应的实体对象
     * 使用request.getParameterNames()方法来获取到表单中的name列表
     * 当属性名称与表单name相同时就把表单中的内容通过反射设置到属性中去
     * 然后利用反射调用execute方法,获取得到的url地址
     * 根据url地址来进行对应的请求转发
     */
    @Override
   protectedvoiddoPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{
       //获取当前对象的属性列表
        Field[] fields=this.getClass().getDeclaredFields();
       //获取表单中的name列表,返回值是一个Enumeration枚举类型
        Enumeration<String> names=req.getParameterNames();
       //开始对表单的name值和属性名称进行比较
        while(names.hasMoreElements()){
           for(Field f:fields){
               //当属性名称和name相同
                if(names.nextElement().equals(f.getName())){
                   //*这一句非常重要*
                    f.setAccessible(true);
                   //将表单中的值设置到对应的属性中去
                    try{
                        f.set(this,req.getParameter(f.getName()));
                   }catch(IllegalAccessException e) {
                        e.printStackTrace();
                   }
               }
           }
       }
       //获取子类中的execute并执行,获取他的返回值即为请求的url
        try{
            Method execute=this.getClass().getMethod("execute",null);
            Object url=execute.invoke(this,null);
           //进行请求转发
            req.getRequestDispatcher(String.valueOf(url)).forward(req,resp);
       }catch(Exception e) {
            e.printStackTrace();
       }
   }
}


对子类LoginServlet进行编写:

/*   
    *步骤:此类继承父类BasicServlet,BasicServlet继承HttpServlet
    *也就是说此类还是一个Servlet
    *当表单数据发送到此类中后会首先执行doPost方法,
    *子类中没有便会去执行父类中的对应方法
    *接下来执行父类中doPost方法
*/
public class LoginServletextends BasicModelServlet{
   private String username;
   private String password;
   public Stringexecute()throws Exception{
        System.out.println("username="+username);
        System.out.println("password="+password);
       return"/chapter02/index.jsp";
   }
   public StringgetUsername() {
       return username;
   }
   publicvoidsetUsername(String username) {
       this.username= username;
   }
   public StringgetPassword() {
       return password;
   }
   publicvoidsetPassword(String password) {
       this.password= password;
   }
}


这便是Struts2中的数据封装机制-----属性驱动

 

二、Struts2数据封装机制之模型驱动

 

接下来介绍Struts2中的另外一种数据封装机制-----模型驱动

同样是刚才的表单,我们在编写一个新的Action来处理提交的数据,在将其配置到Struts系统中去。

package cn.lovepi.chapter02.action;
import cn.lovepi.chapter02.pojo.User;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
/**
* Created by icarus on 2016/7/6.
* 登录活动类-模型驱动演示
* 注意:这个类必须实现ModelDriven<>泛型中必须填对应的封装模型
* 并设置一个与所封装模型的实例化对象(注意必须实例化,否则会报空指针异常)
* ------------------------------------------------------------------
* 思路介绍:
* 当请求发送到action之前,
* 调用MLoginAction类中的getModel方法获取将要把表单数据封装到的实例化的类对象
* 获得该对象之后,我们便可以获得对应的类类型
* 利用反射可以获取到类中的属性列表
* 通过request.getParameterNames()可以获取表单中的name列表
* 判断name值和属性名称,一致的情况下
* 反射调用属性的set方法来给对应的属性值设置参数
* 从而完成数据的封装
*/
public class MLoginActionextends ActionSupportimplements ModelDriven<User>{
   //实例化所需封装的模型类
    private User user=new User();
   /**
     * 模型驱动方法
     * @return 封装好的模型类
     */
   @Override
   public User getModel() {
       return user;
   }
   /**
     * 活动执行方法
     * @return "success"与父类默认返回的一致
     * @throws Exception
     */
   @Override
   public String execute()throws Exception {
        System.out.println("username:"+user.getUsername());
        System.out.println("password:"+user.getPassword());
       return "success";
   }
}


那么我们也来看一看模型驱动的实现原理吧

其实具体的实现步骤和属性驱动的实现机制相同,只是由原先的字符类型的属性变成了一个对象属性,但是使用模型驱动必须实现ModelDrive<>而且泛型之中存放的是属性对象,实现接口的方法是getModel(),通过反射调用这个方面便能获取到属性的对象。剩下来的操作便和属性驱动的一致了。

接下来我们使用代码来实现。

首先那么我们将子类LoginServlet中的属性改为和Action中相同的

然后编写其的父类BasicModelServlet

package cn.lovepi.chapter02.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Enumeration;
/**
* Created by icarus on 2016/7/8.
* 模拟实现模型驱动的数据自动封装原理
* 当子类执行到doPost方法时
* 就可以获取到子类中的getModel方法,
* 执行后便能得到封装的模型类对象
* 获取模型类对象中的属性列表
* 利用request.getParameterNames()来获得表单中的name列表
* 判断naem和属性名称,相同的话便将name中的值设置到对象的属性中去
* 然后获取子类中的execute方法,执行后获得到对应的url
* 对url进行请转发
*/
public class BasicModelServletextends HttpServlet{
   @Override
   protectedvoiddoPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{
       try{
           //获取子类中的getModel方法
            Method getModel=this.getClass().getDeclaredMethod("getModel",null);
           //利用反射获取到所需封装类对象
            Object object=getModel.invoke(this,null);
           //获取模型类中的所有属性
            Field[] fields=object.getClass().getDeclaredFields();
           //获取表单中的name列表
            Enumeration<String> names=req.getParameterNames();
           //开始进行name和属性名称的比较
            while(names.hasMoreElements()){
               for(Field f:fields){
                   //当属性名称和表单name相同时
                    if(names.nextElement().equals(f.getName())){
                       //非常重要
                        f.setAccessible(true);
                        f.set(object,req.getParameter(f.getName()));
                   }
               }
           }
           //获取子类中的execute方法并执行,获取得到的url
            Method execute=this.getClass().getDeclaredMethod("execute",null);
            Object url=execute.invoke(this,null);
           //根据对应的url进行请求转发
            req.getRequestDispatcher(String.valueOf(url)).forward(req,resp);
       }catch(Exception e) {
            e.printStackTrace();
       }
   }
}


 

三、Struts2数据封装机制之标签驱动

 

想要通过Struts2中的标签来实现表单数据的自动封装的前提是得在表单中加入Struts2标签支持。

<%@taglibprefix="s"uri="/struts-tags" %>

这里需要注意的是prefixStruts2标签的别称,一般都是“s

同时在表单之中也得使用Struts2中定义的表单实现方式来定义表单。

<s:form action="sUserLogin"method="POST">
       账号:<s:textfieldname="user.username"/>
       密码:<s:passwordname="user.password"/>
       <s:submitvalue="提交"/>
</s:form>

在这里表单标签都得使用s:的形式,而且action不需要后缀。

在这里还存在着表单样式的问题,这个问题后再后面的课程中讲解,这里就不处理了。

 

接下来是对应Action的编写

package cn.lovepi.chapter02.action;
import cn.lovepi.chapter02.pojo.User;
import com.opensymphony.xwork2.ActionSupport;
/**
* Created by icarus on 2016/7/7.
* 标签方法实现数据自动封装
*/
public class SLoginActionextends ActionSupport{
   // 与模型驱动方法不同之处是这里无须实例化所封装对象
    private User user;
   @Override
   public Stringexecute()throws Exception{
        System.out.println("----标签实现----");
        System.out.println("username="+user.getUsername());
        System.out.println("password="+user.getPassword());
       return"success";
   }
   public UsergetUser() {
       return user;
   }
   publicvoidsetUser(User user) {
       this.user= user;
   }
}


 

总结:

       在这里我们学习了Struts2当中数据封装的三种机制:属性驱动、模型驱动、标签驱动。

三种方式各有优势,我们都应掌握每种方式的使用以及了解其具体的内部实现原理。基于实际使用情况来看我们最多使用的是属性驱动和模型驱动,由于标签驱动使用了Struts2的一些东西,增加了系统的耦合度,所以较少使用。

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
Spring MVC框架是有一个MVC框架,通过实现Model-View-Controller模式来很好地将数据、业务与展现进行分离。从这样一个角度来说,Spring MVC和Struts、Struts2非常类似。Spring MVC的设计是围绕DispatcherServlet展开的,DispatcherServlet负责将请求派发到特定的handler。通过可配置的handler mappings、view resolution、locale以及theme resolution来处理请求并且转到对应的

通过WSDL生成客户端代码 - 2016-07-25 14:07:03

目录 1.WSDL2Java:Building stubs,skeletons,and data types from WSDL . 1 1.1示例 ... 1 1.2测试 ... 4 1.2.1异常:没有定义com.pan.model.User的序列化的实现 ... 5 1.3WSDL与生成的客户端代码结构分析 ... 5 1.3.1Types . 6 1.3.2Holders . 12 1.3.3PortTypes . 13 1.3.4Bindings . 13 1.3.5 Services . 14
                                               Ofbiz:数据库移植mysql并创建自己的mysql            Ofbiz原生数据库是derby,而作为开发使用,其就不能满足我们需求,ofbiz支持多种数据库,我们就可以将数据移植到mysql.            第一步:找到framework\entity\config\entityengine.xml这个文件,找到之后进行下面相关操作.          1、添加或者修改datasourc

linux的一些简单命令 - 2016-07-25 14:07:59

这里只是列出实际中使用频率较高的,可以通过 man 命令或者 命令 –help 来查看更为详细的内容 文件有关的 1:【ls命令】 ls [option] …[file]… -a all 列出所有的文件 包括隐藏文件 [eg ls -a /home] -l 列出详细的文件信息 可以简写为ll filename [eg: ls -l /home or ll /home ] -h –human-readable 将文件的大小通过字节的方式列出来 -R 递归显示出该目录所有的文件 -d 只显示本文件下面 可以通

Nginx下的rewrite规则 - 2016-07-25 14:07:08

正则表达式匹配,其中: * ~ 为区分大小写匹配 * ~* 为不区分大小写匹配 * !~和!~* 分别为区分大小写不匹配及不区分大小写不匹配 文件及目录匹配,其中: * -f 和! -f 用来判断是否存在文件* -d 和! -d 用来判断是否存在目录* -e 和! -e 用来判断是否存在文件或目录* -x和!-x 用来判断文件是否可执行 rewrite指令的最后一项参数为flag标记,flag标记有: 1. last 相当于apache里面的[L]标记,表示rewrite。 2. break 本条规则匹配

Apache Flink Client生成StreamGraph - 2016-07-25 04:07:11

概述 上文我们分析提交流程时, RemoteStreamEnvironment 类的 execute 方法的第一步就是生成 StreamGraph 。 StreamGraph 是用于表示流的拓扑结构的数据结构,它包含了生成 JobGraph 的必要信息。它的类继承关系图如下: 如果你按照 StreamGraph 的继承链向上追溯,最终会发现它实现了接口 FlinkPlan 。Flink在这里效仿的是数据库的执行SQL是产生执行计划的机制, FlinkPlan 定义在Flink的优化器相关的包中,针对流应用

zabbix wechat 报警 - 2016-07-24 22:07:29

监控在运维工作中是比不可少的一环,那伴随着监控也同时会涉及到告警机制,一般的监控到的结果是成功或者失败,如Ping不通、访问网页出错、连接不到Socket,发生时这些称之为故障,故障是最优先的告警。那针对于 zabbix 的告警可以有多种方式去做: zabbix三种常见报警介质: 短信:它的好处是不用联网手机有信号就行,但是需要有短信网关,需要花钱。 邮件:它也可以做到手机短信通知,基本现在邮箱都有这个功能(如果你使用的是移动的手机号,可以让zabbix将报警信息发送到139邮箱,再通过139绑定到手机号
J2EE进阶(十一)SSH框架整合常见问题汇总(二) 问题 8       java.lang.ClassCastException : java.lang.String cannot be cast to java.lang.Boolean      解决       数据库中userdetail表的映射文件如下,可见xb字段数据类型为boolean类型,而自己在userdetail模型类中定义的类型为String类型。为此可以得出这样的结论。模型类中的数据及类型必须与数据表映射文件中的字段信息保持一致
背景: 为了方便整体产品的发布,希望通过docker实现增量发布。大致的思路如下: is-there-a-way-to-add-only-changed-files-to-a-docker-image-as-a-new-layer-with 。本博文对这种方式进行了尝试,与此同时简单介绍如何通过Dockerfile来创建Docker镜像。 前期准备: 解决centos的网络问题 【can not find a valid baseurl for repo: base/7/x86_64】 ,使用dhclie

pcap文件的python解析实例 - 2016-07-24 19:07:52

最近一直在分析数据包。 同时也一直想学python。 凑一块儿了...于是,便开工了。座椅爆炸! 正文 首先要说的是,我知道python有很多解析pcap文件的库,这里不使用它们的原因是为了理解pcap文件的格式细节。使用tcpdump你可以很容易抓取到一系列的数据包,然而tcpdump并没有分析数据包的功能,如果想从这个抓包文件中分析出一些端倪,比如重传情况,你必须使用wireshark之类的软件,用wireshark打开tcpdump抓取的pcap文件,如果你看到了一堆堆的深红色(类似静脉血管里流出的猪