In-depth understanding of Struts2 ---- type conversion


The previous series of articles mainly introduced some basic usage of Struts2 and some of the simple principles, but never introduced the relevant content about the interceptor, from this article we will start from another perspective to understand the use of the framework in depth, the core or interceptor, but this article first to introduce the relevant content about the framework of the type conversion. The main subsections contain the following.

  • 类型转换的使用场景
  • Default converter within Struts2
  • OGNL-based type conversion
  • Custom Type Conversion
  • 注册类型转换器
  • Error handling for type conversions

I. Scenarios for the use of type conversion What is type conversion? Type conversion means that when we use GET/POST or action tags on the client side to forward to a specific Action instance, the parameters we pass in can be automatically converted to the value of the instance property of the Action instance in a process. Look at a familiar example below and review.

//login登录页面
  <body>
    <form method="post" action="login">
        姓名:<input type="text" name="username" /><br />
        密码:<input type="password" name="password" /><br />
        <input type="submit" value="提交"/>
    </form>
  </body>
//login
public class LoginAction extends ActionSupport {
    private String username;
    private int password;
    public void setUsername(String name){
        this.username = name;
    }
    public String getUsername(){
        return this.username;
    }
    public void setPassword(int pa){this.password = pa;}
    public int getPassword(){
        return this.password;
    }

    public String execute() throws Exception{
        return SUCCESS;
    }
}
//index页面
  <body>
    <h1>this is the index page</h1>
    <p><s:property value="username"/></p>
    <p><s:property value="password"/></p>

    <br/>
    <s:debug/>
  </body>

We first enter the values of the two forms on the login page, then forward to the LoginAction , execute the execute method, pull the content of the index page, and the output is as follows.

Although we don't explicitly assign values to the two instance properties of LoginAction, we can still get the values of the properties in the index page, and their values correspond to the values submitted from the login form page, which means that the values are automatically passed by name when they are submitted to LoginAction from the form page. This is a type conversion from the String type of the form page to the type of the corresponding property in the LoginAction, but this automatic conversion doesn't always work, as we'll see next.

二、 Default converter within Struts2 All the values entered in the form will be submitted as String types to the corresponding Action. As for how to convert these String types to the types of the properties in the Action is to do some judgment, there is a type converter in Struts2 by default that can help us to do most of the automatic conversion operations. Its supported target types for conversion from String types are as follows.

  • boolean和Boolean:字符串true会转换为布尔类型值true
  • char and Character: string to character
  • int and Integer: string to integer type
  • long and Long: string to long integer
  • float and Float: string to single precision floating point
  • double and Double: string to double precision floating point
  • Date: string to date type, need string to meet certain format
  • Array: multiple input forms submitted to the same Action's property will form an array to be passed into the property
  • Sets: similar to arrays, the type needs to be specified, and the type cannot exceed the basic data type

对于我们在Action中声明的属性的类型,如果是以上的这些类型的话,那么从客户端提交的过来的字符串就可以默认使用该机制自动转换成对应的类型,完成自动赋值。如果不是上述的类型,那么就需要 Custom Type Conversion器来实现显式的转换类型,该内容后文介绍。此处只需要知道Action中的属性的类型为上述的几种,则从表单页面传入的数值会自动根据属性名自动完成赋值。

III. OGNL-based type conversion For non-basic types, we can't solve the problem using the default conversion mechanism, e.g. by modifying the above LoginAction.

 // where walker is a Javabean compliant class with two properties name and age
public class LoginAction extends ActionSupport {
    private Walker walker;
    public void setWalker(Walker w){
        this.walker =w;
    }
    public Walker getWalker(){
        return this.walker;
    }

    public String execute() throws Exception{
        return SUCCESS;
    }
}

If one of the properties of the Action instance is of our custom type, then the original code of the login form page will definitely not work, because the username and password you specify are not available in the Action instance. So how do we assign a string to an Action instance property? The ognl syntax is capable of doing this, e.g.

 //login page, using the form tag
  <body>
    <s:form method="post" action="/login">
        <s:textfield name="walker.username" label="用户名"/>
        <s:textfield name="walker.age" label=" age"/>
        <s:submit value="提交"/>
    </s:form>
  </body>
//index页面
  <body>
    <h1>this is the index page</h1>
    <p><s:property value="walker.getUsername()"/></p>
    <p><s:property value="walker.getAge()"/></p>

    <br/>
    <s:debug/>
  </body>

