java框架之Struts2(2)-訪問Servlet API及請求數據封裝

準備

爲後面測試示例編寫代碼及配置以下:html

package com.zze.bean;

import java.util.Date;

public class User {
    private String name;
    private Integer age;
    private Date birthday;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }
}
com.zze.bean.User
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
        "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
    <constant name="struts.devMode" value="true"/>
    <package name="test" extends="struts-default" namespace="/">
        <action name="*" class="com.zze.action.{1}Action">
            <result>/show.jsp</result>
        </action>
    </package>
</struts>
struts.xml
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
v1:${requestScope.key}
v2:${sessionScope.key}
v3:${applicationScope.key}
show.jsp

Servlet API的訪問

在使用 Struts2 的過程當中,會發現 Struts2 和 Servlet 的 API 是解耦合的。在實際開發中,常常用到 Servlet 的 API,好比將信息保存到 Session 中、使用 response 響應一些內容等等..,這些都涉及到對 Servlet 的 API 的訪問。java

解耦合方式

package com.zze.action;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

import java.util.Arrays;
import java.util.Map;

public class Test1Action extends ActionSupport {

    /**
     * 解耦合方式
     */
    @Override
    public String execute() {
        ActionContext context = ActionContext.getContext();
        // 獲取請求參數
        Map<String, Object> parameters = context.getParameters();
        for (String key : parameters.keySet()) {
            String[] values = (String[]) parameters.get(key);
            System.out.println(String.format("key:%s value:%s", key, Arrays.toString(values)));
        }
        // 存放參數到域對象
        context.put("key", "value from context.put"); // request.setAttribute("key","value from context.put")
        context.getSession().put("key", "value from context.getSession().put"); // session.setAttribute("key","value from context.getSession().put")
        context.getApplication().put("key", "value from context.getApplication().put"); // application.setAttribute("key","value from context.getApplication().put")
        return SUCCESS;
    }
}

com.zze.action.Test1Action

原生Servlet的API

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Map;

public class Test2Action extends ActionSupport {

    @Override
    public String execute() throws Exception {
        // 獲取原生 request 對象
        HttpServletRequest request = ServletActionContext.getRequest();
        Map<String, String[]> parameterMap = request.getParameterMap();
        for (String key : parameterMap.keySet()) {
            String[] values = (String[]) parameterMap.get(key);
            System.out.println(String.format("key:%s value:%s", key, Arrays.toString(values)));
        }
        // 存放數據到域對象
        request.setAttribute("key","value from request.setAttribute");
        request.getSession().setAttribute("key","value from request.getSession().setAttribute");
        // ServletActionContext.getServletContext()
        request.getServletContext().setAttribute("key","value from request.getServletContext().setAttribute");
        return super.execute();
    }
}

com.zze.action.Test2Action

接口注入 

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import org.apache.struts2.util.ServletContextAware;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Map;

/**
 * 接口注入的方式須要實現相應接口
 */
public class Test3Action extends ActionSupport implements ServletRequestAware, ServletResponseAware, ServletContextAware {
    private HttpServletRequest request;  // 接收注入的原生 request
    private HttpServletResponse response; // 接收注入的原生 response
    private ServletContext servletContext; // 接收注入的原生 servletContext

    @Override
    public String execute() throws Exception {
        // 接收請求參數
        Map<String, String[]> parameterMap = request.getParameterMap();
        for (String key : parameterMap.keySet()) {
            String[] values = (String[]) parameterMap.get(key);
            System.out.println(String.format("key:%s value:%s", key, Arrays.toString(values)));
        }
        // 存放數據到域對象
        request.setAttribute("key", "value from request.setAttribute");
        request.getSession().setAttribute("key", "value from request.getSession().setAttribute");
        servletContext.setAttribute("key", "value from servletContext.setAttribute");
        return super.execute();
    }

    @Override
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }

    @Override
    public void setServletResponse(HttpServletResponse response) {
        this.response = response;
    }

    @Override
    public void setServletContext(ServletContext context) {
        this.servletContext = context;
    }
}

