结果处理方式、在Action中获取原生ServletAPI、获取前端参数
结果跳转方式
在struts.xml文件中,配置result标签的type类就可以选择处理结果的方式,struts2提供了如下处理结果的方式,可以在struts2核心包 --> struts-default.xml文件中找到
<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>
这里介绍四种,分别是转发、重定向、转发到Action、重定向到Action,如果不对type属性进行配置,会默认type属性为转发
转发dispatcher(默认)
<action name="helloAction" class="com.jinxin.action.HelloAction" method="hello"> <result name="success" type="dispatcher">/hello.jsp</result> </action>
由struts-default.xml文件中的配置可知,转发是交给 org.apache.struts2.dispatcher.ServletDispatcherResult 类处理的,进入这个类,有一个 doExecute() 方法,这就是处理转发的方法,在该方法中,有如下几条代码
HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation); dispatcher.forward(request, response);
分别是获取request对象,获取response对象,获取转发对象,转发,这很显然就是在Servlet中实现的转发的代码,只是struts2已经将它们封装好了,提高了代码的重用性
重定向redirect
<action name="helloAction" class="com.jinxin.action.HelloAction" method="hello"> <result name="success" type="redirect">/hello.jsp</result> </action>
同样,可以进redirect的处理类 org.apache.struts2.dispatcher.ServletRedirectResult 看看它是怎样实现重定向的
同样,在该类中也有一个 doExecute() 方法,很显然,这个方法就是处理重定向的,在这个方法中,先是获取了request跟response对象,然后进行了一些判断跟路径的拼接处理,最后将拼接好的路径和response对象传进了一个 sendRedirect() 方法,其中核心的代码如下
HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST); HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE); sendRedirect(response, finalLocation);
进入这个 sendRedirect() 方法,发现该方法执行了 response.sendRedirect(finalLocation); ,显然,这同样也是Servlet中重定向的代码。。。
转发到Action
<action name="helloAction" class="com.jinxin.action.HelloAction" method="hello"> <result name="success" type="chain"> <param name="actionName">Demo1Action</param> <param name="namespace">/</param> </result> </action>
actionName表示转发到哪个Action类,namespace表示那个Action所在的命名空间
重定向到Action
<action name="helloAction" class="com.jinxin.action.HelloAction" method="hello"> <result name="success" type="redirectAction"> <param name="actionName">Demo1Action</param> <param name="namespace">/</param> </result> </action>
actionName表示重定向到哪个Action类,namespace表示那个Action所在的命名空间
在Action中获取ServletAPI
在Action中获取ServletAPI主要通过一个数据中心ActionContext,这个ActionContext的生命周期与request一致,每次请求时,都会创建一个与请求对应的ActionContext对象,请求处理完ActionContext销毁
那么,如何去获取这个数据中心呢?struts2的设计是,将ActionContext对象创建好后,将ActionContext与当前线程绑定,当需要获取ActionContext时,只需要从ThreadLocal中获取即可
其实ActionContext是一个Map,它通过键值对存放内容,其中存放的request域,application域跟session域同样也是Map,那么下面就三种方式讨论如何获取这些域
通过ActionContext获得(常用)
获取session域
Map<String, Object> sessionScope = ActionContext.getContext().getSession();
获取application域
Map<String, Object> applicationScope = ActionContext.getContext().getApplication();
获取request域
获取request域不能像上面那样通过 getRequest() 方法获取,在ActionContext中并没有提供这个方法,那么用该如何去获取呢?很简单,上面有说过ActionContext是一个Map映射,那么自然就可以通过键名去获取值了,下面就是通过request键名去获取的request域
Map<String, Object> requestScope = ActionContext().getContext().get("request");
虽然获取了request域,但是并不推荐使用,上面提到过ActionContext的生命周期与request中的一致,因此将数据存在ActionContext域中与request域中是一样的,所以更推荐使用ActionContext域
ActionContext.getContext().put("name", "Scarlett");
那么,既然存储的地方变了,自然就会有这样一种担心——在取值的时候应该怎么办?其实并不需要有这种担心,因为在取值的时候会先到request原生域中找,如果没有找到就会继续到ActionContext域中查找,即取值的方式不会改变
同ServletContext(为了跟Servlet解耦,所以不推荐使用)
获取原生的request
HttpServletRequest hr = ServletActionContext.getRequest();
获取原生的response
HttpServletResponse hp = ServletActionContext.getResponse();
获取原生的ServletContext
ServletContext sc = ServletActionContext.getServletContext();
获取原生的session
HttpSession session = request.getSession();
这看似是一种新的方式,但实则跟上一种没什么区别,何出此言,我们可以进入到 ServletActionContext 类中,查看其中的某一个方法,这里以 getRequest() 方法为例
1 public static HttpServletRequest getRequest() { 2 return (HttpServletRequest) ActionContext.getContext().get(HTTP_REQUEST); 3 }
可以看到,上面的 getRequest() 方法正是通过键名向ActionContext中取值的方式获取的request对象,这与第一种方式难道不是一致的吗
通过接口获得
第三种方法是通过实现相关的 xxxAware 接口来获得的,比如想要获取request对象可以实现 ServletRequestAware 接口
public class GetAPI extends ActionSupport implements ServletRequestAware{ private HttpServletRequest request; @Override public void setServletRequest(HttpServletRequest request) { // TODO Auto-generated method stub this.request = request; } }
上述代码就获取一个request对象,若想要获取其他对象,以相同的方法实现对应的Aware接口即可
那么,如此神奇的方式怎样实现呢?正是通过struts2的核心拦截器实现的,同样可以进入到 struts-default.xml 文件中,找到设置拦截器的部分
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/> <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/> <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/> <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/> <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/> <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/> <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" /> <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" /> <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" /> <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/> <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/> <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/> <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/> <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/> <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/> <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/> <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/> <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/> <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/> <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/> <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/> <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/> <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/> <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/> <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/> <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/> <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" /> <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" /> <interceptor name="datetime" class="org.apache.struts2.interceptor.DateTextFieldInterceptor" /> <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" /> <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" /> <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" /> <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" /> <interceptor name="deprecation" class="org.apache.struts2.interceptor.DeprecationInterceptor" />
其中name属性为 servletConfig 的,就是实现获取这些对象的拦截器,进入到相应的class属性指定的类中,可以看到一个 intercept() 方法,就是用来获取这些对象的,以request为例
final Object action = invocation.getAction(); final ActionContext context = invocation.getInvocationContext(); if (action instanceof ServletRequestAware) { HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST); ((ServletRequestAware) action).setServletRequest(request); }
在 intercept() 方法中,先通过 invocation 获取了本次请求的Action以及ActionContext,然后判断该Action是否实现了ServletRequestAware接口,如果实现了,就通过键名从ActionContext中取出request对象,通过 setServletRequest() 方法设置request,而这个 setServletRequest() 方法不正是先前实现ServletRequestAware接口重写的方法吗,而request也就自然而然的设置给了该Action类中的 this.request
总结:其实虽然是分了三种方式去获取ServletAPI,但是当研究了底层的代码后,发现,这三种获取的方式最终都是通过键名去ActionContext中取值的,实际上可以看做是一种方式,只是外表不一样罢了,但是这三种方式任然各有优劣,比如第二种与Servlet耦合太深,第三种在笔者看来太过麻烦,因此更推荐使用第一种方式。。。
获取前端参数
一个网站,经常会免不了用户登录、用户注册,那么这时候就需要从前端获取到用户填写的数据,那么在struts2中如何去获取呢?下面分别介绍四种方式获取这些参数
属性驱动获取参数
顾名思义,属性驱动获取参数就是在Action中准备与input标签中name属性同名的一个属性去接收参数,具体代码如下
jsp页面
<body> <form action="${pageContext.request.contextPath}/hello/paramAction.do" method="post"> <input type="text" name="name" /> <input type="submit" value="提交" /> </form> </body>
在Action类中
public class ParamAction extends ActionSupport{ private String name; // 添加一个与前端input标签name属性同名的属性即可 public String test() { System.out.println(name); // 打印属性 return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
在上面的Action类中,接受参数使用了成员变量,但是这在Servlet中是不允许的,因为Servlet是线程不安全的,就这么一个成员变量,而用户却有很多人,除非能保证同一时间只有一个用户去使用这个变量,不然,当多个用户同时使用就会乱套。而为什么在Action中又能使用了呢?原因就是Action是线程安全的,每次请求Action时,都会新建Action的实例对象,这就相当于每个用户都有自己的成员变量,互不干扰。
对象驱动获得参数
像上面那样将属性都放到Action类中,属性跟业务写在一块儿让笔者觉得非常的乱,看起来不舒服,影响体验,甚至可能因此出错,那么,很自然的就会想到,能不能将这些属性单独的放到一个类中,而在Action类中只需要实例化这个类就可以了呢?当然是有的,这就是对象驱动获取参数
User类
将属性都放在一个类中,这个类取名为User
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; } public String toString() { return name + "===>" + age + "===>" + birthday; } }
Action类
在Action类中只需要准备一个User类型的属性即可
public class ParamAction extends ActionSupport{ private User user; public String test() { System.out.println(user); // 打印获取的参数 return "success"; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
jsp页面
在jsp页面中的input标签的name属性同样要与User类中的各个属性名称相同,但是这次需要在它们前面加上Action中的user属性名,在将它们通过句点符连接起来
<body> <form action="${pageContext.request.contextPath}/hello/paramAction.do" method="post"> <input type="text" name="user.name" /> <!-- 这里是 对象名.对象的属性名 --> <input type="text" name="user.age" /> <input type="text" name="user.birthday" /> <input type="submit" value="提交" /> </form> </body>
模型驱动获取参数
上面通过对象驱动获取参数显然方便多了,但是在input标签中的name属性的写法显然跟平时的习惯不太一样,那么怎样才能不写前面的user呢?这就需要用到模型驱动获取参数
User类
User类的写法同对象获取参数一样,不做赘述
Action类
在Action类中需要实现ModelDriven接口,然后实现一个 getModel() 方法,方法中返回user对象,由于此时的user不是一个属性,没有get、set方法,所以需要手动去实例化,即 private User user = new User();
public class ParamAction extends ActionSupport implements ModelDriven<User>{ // 实现ModelDriven接口,这里的泛型写User类 private User user = new User(); public String test() { System.out.println(user); return "success"; } // 实现getModel()方法,返回user对象 @Override public User getModel() { // TODO Auto-generated method stub return user; } }
jsp页面
在使用了模型驱动后,此时在input标签中就不需要写user了
<body> <form action="${pageContext.request.contextPath}/hello/paramAction.do" method="post"> <input type="text" name="name" /> <!-- 这里是 对象名.对象的属性名 --> <input type="text" name="age" /> <input type="text" name="birthday" /> <input type="submit" value="提交" /> </form> </body>
集合类型封装参数
除了上面的通过属性获取参数以外,还可以使用集合获取参数,可以用List和Map获取
使用List获取
使用集合获取跟使用属性获取其实是差不多的,同样是在Action类中准备一个List类型的属性存放参数即可
Action类
public class ParamAction extends ActionSupport{ private List<String> list; public String test() { System.out.println(list); return "success"; } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } }
jsp页面
<body> <form action="${pageContext.request.contextPath}/hello/paramAction.do" method="post"> <input type="text" name="list" /> <input type="text" name="list" /> <input type="text" name="list" /> <input type="submit" value="提交" /> </form> </body>
如果在name中直接填写list,那么获取的参数会按照默认的顺序存放,当然,也可以指定参数存放的位置,比如 list[5] 就是将该参数存放到索引为5的位置,前面没有参数的位置以null填充
使用Map获取
使用Map获取跟使用list获取是一样的
Action类
public class ParamAction extends ActionSupport{ private Map<String, String> map; public String test() { System.out.println(map); return "success"; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } }
jsp页面
在input标签的name属性中,需要写键名,比如键名为name,那么应该写 map['name']
<body> <form action="${pageContext.request.contextPath}/hello/paramAction.do" method="post"> <input type="text" name="map['name']" /> <input type="text" name="map['age']" /> <input type="text" name="map['birthday']" /> <input type="submit" value="提交" /> </form> </body>