We use ognl syntax on the login page, walker.username specifies passing a value for the username property of the Action instance property walker, and walker.age specifies passing a value for the age property of the Action instance property walker. In fact, we can see here that the use of ognl syntax allows for the conversion of non-basic types, which actually still translates the problem to the first case we discussed, that is, converting the question: how to convert a String type to a non-basic type, to :how to convert a String type to the type of a non-basic type property. And this kind of problem has been solved for us by Struts. The following is a screenshot of the above procedure in action.

One more thing to note about this classification: the operation here is somewhat changed for list and map collections. Let's look in detail at.

// Modify the property to alist muster
public class LoginAction extends ActionSupport {
    private List<Walker> list;
    public void setList(List<Walker> w){
        this.list =w;
    }
    public List<Walker> getList(){
        return this.list;
    }

    public String execute() throws Exception{
        return SUCCESS;
    }
}
 // Modified login form page
  <body>
    <s:form method="post" action="/login">
        <s:textfield name="list[0].username" label="用户名"/>
        <s:textfield name="list[0].age" label=" age"/>
        <s:textfield name="list[1].username" label="用户名"/>
        <s:textfield name="list[1].age" label=" age"/>
        <s:submit value="提交"/>
    </s:form>
  </body>

LoginAction in the changes nothing needs to be said, as for the login page uses list[0].username to pass the value of username for the first element of the Action property list, the same, list[0].age to pass the value of age property for the first element of the Action property list. The code for the index page to traverse the list is not posted because it is relatively simple. Essentially the same as described above, and it all ends up using Struts' default converter. The following is the output.

The above describes the case of a list collection as an Action property, the case is actually similar for a map collection as an Action instance property, with only minor differences in terms of passing values and traversal.

//修改后的LoginAction 页面
public class LoginAction extends ActionSupport {
    private Map<String,Walker> map;
    public void  setMap(Map<String,Walker> w){
        this.map =w;
    }
    public Map<String,Walker> getMap(){
        return this.map;
    }

    public String execute() throws Exception{
        return SUCCESS;
    }
}
 Passing values to the form on the //login page
  <body>
    <s:form method="post" action="/login">
        <s:textfield name="map['1'].username" label="用户名"/>
        <s:textfield name="map['1'].age" label=" age"/>
        <s:textfield name="map['2'].username" label="用户名"/>
        <s:textfield name="map['2'].age" label=" age"/>
        <s:submit value="提交"/>
    </s:form>
  </body>

map['1'].username表示为Action实例的map属性添加一条信息:key为1,key为1的value值为walker的username属性的值为该文本框的值。age属性类似。

四、 Custom Type Conversion In the previous subsection, we were able to accomplish conversions to non-basic types using the ognl syntax, but essentially called Struts' default converter. Although we can do most type conversions using ognl syntax, there are extreme cases where this approach will not solve the problem, at which point we can consider a custom type converter to resolve the type conversion. To customize a type converter you must inherit the TypeConverter interface and implement its only method.

public abstract Object convertValue(Map<String, Object> paramMap, Object paramObject1, Member paramMember, String paramString, Object paramObject2, Class paramClass);

The method is quite complex, with seven parameters alone. The good thing is that the framework provides us with a default implementation class: the DefaultTypeConverter. This abstract class implements the TypeConverter interface and implements some methods by default, so we only need to override a method of this class when we customize our own type converter, which greatly reduces our development cost. We can of course go in and take a brief look at the internal structure of the DefaultTypeConverter abstract class: the

public Object convertValue(Map<String, Object> context, Object target, Member member, String propertyName, Object value, Class toType)
  {
    return convertValue(context, value, toType);
  }
public Object convertValue(Map<String, Object> context, Object value, Class toType)
  {
    return convertValue(value, toType);
  }
public Object convertValue(Object value, Class toType)
{
......
}

代码比较多,只贴出一些关键性的代码。该抽象类为我们提供了三个convertValue方法重载,他们之间的关系就是:参数多的重载调用参数少的。最后convertValue(Object value, Class toType)方法提供了默认实现,如果目标类型toType是我们上述介绍的几种基本类型,那么直接将value转换成该类型返回,如果value是个数组类型并且目标类型toType也是个数组类型,那么会获取value中的每个元素递归的调用该方法,为当前元素实现类型转换,最后返回toType类型。这里可能文字说明不能很明朗的会意你,你需要辅助着源代码。

