Struts2 第五讲 -- Struts2与Servlet的API解耦

Posted Abri

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2 第五讲 -- Struts2与Servlet的API解耦相关的知识,希望对你有一定的参考价值。

  为了避免与 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解耦

  • ActionContext Action 执行的上下文对象, ActionContext 中保存了 Action 执行所需要的所有对象, 包括 parameters, request, session, application .
static ThreadLocal actionContext = new ActionContextThreadLocal()
(ActionContext) actionContext.get();来获取
  • 获取 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);
  • 获取 HttpSession 对应的 Map 对象:
public Map getSession()
  • 获取 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解耦的主要内容,如果未能解决你的问题,请参考以下文章

[struts2学习笔记] 第五节 编写struts2的action代码

架构设计第五讲:数据巡检系统的设计与应用

第五讲 树状数组与线段树 未完结

大数据技术原理与应用:第五讲NoSQL数据库

雅思基础课程语法基础课程-第五讲

Git应用详解第五讲:远程仓库Github与Git图形化界面