1. 程式人生 > >Struts2 第五講 -- Struts2與Servlet的API解耦

Struts2 第五講 -- Struts2與Servlet的API解耦

ces user gets namespace jsp throw 方法 ioc 取數據

  為了避免與 Servlet API 耦合在一起, 方便 Action 單元測試, Struts2 HttpServletRequest, HttpSession ServletContext 進行了封裝, 構造了 3 Map 對象來替代這 3 個對象, Action 中可以直接使用 HttpServletRequest, HttpSession, ServletContext 對應的 Map 對象來保存和讀取數據。這裏大家註意,struts1是沒有提供與ServletAPI解耦的。

1.Struts2如何獲取request、response、session、ServletContext

  在Struts2開發中,除了將請求參數自動設置到Action的字段中,我們往往也需要在Action裏直接獲取請求(Request)或會話(Session)的一些信息,甚至需要直接對JavaServlet Http的請求(HttpServletRequest),響應(HttpServletResponse)操作.Struts2提供了三種用於獲取這些對象的方法,下面來一一介紹一下;

非IOC方法:通過ActionContext,ServletActionContext類直接獲取,實現與Servlet解耦

  • l ActionContext Action 執行的上下文對象, ActionContext
    中保存了 Action 執行所需要的所有對象, 包括 parameters, request, session, application .
static ThreadLocal actionContext = new ActionContextThreadLocal()
(ActionContext) actionContext.get();來獲取
  • l 獲取 HttpServletRequest 對應的 Map 對象:public Object get(Object key): ActionContext 類中沒有提供類似 getRequest() 這樣的方法來獲取 HttpServletRequest
    對應的 Map 對象. 要得到 HttpServletRequest 對應的 Map 對象, 可以通過為 get() 方法傳遞 “request” 參數實現
ActionContext.get(org.apache.struts2.StrutsStatics.HTTP_REQUEST);
  • 獲取HTTPServletResponse,該獲取同request相似;
ActionContext.get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);
  • l 獲取 HttpSession 對應的 Map 對象:
public Map getSession()
  • l 獲取 ServletContext 對應的 Map 對象:
public Map getApplication()

測試代碼:獲取requestresponsesessionapplication對象

@SuppressWarnings("serial")
public class ContextAction extends ActionSupport{
    