对于默认的实现,我们还是不能完成某些自定义类型的转换,毕竟它只是一个默认实现。因为当系统无法使用默认类型转换器实现类型的转换的时候就会去查找是否有自定义的类型转换器,有则会自动调用convertValue最多参数的重载。所以我们可以重写convertValue的任意一个重载来完成 Custom Type Conversion器。下面我们看一段代码:

public class WalkerConvert extends DefaultTypeConverter {

    public Object convertValue(Object value, Class toType){
        if(toType == Walker.class){
            String[] params = (String[])value;
            Walker w = new Walker();
            String[] user = params[0].split(",");
            w.setUsername(user[0]);
            w.setAge(Integer.valueOf(user[1]));
            return w;
        }
        return null;
    }
}

Here we define a WalkerConvert class that inherits from DefaultTypeConverter and overrides that convertValue method. This method has two arguments, the first indicating the original type and the second indicating the target type. Here you need to do a little clarification on the first parameter value, the value of the parameter is actually an array of String, in general our parameters are stored in the element with index position 0, the rest of the element content only when the form is a drop-down box will be passed to all the options in the drop-down box (if you do not use the drop-down box is generally only used to the first element of the array). In the above code, we separated the incoming string by comma, the first half is the value of username and the second half is the value of age, and we look at the following result graph.

When we submit the string we fill in from the form, to the Action after the default converter can not complete the automatic conversion, so the framework to find out if there is a custom converter, after finding the call convertValue returns the result is the value of the property walker, and finally we output the walker property in the index page of the two sub-properties. The operations in this method have already been described and we will not repeat them here. Of course there are some questions here, such as: where should this WalkerConvert defined be placed, and how is it loaded by the web application? etc. We will explain these issues in detail in the next subsection.

V. Registration Type Converter With the questions from the previous subsection in mind, let's see how we can let the web container know about our custom converter and look up our own defined converter when we can't use the default converter to achieve the conversion. There are three main ways to register a type converter.

  • Registering a type converter in a local scope
  • 在全局范围内注册一个类型转换器
  • Registering a type converter using annotations

Partially registering a type converter will actually only work for an Action's properties. First we need to create a local type conversion file, which has the following naming convention.

ActionName-conversion.properties

For example, if we have an attribute of type Walker in LoginAction and we need to register a converter for that Action, name it as follows.

LoginAction-conversion.properties

This is the filename of the file and for the contents of the file, for example, if we need to register a converter for the Walker type, the following line of code could be added to the above file.

// Property name = location of the converter class
walker=MyPackage.WalkerConvert

One last thing to add is that the file created should be located under the same package as the corresponding Action, which is easy for the framework to search.

如果想要注册一个全局范围的类型转换器,那么对于该应用的任意一个Action中,只要存在指定的属性,都会调用该转换器实现转换,这是与局部转换器不同之处。注册全局类型转换器需要提供一个文件,该文件名称如下:

xwork-convertion.properties

The code for registering a type converter for a property is the same, except that the file can be used globally. The above is a brief introduction to the two ways of registering type converters, and it is also very simple to register using annotations. At this point, we know that once the string passed in on the form page cannot be automatically converted to the appropriate type by the default converter, then the appropriate custom converter will be looked up to return the value of that property.

VI. Error handling of type conversions 最后有关类型转换这块还有一个错误处理的内容没有介绍,其实框架为我们在拦截器栈中注册了一个拦截器:convertionError。该拦截器专门用于拦截类型转换异常,一旦该拦截器拦截到异常产生则会封装所有的异常信息到ActionContext中,然后跳转到input处理结果页面,所以一般我们在为Action添加处理结果的时候会为其添加一个input页面。下面看一个错误处理的示例:

//input.jsp
<html>
  <head>
    <title></title>
  </head>
  <body>
    <h1>this is the input page</h1>
  </body>
</html>

We just need to add an input result for the LoginAction, which encapsulates the error message and jumps to the input page when a type conversion failure occurs. The following are some screenshots of the program in action.

If we pass the second parameter as a string type, a type conversion error is bound to occur, and we see the result redirected to the input page.

So far, we have briefly introduced struts2 related to type conversion, some places to understand the poor summary, I hope not to be stingy.


Recommended>>
1、Xian University of Engineering successfully completed the acceptance of the legal corpus project
2、Junior your citation could be less
3、Japanese cabinet passes bill to ease restrictions in areas such as artificial intelligence
4、The market size will break 25 billion AI medical how is the landing situation
5、Baidu driverless car experience

    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号