com.zze.action.Test3Action

請求數據封裝

Struts2 是一個 web 層框架,提供了請求數據封裝的功能。而它的提供了以下兩種方式進行請求數據的封裝:web

屬性驅動

提供set方法

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;

import java.util.Date;

public class Test4Action extends ActionSupport {
    private String name;
    private Integer age;
    private Date birthday;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String execute() throws Exception {
        System.out.println(name);
        System.out.println(age);
        System.out.println(birthday);
        return NONE;
    }
}

com.zze.action.Test4Action

表達式方式

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;
import com.zze.bean.User;

/**
 * 請求參數的參數名需按指定格式
 */
public class Test5Action extends ActionSupport {
    // 給接收參數的對象提供 get 方法
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String execute() throws Exception {
        System.out.println(user);
        return NONE;
    }
}

com.zze.action.Test5Action : 封裝到 JavaBean
package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;

import java.util.Arrays;
import java.util.List;

/**
 * 請求參數的參數名需按指定格式
 */
public class Test6Action extends ActionSupport {
    public List<String> names;

    public List<String> getNames() {
        return names;
    }

    public void setNames(List<String> names) {
        this.names = names;
    }

    @Override
    public String execute() throws Exception {
        System.out.println(Arrays.toString(names.toArray()));
        return NONE;
    }
}

com.zze.action.Test6Action : 封裝到 List<String>
package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;
import com.zze.bean.User;

import java.util.List;

public class Test7Action extends ActionSupport {
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    @Override
    public String execute() throws Exception {
        for (User user : users) {
            System.out.println(user);
        }
        return NONE;
    }
}

com.zze.action.Test7Action : 封裝到 List<JavaBean>
package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;

import java.util.Arrays;
import java.util.Map;

public class Test8Action extends ActionSupport {
    private Map<String,Object> map;

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    @Override
    public String execute() throws Exception {
        for (String key : map.keySet()) {
            String valStr=  Arrays.toString((String[])map.get(key));
            System.out.println(String.format("%s:%s",key,valStr));
        }
        return NONE;
    }
}

com.zze.action.Test8Action : 封裝到 Map<String,String[]>
package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;
import com.zze.bean.User;

import java.util.Map;

public class Test9Action extends ActionSupport {
    private Map<String, User> map;

    public Map<String, User> getMap() {
        return map;
    }

    public void setMap(Map<String, User> map) {
        this.map = map;
    }

    @Override
    public String execute() throws Exception {
        for (String key : map.keySet()) {
            System.out.println(String.format("%s : %s", key, map.get(key)));
        }
        return NONE;
    }
}

com.zze.action.Test9Action : 封裝到 Map<String,JavaBean>

模型驅動

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.zze.bean.User;

/**
 * 實現 ModelDriven 接口
 */
public class Test10Action extends ActionSupport implements ModelDriven<User> {
    private User user = new User();

    @Override
    public User getModel() {
        return user;
    }

    @Override
    public String execute() throws Exception {
        System.out.println(user);
        return NONE;
    }
}

com.zze.action.Test10Action

補充

結果頁配置

全局結果頁

全局結果頁指的是:在包中配置一次,其它在這個包中全部 action 只要返回了對應 name 值,就均可以跳轉到全局結果頁配置的頁面。apache

在 struts.xml 的 package 標籤下添加以下節點便可配置全局結果頁:session

<!--全局結果頁配置-->
<global-results>
    <result name="success">/success.jsp</result>
</global-results>

局部結果頁

局部結果頁指的是:所配置的結果只對當前所在 action 有效。app

在 struts.xml 的 package>aciton 下配置:框架

<!--局部結果頁配置-->
<action name="*" class="com.zze.action.{1}Action">
    <result>/show.jsp</result>
</action>

result標籤配置

result 標籤用於配置頁面的跳轉。jsp