    @Override
    public String execute() throws Exception {
        System.out.println("歡迎訪問ContextAction中的execute方法!");
        /**request對象(與servletAPI解耦的方式)*//*
        ActionContext.getContext().put("username", "request_username");
        *//**session對象(與servletAPI解耦的方式)*//*
        ActionContext.getContext().getSession().put("username", "session_username");
        *//**application對象(ServletContext對象)(與servletAPI解耦的方式)*//*
        ActionContext.getContext().getApplication().put("username", "application_username");
        */
        //使用與ServletActionContext的方式操作上述
ServletActionContext.getRequest().setAttribute("username", "request_username");
ServletActionContext.getServletContext().setAttribute("username", "application_username");
ServletActionContext.getRequest().getSession().setAttribute("username", "session_username"); System.out.println("request:"+ServletActionContext.getRequest()); System.out.println("response:"+ServletActionContext.getResponse()); System.out.println("session:"+ServletActionContext.getRequest().getSession()); System.out.println("servletContext:"+ServletActionContext.getServletContext()); //Action接口中常量 SUCCESS="success" return SUCCESS; } }

技術分享圖片

技術分享圖片

IOC方法:實現指定接口,由struts框架運行時註入,即使用Struts2 Aware攔截器

Action 類通過可以實現某些特定的接口, Struts2 框架在運行時向 Action 實例註入 parameters, request, session application 對應的 Map 對象:

測試代碼::獲取requestresponsesessionapplication對象

@SuppressWarnings("serial")
public class ContextActionTwo extends ActionSupport implements ServletRequestAware,
        ServletContextAware,ServletResponseAware,SessionAware{
    private HttpServletRequest request;
    private HttpServletResponse response;
    private Map<String, Object> session;
    private ServletContext application;//註意可以實現接口ApplicationAware同樣的功能
    //使用第二種方式
    @Override
    public String execute() throws Exception {
        System.out.println("歡迎訪問ContextActionTwo中的execute()!");
        //向作用域中存值
        request.setAttribute("username", "request_username2");
        session.put("username", "session_username2");
        application.setAttribute("username", "application_username2");
        return SUCCESS;
    }

    @Override
    public void setSession(Map<String, Object> session) {
        // TODO Auto-generated method stub
        this.session = session;
    }

    @Override
    public void setServletResponse(HttpServletResponse response) {
        // TODO Auto-generated method stub
        this.response = response;
    }

    @Override
    public void setServletContext(ServletContext context) {
        // TODO Auto-generated method stub
        this.application = context;
    }

    @Override
    public void setServletRequest(HttpServletRequest request) {
        // TODO Auto-generated method stub
        this.request = request;
    }
}
attr.jsp  

<body>123 ${requestScope.username}<br> ${sessionScope.username}<br> ${applicationScope.username}<br> </body>

技術分享圖片

struts-context.xml文件的配置:

<struts>
    <package name="context" namespace="/context" extends="struts-default">
        <default-action-ref name="contextAction_test"></default-action-ref>
        
        <action name="contextAction_test" class="cn.youric.you.two_context.ContextAction">
            <result name="success">/context/success.jsp</result>
        </action>
        <action name="contextAction02_test" class="cn.youric.you.two_context.ContextActionTwo">
            <result name="success">/context/attr.jsp</result>
        </action>
    </package>
</struts>

別忘了include

2.聊聊ActionContext

  ActionContext(com.opensymphony.xwork.ActionContext)是Action執行時的上下文,上下文可以看作是一個容器(其實我們這裏的容器就是一個Map而已),它存放的是Action在執行時需要用到的對象. 一般情況, 我們的ActionContext都是通過: ActionContext context = (ActionContext) actionContext.get();來獲取的.我們再來看看這裏的actionContext對象的創建:

static ThreadLocal<ActionContext> actionContext = new ThreadLocal<>();

  ThreadLocal可以命名為"線程局部變量",它為每一個使用該變量的線程都提供一個變量值的副本,使每一個線程都可以獨立地改變自己的副本,而不會和其它線程的副本沖突.這樣,我們ActionContext裏的屬性只會在對應的當前請求線程中可見,從而保證它是線程安全的.

3. ServletActionContext和ActionContext聯系

  ServletActionContext和ActionContext有著一些重復的功能,在我們的Action中,該如何去抉擇呢?我們遵循的原則是:如果ActionContext能夠實現我們的功能,那最好就不要使用ServletActionContext,讓我們的Action盡量不要直接去訪問Servlet的相關對象.

  註意:在使用ActionContext時有一點要註意: 不要在Action的構造函數裏使用ActionContext.getContext(),因為這個時候ActionContext裏的一些值也許沒有設置,這時通過ActionContext取得的值也許是null;同樣,HttpServletRequest req = ServletActionContext.getRequest()也不要放在構造函數中,也不要直接將req作為類變量給其賦值。至於原因,我想是因為前面講到的static ThreadLocal actionContext = new ActionContextThreadLocal(),從這裏我們可以看出ActionContext是線程安全的,而ServletActionContext繼承自ActionContext,所以ServletActionContext也線程安全,線程安全要求每個線程都獨立進行,所以req的創建也要求獨立進行,所以ServletActionContext.getRequest()這句話不要放在構造函數中,也不要直接放在類中,而應該放在每個具體的方法體中(eg:login()、queryAll()、insert()等),這樣才能保證每次產生對象時獨立的建立了一個req。

Struts2 第五講 -- Struts2與Servlet的API解耦