Struts2 learning (2) data encapsulation mechanism

Course outline:

        Explain the three ways of data encapsulation in Struts2 and the specific implementation principles

 

1. Attribute-driven Struts2 data encapsulation mechanism

 

Let's first look at how a traditional servlet handles data passed from a page.

First, we send the corresponding data to the servlet in the form

<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);
}


It can be seen that in the servlet , the request object in the Web is used to obtain the value of the Parameter element in it to obtain the corresponding value, and then encapsulate it into the corresponding object.

 

But in Struts2 , there is no servlet class, but the filter is implemented with the Action object.

So how is the above work done in Struts2 ?

public class LoginActionextends ActionSupport{
   //The attribute value here should be consistent with the name value in the form
    //And must have corresponding set and get methods
    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;
   }
}

The above is the Action class we wrote for login authentication. After writing, we need to configure it into the struts.xml file so that it can be used by the framework. The configuration information is as follows:

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

After the configuration is complete, you can change the action in the form to the name of the current action (note that the suffix is ​​.action , or you can set it yourself, using the constant attribute)

Then start the project and go to the corresponding page to enter the user name and password, and you can see that the relevant information has been printed in the console.

But we have not done any related data encapsulation operations in the code,

But the corresponding data has been encapsulated into attributes.

 

This is the attribute encapsulation mechanism in the data encapsulation of struts2 . You only need to set the attributes of the data to be submitted in the current form in the Action (note that there may be a problem of attribute type conversion, which will be discussed in this later course. The current The demo only involves attributes of type String , and the data submitted in the form is also of type String . All types are not involved.) When the form is submitted, the data will be directly encapsulated into the corresponding attributes.

 

So how does this mechanism work?

 

Before discussing this issue, let's first look at the specific steps in this process:

  1. First of all, the form needs to submit data, then it must be a servlet that receives the data .
  2. Since it is a Servlet , it must be the doPost method that performs the corresponding operation .
  3. But the class that performs the relevant operations is not a servlet !
  4. In this case, there is only one way if you want to have Servlet methods
  5. Inherit the parent class and make the parent class a servlet
  6. The subclass does not override the method in the parent class
  7. In this way, when the form data is sent over, it will go back and execute the doPost method in the parent class.
  8. If you want to operate the data in the subclass when the parent class executes, you need to use the core mechanism in java - the reflection mechanism

 

Then we will use the code to implement the corresponding operation:

首先我们先编写父类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的一些东西,增加了系统的耦合度,所以较少使用。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325716869&siteId=291194637