屬性:ide

  • name:邏輯視圖名稱,與 action 返回值對應。
  • type:頁面跳轉類型。
    type 的取值有不少,在 struts2-core-2.3.37.jar!/struts-default.xml 的 package 下有定義:
    <result-types>
        <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
        <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
        <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
        <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
        <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
        <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
        <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
        <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
        <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
        <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
        <result-type name="postback" class="org.apache.struts2.dispatcher.PostbackResult" />
    </result-types>
    struts-default.xml > package > result-types

    經常使用類型有以下幾個:post

        dispatcher:默認值,請求轉發,從 當前 action 到 jsp。

        redirect:從當前 action 重定向到 jsp。

        chain:請求轉發,從當前 action 轉發到另外一個 action。

        redirectAction:從當前 action 重定向到另外一個 action。

        stream:返回流,文件下載。

INPUT邏輯視圖

在 Action 接口中提供了 5 個靜態字段,以下:

public static final String SUCCESS = "success"; // 操做成功
public static final String NONE = "none"; // 操做成功不跳轉
public static final String ERROR = "error"; // 操做失敗
public static final String INPUT = "input"; // 一般在表單提交請求參數驗證失敗時返回,標識需從新輸入提交
public static final String LOGIN = "login"; // 返回到登陸頁

"input" 返回值在 Struts2 內置攔截器中就有用到,先看以下狀況:

package com.zze.action;

import com.opensymphony.xwork2.ActionSupport;

public class Test11Action extends ActionSupport {
    private Integer age;

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String execute() throws Exception {
        System.out.println(age);
        return NONE;
    }
}

com.zze.action.Test11Action

在上述 action 中使用屬性驅動的方式接受一個 Integer 類型的年齡字段,而參數提交時提交的是 "aa","aa" 明顯是不能夠轉成 Integer 類型的,此時 Struts2 就會返回錯誤以下:

No result defined for action com.zze.action.Test11Action and result input - action - file:/WEB-INF/classes/struts.xml:13:51

即:沒有爲 action 定義一個 name="input" 的 result 。爲何 Struts2 會返回這個結果呢?

首先咱們知道 Struts2 內部默認是有一個攔截器棧的,在 struts2-core-2.3.37.jar!/struts-default.xml 中能夠看到,以下:

<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<!--接收參數-->
<interceptor-ref name="params"/>
<!--類型轉換-->
<interceptor-ref name="conversionError"/>
<!--數據校驗-->
<interceptor-ref name="validation">
    <param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
    <param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
<interceptor-ref name="deprecation"/>
</interceptor-stack>

在請求 action 時該棧下的攔截器會依次執行,而每一個攔截器都有它自身的功能。如 params 攔截器用來接收參數、conversionError 攔截器用來類型轉換、validation 攔截器用來數據校驗。

而 Struts2 有一個專門存放錯誤的區域,當這幾個操做出錯時,Struts2 就會將對應錯誤信息存到該錯誤區域。接着在 workflow 攔截器中就會檢查錯誤區域是否有錯誤,若是有,則返回 "input" 邏輯視圖,若是沒有則繼續執行後續攔截器及 action。workflow 對應攔截器部分源碼以下:

@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
    Object action = invocation.getAction();

    if (action instanceof ValidationAware) {
        ValidationAware validationAwareAction = (ValidationAware) action;
        // 若是錯誤區有錯誤
        if (validationAwareAction.hasErrors()) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Errors on action [#0], returning result name [#1]", validationAwareAction, inputResultName);
            }

            String resultName = inputResultName; // "input"
            resultName = processValidationWorkflowAware(action, resultName);
            resultName = processInputConfig(action, invocation.getProxy().getMethod(), resultName);
            resultName = processValidationErrorAware(action, resultName);

            return resultName;
        }
    }

    return invocation.invoke();
}
com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor#doIntercept

瞭解了攔截器返回 "input" 邏輯視圖的緣由,咱們就能夠對症下藥了,在 struts.xml 中 action 標籤下添加一個 "input" 的結果:

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

而在 jsp 中咱們能夠經過 Struts2 提供的標籤輸出錯誤信息:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<s:fielderror/>
</body>
</html>
input.jsp

再次請求,結果以下:

相關文章
相關標籤/搜索