Struts2学习总结
一、配置文件问题
首先,javaweb项目在运行时首先会查web.xml文档,比如我们直接在服务器上运行某一个项目时,localhost:8080/helloworld会直接运行出该项目的index.jsp页面,那是因为在初始的web.xml文档中有这么一段
<welcome-file-list>
<welcome-file>index.jsp</welcome-file> </welcome-file-list>
我们大体基本可以明白,在xml文档中,默认的欢迎页面就是index.jsp如果不设置这个(当然,这里是默认的)或者将index.jsp的名字改掉,那么服务器就会报错误。
对于Struts2框架而言,需要加载FilterDispatcher,只要Web应用负责加载FilterDispatcher,FilterDispatcher将会加载Struts2框架。
因为Struts2将核心控制器设计成Filter,而不是一个普通Servlet。故为了让Web应用加载FilterDispatcher,只需要在web.xml文件中配置FilterDispatcher即可。
标准配置文件如下:
<?xml version="1.0"encoding="UTF-8"?>
<web-app id="WebApp_9"version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts Blank</display-name>
<filter>
<!-- 配置Struts2核心Filter的名字 -->
<filter-name>struts2</filter-name>
<!-- 配置Struts2核心Filter的实现类 -->
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
<init-param>
<!-- 配置Struts2框架默认加载的Action包结构,可以没有。 -->
<param-name>actionPackages</param-name>
<param-value>org.apache.struts2.showcase.person</param-value>
</init-param>
<!-- 配置Struts2框架的配置提供者类 -->
<init-param>
<param-name>configProviders</param-name>
<param-value>lee.MyConfigurationProvider</param-value>
</init-param>
</filter>
<!-- 配置Filter拦截的URL -->
<filter-mapping>
<!-- 配置Struts2的核心FilterDispatcher拦截所有用户请求 -->
<filter-name>struts2</filter-name>
<!-- /*在这里指的是根目录下所有的请求页面的路径-->
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
有了标准的web.xml文档,我们在部署运行web项目时,服务器先读取web.xml文档,这时通过过滤器所提供的<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
这样项目就知道,页面所提交的参数和请求都是由Struts2框架所提供的方法来处理,既然由Struts2框架来处理,那么Struts2是否也要有自己独立的xml文档来处理一系列的请求分类呢?
答案是肯定的,我们需要在src文件夹下建立struts.xml 来处理不同的请求
Struts2框架中核心组件就是Action、拦截器等,Struts2框架使用包来管理Action和拦截器等。每个包就是多个Action、多个拦截器、多个拦截器引用的集合。
在struts.xml文件中package元素用于定义包配置,每个package元素定义了一个包配置。它的常用属性有:
l name:必填属性,用来指定包的名字。
l extends:可选属性,用来指定该包继承其他包。继承其它包,可以继承其它包中的Action定义、拦截器定义等。
l namespace:可选属性,用来指定该包的命名空间。
<!DOCTYPE struts PUBLIC
"-//Apache SoftwareFoundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!—我们在编写时,需要时时更改代码,并且观察在服务器上的运行效果,这行代码保证了,我们在时时更改时,服务器可以在不重启的情况下可以动态更改执行-->
<constant name="struts.devMode" value="true" />
<!—这行保证的是页面在提交中文时,后台接收不出现乱码-->
<constantname="struts.i18n.encoding" value="GBK" /> <!--internationalization -->
<!-- struts2的action必须放在一个指定的包空间下定义 -->
<packagename="default" extends="struts-default" namespace=””>
<!-- 定义处理请求URL为login.action的Action -->
<actionname="login"class="org.qiujy.web.struts.action.LoginAction">
<!-- 定义处理结果字符串和资源之间的映射关系 -->
<resultname="success">/success.jsp</result>
<resultname="error">/error.jsp</result>
</action>
</package>
</struts>
上面是xml的一个基础配置,当然action只是一个简单的例子。后续会补充xml文档中新出现的常用标签。
二、Action总结
继承ActionSupport
在使用时需要继承ActionSupport方法,注意excute()的重写,但是也不一定必须使用或者重写该方法,在没使用的方法中要有return SUCCESS,来指定跳转页面
调用Action类时利用DMI(DynamicMethod Invocation)方式使用其方法
具体来看,我们在某一个Action类中写了几个方法,如果按照在xml中一次填写Action的类和方法就会产生大量的action,所以使用动态方法调用时就避开了这种麻烦
<a href="<%=context %>/user/userAdd">添加用户</a>
<br />
<ahref="<%=context %>/user/user!add">添加用户</a>
<br />
从上述代码就可以看懂,动态调用将不直接在xml文档中指定被调用Action的method而是转向在jsp页面中根据操作找到指定方法
Struts2中Action接收参数的方法主要有以下三种:
1.使用Action的属性接收参数:
a.定义:在Action类中定义属性,创建get和set方法;
b.接收:通过属性接收参数,如:userName;
c.发送:使用属性名传递参数,如:user1!add?userName=Magci;
2.使用DomainModel接收参数:
a.定义:定义Model类,在Action中定义Model类的对象(不需要new),创建该对象的get和set方法;
b.接收:通过对象的属性接收参数,如:user.getUserName();
c.发送:使用对象的属性传递参数,如:user2!add?user.userName=MGC;
3.使用ModelDriven接收参数:
a.定义:Action实现ModelDriven泛型接口,定义Model类的对象(必须new),通过getModel方法返回该对象;
b.接收:通过对象的属性接收参数,如:user.getUserName();
c.发送:直接使用属性名传递参数,如:user2!add?userName=MGC
通配符问题
在指定命名规则后利用通配符来实现同包下多个类一个action调用
如*_*可以在User_Add类和Manager_Add类可以根据jsp中所写的路径再经过查找对应包下的com.Add.{1}_{2}来对指定类进行操作,这样做也大大减少了action的冗余问题
获取web元素
获取web元素共有四种方法,但实际上常用的IOC原则也叫作依赖注入来获取page所传过来的session、application,request,response。
但是还是粗略的整理一下其他三种获取方式。
1.依赖于容器(ActionContext)获取Map类型的Web元素
2.依赖于容器(ServletActionContext)获取真实类型的Web元素
3.依赖于Struts2框架注入(IoC-控制反转)获取Map类型的Web元素(只用这种方法)
4.依赖于Struts2框架注入(IoC-控制反转)获取真实类型的Web元素(与第三种几乎相同,只是获取的Map元素不再使用,而是直接使用类来获取元素)
第一种方法:依赖于容器(ActionContext)获取Map类型的Web元素
【ActionContext】是Action执行的上下文,提供一系列相关方法用于访问保存在HttpServletRequest、HttpSession、ServletContext中的信息,并将其存储在Map中。
【ActionContext对象获取】 通过ActionConext类的静态方法getContext()
【ActionContext常用方法】
public Object get(String key):获取ActionContext中指定键名的元素对象
public Map<String, Object> getSession():获取session元素对象
public Map<String, Object> getApplication():获取application元素对象
【Map常用方法】
public void put(String key,Object value):向Map对象中添加一个具有键名标识的元素
public Object get(Object key):从Map对象中获取指定的元素
在Action中获取Web元素的方法】
1.获取ActionContext对象
ActionContext ac= ActionContext.getContext();
2.通过ActionContext对象获取Map类型的Web元素
Map<String,Object> s=ac.getSession();
或:Map<String,Object> s=( Map<String,Object> )ac.get("session");
3.使用Map对象的put/get方法添加/获取属性
s.put("username","lucky");
【JSP中获取Web元素属性方法】
1.通过JSP脚本元素
<%=session.getAttribute("username")%>
<s:property value="#session.username"/>
demo1.java
ActionContext ac=ActionContext.getContext();
//ac已经算是获得了request
ac.put("sse","request获取一");
Map<String,Object>sse=ac.getSession();
sse.put("sse","session获取一");
Map<String,Object>app=ac.getApplication();
app.put("sse","applaction获取一");
demo1.jsp中获取元素之代码
<%=" session内容为:"+session.getAttribute("sse")%><br>
<%=" aplication内容为:"+application.getAttribute("sse")%><br>
<%=" request内容为:"+request.getAttribute("sse")%><br>
第二种方法:依赖于容器(ServletActionContext)获取真实类型的Web元素
【问题】 通过ActionContext获取的Web元素是Map形式的,这对于习惯了Servlet编程的开发人员可能不太习惯
【解决方法】Struts2框架中可以通过ServletActionContext类 获取 ServletAPI对象
【ServletActionContext的常用方法】
public static HttpServletRequest getRequest():获取请求对象
public static ServletContext getServletContext():获取应用程序对象
【思考】
获取HttpSession的方法?
【基本思路】
1、在Action中使用真实类型的Web元素,即通过ServletActionContext获取HttpServletRequest、HttpSession、ServletContext类型的Web元素。
2、在视图中输出Web元素属性的方法不变。
demo2.java
HttpServletRequest request;
HttpSessionsession;
ServletContextapplication;
request=ServletActionContext.getRequest();
session=request.getSession();
application=ServletActionContext.getServletContext();
request.setAttribute("sse","request获取二");
session.setAttribute("sse","session获取二");
application.setAttribute("sse","application获取二");
demo2.jsp和demo1.jsp一样
第三种方法:依赖于Struts2框架注入(IoC-控制反转)获取Map类型的Web元素(只用这种方法)
相关概念】
DI:DependencyInjection—依赖注入
IoC:Inversion ofControl—控制反转
两个概念表达相同的思想,即:
当我们想得到某个对象时,不需要我们主动创建这个对象,或主动去获取这个对象,而是等待系统把这个对象自动交给我们,我们只是被动接受。(网购模式)
我们需要的对象我们不去主动创建,而让系统自动填充的,叫依赖注入;本来是我们控制去获取这个对象,结果控制权交给了系统,我们被动接受,叫控制反转。
【依赖注入的相关接口】
Action中通过实现相关接口,Web元素即可由框架自动注入,这组接口以Aware(知道,明白)结尾
RequestAware:Map类型的request元素
ServletRequestAware:HttpServletRequest类型
ServletResponseAware:HttpServletResponse类型
SessionAware:Map类型的session元素
ApplicationAware:Map类型的application元素
CookiesAware:Map类型的Cookie元素
【依赖注入的Web元素获取方法】
1.创建Action时实现Web元素相关接口
2.在Action中声明Map类型的私有变量
3.在Aware接口的实现方法中为变量赋值
【基本思路】
1、创建Action实现RequestAware、SessionAware、ApplicationAware三个接口,声明三个Map类型的私有变量,三个接口的实现方法分别对三个变量赋值,从页获取Web元素。
2、在视图中输出Web元素属性的方法不变。
demo3.java
public class demo1 extends ActionSupportimplements RequestAware,SessionAware,ApplicationAware {
privateString name,password;//变量名可以与form表单中的name一致,也可以不一致,但是set与get方法后的变量必须与form表单中的保持一致
Map<String,Object>request;
Map<String,Object>session;
Map<String,Object>application;
publicString execute() throws Exception {
this.name="demo1获取参数Action参数的第一种方法:基本数据类型的自动填充";
request.put("sse","request获取三");
session.put("sse","session获取三");
application.put("sse","application获三");
returnsuper.execute();
}
publicString getUsername() {
returnname;
}
publicvoid setUsername(String username) {
this.name= username;
}
publicString getPassword() {
returnpassword;
}
publicvoid setPassword(String password) {
this.password= password;
}
publicvoid setRequest(Map<String, Object> request) {
this.request=request;
}
publicvoid setApplication(Map<String, Object> application) {
this.application=application;
}
publicvoid setSession(Map<String, Object> session) {
this.session=session;
}
}
利用include方法来调用其他xml中的action(主要用于模块开发下的整合)
例:
login.xml
<struts>
<package name="login"extends="struts-default" namespace="/login">
<action name="login*"class="com.bjsxt.struts2.user.action.LoginAction{1}">
<result>/user_login_success.jsp</result>
</action>
</package>
</struts>
struts.xml
<struts>
<constant name="struts.devMode"value="true" />
<include file="login.xml"/>
</struts>
这样就可以将login.xml中的action归纳到struts.xml中使用。
DefaultAction的使用
在大部分网站中,即使用户操作错误也很少直接报404错误,而是已更友好的界面来提示,那么来说需要一个DefaultAction来处理,将未知的指令利用这个action来指向一个交互界面。
Result总结(入门级别)
Result中的type
Result中默认的类型是Dispatcher,这个就不详细说了。就是通过Action找到指定的jsp(只能跳转到页面,不适用于Action或者Servlet),但是常用的类型共有四种,其中最常用的有两种
- redirect:action处理完后重定向到一个视图资源(如:jsp页面(只适用于页面,不适用于Action或者Servle)),请求参数全部丢失,action处理结果也全部丢失,页面重新将资源发送到被重定向的页面。
- redirect-action:action处理完后重定向到一个action,请求参数全部丢失,action处理结果也全部丢失。
- chain:action处理完后转发到一个action,请求参数全部丢失,action处理结果不会丢失
具体的应用可以参考以下代码
<struts>
<constant name="struts.devMode"value="true" />
<package name="resultTypes"namespace="/r" extends="struts-default">
<action name="r1">
<result type="dispatcher">/r1.jsp</result>
</action>
<action name="r2">
<result type="redirect">/r2.jsp</result>
</action>
<action name="r3">
<result type="chain">r1</result>
</action>
<action name="r4">
<result type="redirectAction">r2</result>
</action>
</package>
</struts>
根据上面的xml建立页面后尝试跳转并观察跳转后url路径基本上就明白各自的含义了。
stream,freemark会后续跟进学习
global-result(全局result)
当某个页面或者action为每一个Action都可能访问到的时候,我们不需要在每一个人Action下面建立该result,只需要建立一个全局result就可以了
<global-results>
<result name="mainpage">/main.jsp</result>
</global-results>
当多个或者所有Action都会访问mainpage时,所以建立全局result可以减少xml中代码冗余。
那么如果我们想在别的package下调用别的package中的全局result该怎么办呢?
<packagename="admin" namespace="/admin" extends="user">
<action name="admin"class="com.bjsxt.struts2.user.action.AdminAction">
<result>/admin.jsp</result>
</action>
</package>
从上面的代码我们可以看到,extends终于被使用了,不再是我们以前一直默认的struts-default,而是继承了user这个package,那么该package下的所有action和result都可以为我所用了。
从上述的继承中我们可以学到,如果多个package互相继承某种特定的result时,我们就可以选择将特定的result或者Action拿出来放在单独的pack中,这样所有的package都去继承,就有效的避免了继承错乱和代码冗余。
动态结果集(用的不多,了解即可)
根据Action中方法的执行来确定要跳转的jsp或者action
@Override
publicString execute() throws Exception {
if(type== 1) r="/user_success.jsp";
elseif (type == 2) r="/user_error.jsp";
return"success";
}
我在action中这样重写execute()方法,其中r是这个类里面的一个成员变量,那么我根据传进来不同的参数,来动态的给r赋值为所要跳转的jsp页面。那么我只需在struts.xml中这样去取得Action中的这个属性,去动态的获取所要跳转的路径
<action name="user" class="com.bjsxt.struts2.user.action.UserAction">
<result>${r}</result>
</action>
我们可以看到利用${r}取出相应的参数值即可。也就是从Action的值栈中取值,不是EL表达式,而是OGNL表达式(这样不就写死在java之中,如果页面名字改了的话不就需要改动固定代码吗?为什么要这样,即使多在xml文档中配点也无所谓,最起码以后要改的时候改文档就可以,java类可以避免改动)
带参数的结果集
首先,我们先看看上面的图并结合下面的代码来看,当client向后台提交一个request请求,并且传入一个参数
<ol>
<li><ahref="user/user?type=1">传参数</a></li>
</ol>
再到struts.xml中去找user这个namespace下找到user这个Action并且指定type=1
<packagename="user" namespace="/user" extends="struts-default">
<action name="user"class="com.bjsxt.struts2.user.action.UserAction">
<resulttype="redirect">/user_success.jsp?t=${type}</result>
</action>
</package>
下面放上Action的代码
packagecom.bjsxt.struts2.user.action;
importcom.opensymphony.xwork2.ActionSupport;
public classUserAction extends ActionSupport {
privateint type;
publicint getType() {
returntype;
}
publicvoid setType(int type) {
this.type= type;
}
@Override
publicString execute() throws Exception {
return"success";
}
}
我们再回看上面的那幅图,当Action通过dispatcher来forward指定的page时,Action接收到的参数会存在值栈里面,利用<s:debug></s:debug>我们来看一下会有什么样的结果,这里就不截取运行图了,直接说结果,在valuestack中type中的值为传入值,那么:当使用dispatcher或者chain来跳转到相应的jsp页面时,值栈中的属性是公用的,也可以理解为,在一次Client发起的一次request中值栈的值共用且不清空
而我们的例子里面,在xml中result的type是redirect也就是重定向到一个页面,来,继续回看上图,Client向userAction发起一个request,找到这个Action,type值也成功传入,但是result的type为redirect,直接告诉Client:“接下来你需要新建一个request,并发送给user_success.jsp,参数t=${type}值”,假设我们继续按照上面从值栈取值<s:propertyvalue="t"/><br/>会得到什么结果呢?答案很容易知道,肯定取不出来东西。当新的request再次发起请求的时候,值栈已经被清空了,这时候再去取必然取不到,
那么该如何取到重定向过来的值呢?利用这个方法<s:property value="#parameters.t"/>我们不去值栈里面取,而是去actioncontext里面以属性的方式给取出来。
好了综上,当我们在Action中想要跳转页面,其实总共分两个大类,一种是一个request,那么值栈被共享只需要到值栈中取到相应的值就可以了,另一种就是重定向的方式来传值,也就是一个Action返回带参数的结果集时被重定向到另一个Action或者jsp页面中去,值栈被清空,那就需要到stackContext中去取得参数。
有些特殊情下,需要在JSP的<%%>java代码段中直接访问值栈内容。而不使用<ww:propertyvalue="name"/>获取,可以通过如下方式进行:
String name =(String)ActionContext.getContext().getValueStack().findValue("name");
为了正常运行,还需要在jsp将这com.opensymphony.xwork.ActionContext 类引入
<%@ page import="com.opensymphony.xwork.ActionContext" %>
Struts2在下载文件时result需要配置的东西
当result为stream类型时,struts2会自动根据你配置好的参数下载文件。
其中主要使用的参数是:
contentType 指定下载文件的文件类型 —— application/octet-stream 表示无限制
inputName 流对象名 ——比如这里写inputStream,它就会自动去找Action中的getInputStream方法。
contentDisposition 使用经过转码的文件名作为下载文件名—— 默认格式是attachment;filename="${fileName}",将调用该Action中的getFileName方法。
bufferSize 下载文件的缓冲大小