struts2总结
1.为什么学习Struts框架
a.Struts框架好处
struts2是主流的开发技术,大多数公司在使用,struts把相关的servlet组件的各种操作都进行了相应的封装,这样就可以轻松地完成复杂的操作。Struts2是基于MVC模式开发的,MVC结构是一个优秀的设计思想,可以提高程序结构的灵活性,便于日后的维护和扩展。
--简化开发工作量,提高开发效率
--采用了优秀的设计思想(MVC)
b.Struts框架作用
struts充当控制器,接收请求,分发处理,
c.如何学习Struts框架
先重点掌握框架使用,原理性有余力可以多了解些。
注:我们目前使用的Struts版本为struts2 2.1.8。(2.1.6版本bug较多)
d.学校struts要掌握的重点:
valuestack
action
result
拦截器
标记库
2.MVC模式在JavaWeb中的应用
*a.什么是MVC
是一种软件架构的思想,将一个软件的组成部分划分成三种不同类型的 模块,分别是:
模型(用于封装业务逻辑,包括业务数据和业务处理逻辑,由JavaBean组件 (比如实体类,DAO,Service)实现),
视图(负责显示界面与用户交互,处理表示逻辑,一种是展示数据,另一种 是接受数据,由jsp组件实现),
控制器(用于控制流程,协调模型与视图,由Servlet、Filter组件(比如之 前的ActionServlet)实现)。
使用mvc的最主要的原因是为了实现模型的复用:即模型只需要开发 一次, 模型不关心处理的结果如何展现,模型将结果交给不同的视 图,由视图来展 现这些数据;另外,可以利用不同的视图来访问 同一个模型。
MVC:基于面向对象的一种常见的体系架构
Model:
封装数据以及对数据操作的方法(相当于饭店中做菜的材料(数据)和做菜的厨师(方法)
一个流程用一个方法
View:
封装的是用户界面,接收用户请求,负责输入和输出(和用户打交道)(相当于服务员)
Controller:
封装的是业务流程,控制业务流程(相当于饭店中的管理者)(如果程序比较简单,可以把Controller写在Model里面)
b.为什么使用MVC
企业程序更强调结构灵活,易于扩展和升级。所以广泛选用MVC模式开发。
*c.JavaWeb中如何实现MVC结构
JavaWeb技术主要有:Servlet,JSP,JavaBean,Filter,Listener等
V部分:JSP组件 (cart.jsp)
M部分:JavaBean组件(entity,dao)
C部分:Servlet,Filter组件 (ShoppingCartServlet)
*d.MVC处理流程
--首先浏览器发出请求
--请求进入C控制器
--C控制器调用M模型组件处理
--M模型组件处理后返回处理结果
-- C控制器调用V视图组件生成响应信息
*e.Struts框架的MVC实现
原有ShoppingCart程序,虽然符合MVC结构,但不适合大型项目开发,因为请求一多,ShoppingCartServlet会出现大量的if..else... ,可以进行以下方式的改造:
为ShoppingCartServlet配置一个XML文件,该文件定义不同请求和不同Action组件的对应关系,将原有if..else..分支处理用Action组件封装。
**f.Struts框架的处理流程
--浏览器发出struts请求
--请求进入struts控制器
--struts控制器解析xml配置文件(xml定义了请求和Action对应关系)
--控制器根据不同请求,调用不同的Action
--Action调用DAO处理请求,之后返回结果
--控制器根据结果调用视图组件为用户响应
struts总结
3.了解Struts历史
Struts框架分成Struts1和Struts2.Struts2和Struts1没有必然联系。Struts2是以WebWork框架核心(xwork)为基础构建起来
最早出现的 Struts1是一个非常著名的框架,它实现了MVC模式。Struts1简单小巧,其中最成熟的版本是Struts1.2。之后出现了WebWork框架,其实现技术比Struts1先进,但影响力不如Struts1。在框架技术不断发展过程中,有人在WebWork核心XWork的基础上包装了 Struts1(算是两种框架的整合),由此,结合了Struts1的影响力和WebWork的先进技术,Struts 2诞生了。所以说,Struts2不是Struts1的升级,它更像是WebWork的升级版本。
*4.Struts基本使用
a.引入struts2核心开发包(5个)
(1).struts2-core-2.1.8.1.jar
Struts2核心包,是Struts框架的“外衣。
(2).xwork-core-2.1.6.jar
Struts2核心包,是WebWork内核。
(3).ognl-2.7.3.jar
用来支持ognl表达式的,类似于EL表达式,功能比EL表达式强大的
多。
(4).freemarker-2.3.15.jar
freemarker是比jsp更简单好用,功能更加强大的表现层技术,用来 替代jsp的。在Struts2中提倡使用freemarker模板,但实际项目中使 用jsp也很多。
(5).commons-fileupload-1.2.1.jar
用于实现文件上传功能的jar包。
b.在web.xml中配置下struts控制器
c.根据请求编写Action,JSP
d.在struts.xml定义Action和请求对应关系
5.HelloWord入门实例
welcome.action-->Struts控制器-->struts.xml-->WelcomeAction
-->welcome.jsp
(Action请求可以是/welcome.action或者/welcome两种写法)
7、struts.xml基本配置
struts.xml放在src根目录下。
a) <struts>
根元素,可以包含多个<package>元素
b)<package>
<package name="***" namespace="/默认" extends="struts-default">
元素主要用于将Action分组定义。name属性用于指定包名;extends一般指 定为struts-default。struts-default这个包在struts-default.xml中定义,其中 包含了struts2运行所需的必要组件。
添加namespace属性的主要作用是避免在大项目中出现的命名冲突问题。
注意:
http://localhost:8080/struts01/netctoss/welcome
<package name="demo1" namespace="/netctosss"
extends="struts-default">
<action name="welcome" class="action.WelcomeAction">
<result name="success">/WEB-INF/jsp/Welcome.jsp</result>
</action>
namespace属性默认为“/”(即从应用名后的第一个"/"开始到最后一个“/”结束)。
eg:对于http://localhost:8080/struts01/netctoss/cost/showList.action
其中的namespace属性值应为”/netctoss/cost”。
eg:
如果不加namespace="/netctoss"那么
http://localhost:8080/struts01/netctoss/list.action便不会得到正确的匹配
一个<package>元素可以包含多个<action>..</action>主要用于定义请求
和Action组件的对应关系。name属性指定请求名;class属性指定Action的包
名.类名
c)<action>
<action name="***" method="execute默认" class="***">
一个<action>元素可以包含多个<result>...<result>主要用于定义视图响 应。name属性与Action.execute()返回值一致。
其中
(1).method属性可以省略,如果省略则默认调用Action中的execute()方法
(2.)如果不指定class属性,Struts会默认调用框架底层的ActionSupport
类处理(见下面:)。框架会默认为该<action>添加一个class,作用是
转发到对应
的<result name="success">中指定的页面。当有nameform请求发
来时,struts不会调 用action(找不到)而是直接调用result中name属
性为success对应的页面。
注:name=”success”可以省略
<action name="nameform">
<!--<result name=”success”>/WEB-INF/jsp/Welcome.jsp</result>-->
<result>/WEB-INF/jsp/Welcome.jsp</result>
</action>
struts总结
--------ActionSupport类:---------
public String execute(){
return “success”;
}
d)<result>
<result name="success默认" type="dispatcher默认">
e:注意:
1.对于package、action、result,如果使用默认属性值,则可以省略属性名。
eg:<result name=”sucess” type=”dispatcher”>****</sesult>
如果使用默认属性值:则可以写成:
<result >****</sesult>
对于重复使用的result可以写成全局的result
<!-- 定义全局result -->
<global-results>
<!-- 错误处理 -->
<result name="error">/WEB-INF/jsp/error.html</result>
<!-- 拦截器登录处理 -->
<result name="login" type="redirectAction">showLogin</result>
</global-results>
2.page包中的各组件的放置顺序:
具体见struts-2.1.7dtd
package (result-types——> interceptors——>default-interceptor-ref——>
default-action-ref——> default-class-ref——> global-results——>
global-exception-mappings——> action*)>
拦截器配置放在global-results之前
3.当项目业务处理较多时如果将所有的配置都写在struts.xml一个文件中将很不方便,所以可以将不同的处理模块分别写在不同的xml文件中,最后在struts.xml中加载和模块配置文件,过程如下:
struts.xml:
<struts>
<!-- 加载各模块配置文件 -->
<include file="struts-cost.xml"/>
<include file="struts-account.xml"/>
...........
<package name="netctoss-default" extends="json-default">
<!-- 追加共通的Action,Interceptor组件 -->
拦截器、全局results....
</package>
</struts>
struts-cost.xml:
<!-- 资费模块配置文件 -->
<struts>
<package name="cost" extends="netctoss-default">
........
</package>
</struts>
struts-account.xml:
<!-- 资费模块配置文件 -->
<struts>
<package name="account" extends="netctoss-default">
........
</package>
</struts>
.............................
浏览器访问地址:
http://localhost:8080/应用名/namespace/action
http://localhost:8080/struts03_2/list/costList.action?page=4
以上的namespace默认为’’/’,本例为list
8、如何解决用户绕过ActionServlet,直接访问jsp文件的问题。
将所有jsp文件拷贝到WEB-INF目录下。因为WEB-INF目录中的内容不能直接访问,但能转发过来。
当然也可以用之前的session技术(太麻烦)。
对于struts中WebRoot文件夹下的.jsp页面仍可以直接输入地址访问。
9、Struts2提供的方便之处:
a.数据的自动的封装
根据页面组件的name属性,自动封装到Action中对应的name属性中。
在Jsp页面<input name=”name” type=”text”/>
在action中会自动给属性private String name 赋值。但是注意要有name对应的getter和setter方法。
b.数据的自动的传递
Action中的属性在jsp页面可以直接用EL表达式拿到
eg:
Action中属性private String name;
在jsp页面上可以直接${name}的到对应的值
按理说,EL表达式取数据的对象只有pageContext,request,session,application四类,不应该从Action对象中取name属性值,但 struts框架重新包装了request,getAttribute()方法被改写(request的其他方法没被改写),如果从request对象中调用getAttribute()方法没获取到值(null),那么会自动从action中取,这样,struts中EL表达式就可以直接获取到 Action属性中的值了。
上面介绍的采用了包装模式:(相当于手机加壳案例和线程池案例(其中的close方法特殊)。参考如下:
public class StrutsRequestWrapper extends HttpServletRequestWrapper{
//重写
public Object getAttribute(String key){
//寻找原request信息
Object obj = super.getAttribute(key);
if(obj == null){
//寻找valuestack信息
obj = valuestack.findValue(key);
//Ognl.getValue(key,context,root);
} struts总结
return obj;
}
}
${name} -->request.getAttribute("name")
-->Ognl.getValue("name",context,root)
10.项目示例(资费列表显示)
a.显示默认主页面
index.action-->struts控制器-->index.html
b.资费列表显示
feeList.action-->struts控制器
-->FeeListAction-->FeeDAO-->COST
-->fee_list.jsp
1)编写COST对应的Entity实体类
2)编写DAO,DBUtil
--引入oracle驱动包
3)编写Action
4)配置Action
5)编写JSP
--引入jstl.jar,standard.jar
11.OGNL技术的使用
1)什么是OGNL
OGNL是Object-Graphic Navigation Language(对象图导航语言)的缩写,
是一种功能强大的表达式语言(与EL表达式类似)。OGNL可以让我们用非常
简单的表达式访问对象层(访问对象的数据和方法),它用一个独立的lib形
式出现(封装于ognl.jar工具包中),方便我们使用或者构建自己的框架。
OGNL区别于EL表达式,OGNL可以在.java文件中通过getValue()方法访问对
象,而EL表达式不可以在.java文件中使用;EL表达式在.jsp文件中可以直接
输出数据,而OGNL不可以,OGNL在.jsp文件中只能当做struts标签中的属性
值,通过struts标签访问数据。
2)OGNL基本原理
OGNL工具主要由3部分构成,具体如下
a.OGNL引擎
负责解析ONGL表达式,定位数据
b.Root根存储区(Object类型)
负责存储要访问的目标对象(一个)。
c.Context变量存储区(Map类型)
负责存放多个要访问的目标对象。
*3)OGNL基本语法
a.访问Root区域对象基本语法
Ognl.getValue("OGNL表达式", "Map存储器内容","根存储器内容");
Ognl.getValue("OGNL表达式", "根存储器内容");
--访问简单数据:"属性" 例如"name"
int id = (Integer)Ognl.getValue("id", foo);
String name = (String)Ognl.getValue("name", foo);
--访问数组或List集合:"属性[0]"
例如"arr[1]"
String arrVal = (String)Ognl.getValue("arr[1]", foo);
String listVal = (String)Ognl.getValue("list[2]", foo);
--访问map集合元素:"属性['key']"或"属性.key"
例如"map.c"或"map[‘two’]"
注:”map[‘a’]”不可用(‘a’结果为整数)
String mapVal = (String)Ognl.getValue("map.b", foo);
String mapval2 = (String)Ognl.getValue("map['user.name']",foo);
--访问方法:"属性值.方法()",
例如"list.size()"
System.out.println(Ognl.getValue("map['two'].toUpperCase()", foo));
System.out.println(Ognl.getValue("list.size()", foo));
--访问构造方法:"new 包名.构造方法"
例如"new java.util.Date()"
Object obj =
Ognl.getValue("new org.tarena.entity.Foo('TOM')", foo);
--访问静态成员:"@包名.类名@成员"
Ognl.getValue("@java.lang.Math@abs(-1)", foo);
Ognl.getValue("@java.lang.Math@PI", foo);
--创建List对象:"{元素1,元素2}"
Object obj1 = Ognl.getValue("{'struts','hibernate','spring'}",foo);
--创建Map对象:
"#{key1:value1,key2:value2}"
Object obj2 = Ognl.getValue("#{1:'tom',2:'jack',3:'rose'}", foo);
public static void main(String[] args) throws Exception {
Foo foo = new Foo();
foo.setId(100);
foo.setName("张三");
foo.setArr(new String[] { "A", "B", "C" });
foo.setList(Arrays.asList("芙蓉", "凤姐", "春哥"));
Map<String, String> map = new HashMap<String, String>();
map.put("one", "Java");
map.put("two", "JavaWeb");
map.put("three", "Struts2");
map.put("user.name", "Scott");
foo.setMap(map);
//测试OGNL访问foo目标对象
//Ognl.getValue("OGNL表达式", "Map存储器内容","根存储器内容");
//Ognl.getValue(expression, root);
Map context = new HashMap();
//访问root基本的属性值
int id = (Integer)Ognl.getValue("id", foo);
System.out.println(id);
String name = (String)Ognl.getValue("name", foo);
System.out.println(name);
System.out.println(Ognl.getValue("user.name", foo));
//访问root数组和集合元素
String arrVal = (String)Ognl.getValue("arr[1]", foo);
System.out.println(arrVal);
String listVal = (String)Ognl.getValue("list[2]", foo);
System.out.println(listVal);
//访问root对象的map集合元素
String mapVal = (String)Ognl.getValue("map.b", foo);
System.out.println(mapVal);
String mapval2 = (String)Ognl.getValue("map['user.name']",foo);
System.out.println(mapval2);
//支持运算符
System.out.println(Ognl.getValue("id+1",foo));
//System.out.println((Integer)Ognl.getValue("id",foo)+1);
System.out.println(Ognl.getValue("\"姓名:\"+name", foo));
System.out.println(Ognl.getValue("id > 200",foo));
//方法调用
System.out.println(Ognl.getValue("map['two'].toUpperCase()", foo));
System.out.println(Ognl.getValue("list.size()", foo));
//构造方法的调用
Object obj = Ognl.getValue("new org.tarena.entity.Foo('TOM')", foo);
System.out.println(obj);
System.out.println(Ognl.getValue("name", obj));
//静态方法和常量的调用
System.out.println(Ognl.getValue("@java.lang.Math@abs(-1)", foo));
System.out.println(Ognl.getValue("@java.lang.Math@PI", foo));
//创建一个list集合
Object obj1 = Ognl.getValue("{'struts','hibernate','spring'}",foo);
System.out.println(obj1.getClass().getName());
//创建一个map集合
Object obj2 = Ognl.getValue("#{1:'tom',2:'jack',3:'rose'}", foo);
System.out.println(obj2.getClass().getName());
}
12、OGNL技术在Struts的使用
在Struts2中有一个ValueStack(本质就是存放数据的集合)数据对象,该对象存储了请求相关的所有数据信息。例如request,session,application,action等.
Struts2采用OGNL工具对ValueStack进行操作。
1)xwork对OGNL进行了部分改造
在xwork中将原来OGNL标准结构中的Root存储区改造成了一个栈结构
(CompoundRoot)以前只能存放一个对象,现在进行了扩容,可以存放
多个对象
当利用"属性"表达式访问时,优先去栈顶对象寻找,没有再去次栈顶寻找。
2)ValueStack结构(OgnlValueStack)
ValueStack就是一个容器(Map集合)(存放东西的不是Tomcat容器)
3)Struts2标签的使用
在JSP中,利用Struts2标签显示数据,需要为标签指定OGNL表达式,标签利用表达式定位ValueStack中的数据,进行相应操作。
a) debug标签:显示valuestack状态
<s:debug></s:debug>
b) iterator标签:循环集合元素
value属性:指定ognl
var属性:指定循环变量,会被存放到ValueStack的context区域。
status属性:指定循环状态变量,会被存放到ValueStack的context区域.该
变量有count属性表示一共循环了多少个元素。
index属性表示当前循环元素的索引。
<s:iterator value="new int[totalPages]" status="i">
<s:if test="#i.count==page">
<a class="current_page"href="costList.action?page=${i.count }">
<s:property value="#i.count"/>
</a>
</s:if>
<s:else>
<a href="costList.action?page=${i.count }">
<s:property value="#i.count"/>
</a>
</s:else>
</s:iterator>
c) if...else...标签:判断分支
test属性:指定ognl判断表达式
<s:if test="page < totalPages" >
<a href="costList?page=${page+1 }">下一页</a>
</s:if>
<s:else>
下一页
</s:else>
d) property标签:显示数据
value属性:指定ognl表达式
<s:iterator value="costList" var="cost">
<tr><td><s:property value="#cost.id"/></td></tr>
</s:iterator>
e) date标签:将date数据格式化输出
name属性:指定ognl表达式
format属性:指定日期模板
<td>
<s:date name="#cost.createTime" format="yyyy-MM-dd HH:mm:ss"/>
</td>
13、Action组件的相关使用
1)Action组件的原理
--客户发出action请求给struts控制器
--前端控制器截获请求并根据配置信息调用对应的action
--struts控制器会创建valuestack对象
--struts控制器根据请求创建Action对象并将Action压入valuestack的root
栈顶(Action线性安全,不用考虑并发问题,struts会为每一个请求 创
建一个Action对象,区别于Servlet)
--struts控制器调用一系列拦截器做一些辅助性的工作(一般都是
valuestack的操作),如通过parmaeterInterceptor将表单中的数据赋值
给action中的对象;还有将请求相关request,session对象放入到
valuestack的context区域,同时将ValueStack对象存入到request中。
存储的key为”struts.valueStack”。(这样,当一次请求结束后,容器
会销毁request,request销毁了,其中的valuestack对象也就销毁了)
--控制器调用Action,执行业务方法处理(根据输入算输出并返回结果)
--控制器根据Action返回值调用result视图组件处理。
--请求处理完毕,销毁valuestack和Action对象。
一次请求,一个valuestack和action对象
2)Struts中写Action过程:
定输入、定输出、根据输入算输出、返回结果
*3)Action中如何使用session,request
(1).利用ActionContext和ServletActionContext工具类(不推荐)
a.ActionContext返回是Struts框架封装成Map之后的
request,session,application.
eg:
//将用户信息写入Session
ActionContext ac = ActionContext.getContext();
Map<String,Object> session = ac.getSession();
session.put("user", adminCode);
Map<String,Object> application = ac.getApplication();
Map<String,Object> request = (Map)ac.get("request");
b.ServletActionContext返回的是Servlet中使用的request, session,
application原有类型。
(使用建议:存值取值建议用Map结构API,如果有特殊需求再采用
Servlet原有类型(如获取客户端IP))
eg:
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = httpRequest.getSession();
HttpServletResponse response = ServletActionContext.getResponse();
ServletContext application = ServletActionContext.getServletContext()
ActionContext工具类中的方法getSession()如何将HttpSession对
象封装成Map对象的说明:
Map----impements--->AbstructMap---extends-->SessionMap(重
写了Map中的put和get方法)
----如何将原有HttpSession封装成Map------
public class SessionMap extends AbstractMap{
private HttpSession session;
public SessionMap(HttpSession session){
this.session = session;
}
public void put(Object key,Object val){
session.setAttribute(key,val);
}
public Object get(Object key){
session.getAttribute(key);
}
}
不推荐使用 ActionContext访问Session的方式,因为这种方式的“侵入性”较强。ActionContext是Struts2的API, 如果使用其他框架代替目前的Struts2框架,而我们实际项目中的Action的数量非常庞大,每个类都修改的话,会非常麻烦。
(2).利用Aware接口方式注入(推荐)
将Action实现Aware接口,由框架底层将对象注入给Action中相应的变量
当action实例化时候,struts框架就会自动将session对象(在valuestack中)赋值给实现了SessionAware接口的类中的变量(就是注入)。
(即session,ruquest,response,application)
RequestAware 对应的是Map结构的Request
SessionAware 对应的是Map结构的Session
ApplicationAware 对应的是Map结构的Application
ServletRequestAware对应的HttpServletRequest
ServletResponseAware对应的HttpServeltResponse
ServletContextAware对应的ServletContext
eg:
public class LoginAction implements SessionAware{
protected Map<String,Object> session;
...............
public void setSession(Map<String, Object> session) {
this.session = session;
}
public String execute(){
if(("admin".equals(adminCode)) && ("1234".equals(passWord))){
session.put("user", adminCode);
return "success";
}else{
return "error";
}
}
}
(3)、使用工具类(ActionContext、ServletActionContext)还是
Aware接口?
Action组件内部用Aware接口、Action组件之外(比如后面的拦截器)用ActionContext、ServletActionContext
4)Action属性注入
作用:在创建Action对象时指定属性值
在<action>配置中为Action组件的属性指定一个初始值,该值在创建
Action对象时注入。可以将一些变化的参数值,利用该方法指定。例如
pageSize,管理员用户名,dir存储路径等。
在<action>配置中,为Action对象的属性指定初始值。使用格式如下:
<action name="feeList" class="org.action.FeeListAction">
<!--创建Action对象时给属性指定一个值-->
<param name="属性名">属性值</param>
<result></result>
</action>
eg:
public class ListAction{
private Stirng param1;
private String param2;
.....
public String getParam1(){
return param1;
}
..... struts总结
public setParam1(Stirng param1){
this.param1 = param1;
}
.......
}
struts.xml:
<action name="feeList" class="org.action.FeeListAction">
<param name="param1">admin</param>
<param name="param2">1234</param>
<result>........</result>
</action>
5)Action通配符配置
Action通配符可以将多个具有相同属性的Action整合为一个Action。
在<action>配置时,name请求名可以使用若干个*符号。然后再其他属
性中通过使用{1},{2}引用第一个*号,第二个*号所代表的字符串。
<!-- 通配符配置 -->
<action name="*_*" method="{2}"class="com.tarena.action.{1}">
<result name="success">/index.jsp</result>
</action>
eg:
CostAction.java:
public class CostAction {
//属性定义--省略
public String add(){
System.out.println("资费添加操作");
return "success";
}
public String update(){
System.out.println("资费更新操作");
return "success";
}
}
struts.xml:
<action name="cost_*" method="{1}"
class="com.tarena.action.CostAction">
<result name="success">/index.jsp</result>
</action>
14、Result组件的相关使用
1)Result组件原理
a.Result组件的作用
Result组件负责生成响应视图内容。将ValueStack中的数据做不同形式
的输出。
Struts2框架提供了多种不同的Result组件类型,用于做不同形式响应。
例如json数据响应,流数据响应,jsp响应等。
b.Result组件相关配置
--Result声明定义
<package>
<result-types>
<result-type name="result类型" class="Result组件实现类"/>
</result-types>
</package>
--Result使用定义
<action>
<result name="标示符" type="result类型"></result>
</action>
c.编写一个转发功能的Result组件
定义的类类必须实现Result接口(Result组件),并且将约定的execute方法实现在该方法中编写了生成响应输出的逻辑代码。
public class MyDispatcherResult implements Result{
private String location;
//setter和getter方法
................
//约定方法
public void execute(){
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response =ServletActionContext.getResponse();
RequestDispatcher rd = request.getRequestDispatcher(location);
rd.forward(request,response);
}
}
*2)掌握经常使用的Result组件
a. JSP视图
--dispatcher(默认):以转发方式调用JSP
--redirect:以重定向方式调用JSP
<result name="" type="dispatcher(默认)/redirect">
jsp页面
</result>
b.Action视图
--chain:以转发方式调用下一个Action
--redirectAction:以重定向方式调用下一个Action
c:
stream:以字节流方式响应,Action中的InputStream类型的属性以
字节流方式输出。
eg:由服务器端生成验证码图片并以流的方式发送给浏览器。
在struts.xml端的配置如下:
<!--验证码处理 -->
<action name="image" class="org.tarena.netctoss.action.ImageAction">
<result type="stream" name="success">
<param name="inputName">imageStream</param>
</result>
<result name="fail">/WEB-INF/jsp/login.jsp</result>
</action>
注:其中的<param name="inputName">imageStream</param>区别于Action
属性注入,其作用为:给result对象指定参数值
d:
json:以json字符串响应,将Action中指定的属性拼成一个json字符串输
出。
注:
其中,a、b、c中的result组件在’struts-defaulg”包下,d中的result组件
在”json-default”,struts-core.jar中的默认包
<package name="struts-default" abstract="true">如下:
<package name="struts-default" abstract="true">
<result-types>
<result-type name="chain" class="*****"/>
<result-type name="dispatcher" class="*****"/>
<result-type name="freemarker" class="*****"/>
<result-type name="httpheader" class="*****"/>
<result-type name="redirect" class="*****"/>
<result-type name="redirectAction" class="*****"/>
<result-type name="stream" class="class="*****"/>
<result-type name="velocity" class="class="*****"/>
<result-type name="xslt" class="*****"/>
<result-type name="plainText" class="*****"/>
</result-types>
</package>
相同命名空间调用:
<result type="redirectAction">
请求名
</result>
跨命名空间调用
<result type="redirectAction">
<param name="actionName">请求名</param>
<param name="namespace">/</param>
</result>
======资费删除流程=====
fee_delete.action-->FeeDeleteAction-->FeeDAO
-->FeeListAction-->fee_list.jsp
============练习=============
完成课上项目任务。
扩展任务:完成资费的添加,更新处理,查看,启用等。
16、json Result组件
主要负责将Action的属性以json字符串格式输出。
json Result的使用步骤:
a.引入struts2-json-plugin.jar
b.将<package>元素的extends属性改为继承"json-default"(json-default包
的范围比原struts-default包大,所以更改后仍可以使用以前包中的组件)
c.<result>使用配置
//只返回Action中一个属性值
<result type="json">
<param name="root">属性名</param>
</result>
//返回Action中多个属性值
<result type="json">
<param name="includeProperties">
属性名1,属性名2,属性名3
</param>
</result>
//返回Action中所有属性值
<result type="json">
</result>
因为使用name属性默认值,所以name=”success”可以省略。
17、Struts2表单标签
1.什么是标签
struts标签封装了界面显示逻辑,用于简化JSP。
jstl标签用来代替jsp页面中的java代码,struts标签不但可以代替jsp页面中的
java代码,还可以代替jsp页面中html标记
2.struts常用标签:
textfield,password,hidden,textarea
radio,checkboxlist,select
checkbox,form
a) textfield
以输入框方式显示属性值.
name属性指定ognl表达式,表示输入框中的值
<input type="text" class="readonly" name=”cost.id” value=${cost.id}
readonly />
相当于:
<s:textfield name="cost.id" cssClass="readonly" readonly="true">
</s:textfield>
注:规定输入字段为只读(内容不可被更改)。在html中input标签readonly属性
直接写,在struts标签中要写成:readonly=”true”。类似的的还有disabled要写成
disabled=”true”
b) textarea
以文本域方式显示属性值.
name属性指定ognl表达式,表示文本域中的值
<textarea class="width300 height70">${cost.descr}</textarea>
<s:textarea name="cost.descr" cssClass="width300 height70"></s:textarea>
c)password:
以文本框方式显示密码
name属性指定ognl表达式,,表示输入框中的值
注:默认情况下,文本框内不会显示密码,如果要显示密码则需要设置showPassword属性。
密码:<s:password name="password" showPassword="true"></s:password>
d) radio:
以一组单选框方式显示,并可以根据属性值决定哪个选项选中。
list属性指定集合ognl,用于生成若干radio选项。
name属性指定ognl,用于决定哪个raido选中。
<s:radio name="cost.costType" list="#{'1':'包月','2':'套餐','3':'计时'}">
</s:radio>
e)checkbox:
以单选框方式显示选中状态
name属性指定ognl表达式,name属性值为true时,单选框默认会被选中
eg:
婚姻状况:<s:checkbox name="marry""></s:checkbox>已婚
f)checkboxlist:
以复选框方式显示选中状态
name属性指定ognl表达式,用来决定哪些复选框默认被选中。
list属性为map<key,value>集合
listKey属性为map集合中的key
listValue属性为map集合中的value,为在界面显示的内容
参考struts05_1中的form.jsp
eg1:
爱好:<s:checkboxlist name="chk" list="#session.favors" listKey="id"
listValue="name"></s:checkboxlist>
eg2:
<s:checkboxlist name=”” list=”costList’” listKey="id"
listValue="name"></s:checkboxlist>
g)select:
以下拉列表的方式显示数据:
name属性指定ognl表达式,用来决定哪个option默认被选中。
eg1:
<s:select name="status" cssClass="select_search"
list="#{‘0’:’暂停’,'1':'开通','2':'暂停','3':'删除'}">
</s:select>
相当于
<select class="select_search" >
<option value="2">全部</option>
<option value="1">开通</option>
<option value="0">暂停</option>
<option value="-1">删除</option>
</select>
eg2:
<s:select list=”costList” name=”” listKey=”id” listValue=”name”></s:select>
注:对于上面的struts标签中的name属性,不但可以指定ognl表达式获取相关的数据,还可以将action中的属性名设置为name的属性值,这样就可以实现表单数据的自动提交。
h)form:
theme属性:表示主题,可选项有xhtml和simple;如果不写,默认为xhtml 。
theme属性如果为默认值(xhtml),那么在使用struts表单标
签时候会自动添加样式(自动添加tr、tr,让显示更美观),
这样就会破坏当前页面的布局和样式。
如果在用struts表单标签时,没有用struts中的form标签而是
使用html中的form标签,那么在使用struts表单标签时候也
会自动添加样式
<s:form action="update" namespace="/" theme="simple">
.............
</s:form>
通用属性(所有标记都具备的属性):
label
labelpostion
required
tooltip
tooltipIconPath
cssClass(html中的class)
cssStyle(html中的style)
name
value
18、拦截器组件
2)什么是拦截器:
一般利用拦截器封装一些通用性处理功能,便于重复利用。例如请求参
数给action属性赋值、日志记录、权限检查、文件上传、事务处理等。拦截
器通过配置方式调用,因此使用方法比较灵活,便于维护或扩展。
Struts2框架提供了大量的拦截器组件,如果不能满足开发者需要,可以
进行自定义。
eg:
<interceptor name="params"
class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
其中ParametersInterceptor类用于在创建Action对象时将表单中的参数
赋值给Action中的相关属性。
具体见:
ReferencedLibraries/struts2-core-2.1.8.jar/struts-default.xml
/<interceptors>标记
2)拦截器完整工作流程:
a.客户发送一个Action请求,请求交给控制器
b.控制器创建一个ValueStack、Action对象,并将Action压入ValueStack栈顶
c.控制器调用ActionInvocation组件执行请求处理
d.ActionInvocation组件调用请求相关的拦截器组件--前部分处理.
e.然后再调用Action组件业务方法
f.然后再调用Result组件处理方法
g.最后执行拦截器组件--后部分处理.
h.将响应信息输出,为客户端响应
3)拦截器作用
拦截器可以在Action和Result组件调用之前执行,也可以在Action和Result
组件之后执行。
4)自定义拦截器
a.编写拦截器组件实现类(两种方法):
1.编写拦截器组件实现类实现Interceptor接口的interceptor方法。
public class MyInterceptor implements Interceptor{
public String intercept (ActionInvocation in){
//拦截器的前期处理逻辑
in.invoke();//执行Action和Result
//in.invokeActionOnly();//仅执行Action
//拦截器的后期处理逻辑
}
}
eg:
public class CheckLoginInterceptor implements Interceptor{
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
public String intercept(ActionInvocation in) throws Exception {
System.out.println("(前期处理)自定义拦截器被调用。。。");
ActionContext ac = ActionContext.getContext();
//ActionContext ac = in.getInvocationContext();
Map<String,Object> session = ac.getSession();
//登录检查
if(session.get("user") == null){
//为登录
return "login";//进入登录页面
}
in.invoke();//执行Action、DAO和Result--->jsp
System.out.println("(后期处理)自定义拦截器被调用。。。");
return null;//invoke()方法执行之后,后面的返回值都无效(即不会返回),因为执行in.invoke(),也即执行Action和Result调用Jsp后,返回的值没有任何意义。
}
}
2.所有拦截器组件实现类也可以通过继承AbstractInterceptor类并实现
inteceptor方法。
eg:
public class Interceptor2 extends AbstractInterceptor {
public String intercept(ActionInvocation in) throws Exception {
System.out.println("(前期处理)自定义拦截器被调用。。。");
// ActionContext ac = ActionContext.getContext();
ActionContext ac = in.getInvocationContext();
Map<String, Object> session = ac.getSession();
// 登录检查
if (session.get("user") == null) {
// 未登录
return "login";// 进入登录页面
}
in.invoke();// 执行Action和Result
System.out.println("(后期处理)自定义拦截器被调用。。。");
return null;//invoke()方法执行之后,后面的返回值都无效(即不会返回),写着只是好看一点
}
}
注意:当执行了invoke()方法后,invoke()方法后面的返回值无效(即不会返回),写着只是好看一点。如果没有执行invoke(),返回值有效。控制器会根据该返回值调用一个Result生成响应信息
b.注册拦截器(将拦截器注册给struts2框架)
(1).方法一:
<package>
<interceptors>
<interceptor name="拦截器名"class="拦截器实现类"/>
<interceptor name="" class=""/>
</interceptors>
</package>
eg:
<!-- 定义拦截器 -->
<interceptors>
<interceptor name="checklogin"
class="org.tarena.netctoss.interceptor.CheckLoginInterceptor2"/>
</interceptors>
<!-- 使用拦截器 -->
<action name="costDelete" class=”org.tarena.netctos.CostDeleteAction”>
<interceptor-ref name="checklogin"/>
<interceptor-ref name="defaultStack"/>//调用默认拦截器栈中的拦截器
<result name=”cuccess”>/WEB-INF/jsp/cost/cost_delete.jsp</result>
</action>
struts总结
(1).方法二
创建拦截器栈(<interceptor-stack name="loginStack">),将多个拦截器放在拦截器栈中,这样就可以在需要的地方直接引入拦截器栈就行了(<interceptor-ref name="loginStack"/>)
eg:
<!-- 定义拦截器 -->
<interceptors>
<interceptor name="checklogin"
class="org.tarena.netctoss.interceptor.CheckLoginInterceptor2"/>
<interceptor-stack name="loginStack">
<interceptor-ref name="checklogin"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 使用拦截器 -->
<action name="costDelete" class=”org.tarena.netctos.CostDeleteAction”>
<interceptor-ref name="loginStack"/>
<result name=”cuccess”>/WEB-INF/jsp/cost/cost_delete.jsp</result>
</action>
方法二中的定义拦截器的方法实际上是先定义一个checklogin拦截器,然后定义一个拦截器栈(用于存放多个拦截器),把定义的checklogin拦截器和struts默认的拦截器放在拦截器栈里面。在使用拦截器时候,直接引入拦截器栈就可以了(相当于引入了checklogin拦截器和struts默认拦截器)。
c.使用拦截器组件
(1).为所有Action指定默认使用配置,这样,拦截器便对该包中的所有Action
起拦截作用
<package ******>
<default-interceptor-ref name="拦截器名"/>
</package>
--(2).为某一个Action指定使用配置
<action>
<interceptor-ref name="拦截器名"/>
</action>
eg:
<action name="showAddCost">
<interceptor-ref name="loginStack"/>
<interceptor-ref name="defaultStack"/>
<result>/WEB-INF/jsp/cost/cost_add.jsp</result>
</action>
注意:如果为某个Action指定了拦截器配置,不会再调用指定的默认拦截器.所以一般会加上<interceptor-ref name="defaultStack"/>。
5)fileUpload内置拦截器的使用
a)fileUpload拦截器功能
用于实现文件上传功能。
fileUpload拦截器处理流程:
客户端发送上传文件请求
首先,struts控制器收到请求后调用fileUpload拦截器,fileUpload拦截器调用commons-file-upload.jar上传组件对请求提交
的表单信息进行解析。
然后将解析后的文件保存到服务器临时目录下,并将临时目录下的文件
对象赋值给Action的File属性。
执行Action,在Actin业务中,需要做文件复制,将临时文件转移到目标
目录中。
当Action和Result调用完毕后,清除临时目录下的文件,因此在Actin业务中,需要做文件复制,将临时文件转移到目标目录中。
b)上传示例
注意事项:
--需要引入commons-io.jar)
--form表单method必须为post;enctype必须为multipart/form-data
--Action组件按下面规则接收表单信息
<input type="file" name="xxx">
Action属性如下:
private File xxx;// 临时目录下的文件
private String xxxFileName;// 源文件名
private String xxxContentType;// 源文件类型
---struts默认允许上传的文件所占最大内存为2097152字节(不同计算机可
能略有不同)。如果要更改默认设置,可以参考如下“更改struts中的默
认设置”
更改struts中的默认设置(即修改如下配置文件):
ReferencedLibraries/struts2-core-2.1.8.jar/org-apache.struts2/default.properties
18、更改strts中的默认配置:
1.更改对客服端发送过来的参数进行解码的方式:
<constant name="struts.i18n.encoding" value="gbk"/>(默认为utf-8)
2.更该允许客户端上传的最大文件大小值:
<constant name=”struts.multipart.maxSize” value=”100000000”/>
(默认大小为:2097152字节,约2M)
19、全局异常处理:
<!--全局的异常处理,当异常从Action中抛出时,会自动进入全局异常处理模块-->
<global-results>
<!-- 错误处理 -->
<result name="error">/WEB-INF/jsp/error.html</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="error">
</exception-mapping>
</global-exception-mappings>
20、用户输入不存在请求资源路径(及action不存在)处理:
<!-- 指定一个默认响应的Action,当遇到例外的action请求,该项配置启作用 -->
<default-action-ref name="defaultAction"/>
<!-- 采用重定向方式调用toLogin -->
<action name="defaultAction">
<result type="redirectAction">
<param name="namespace">/</param>
<param name="actionName">toLogin</param>
</result>
</action>
<!-- 显示登录页面 -->
<action name="toLogin">
<result>/WEB-INF/jsp/login.jsp</result>
</action>
通过此配置后,用户输入非法请求资源路径时便不会出现“404”错误提示了,而是友好地跳转到登录页面。
======NETCTOSS项目========
1)项目描述(一期工程)
实现电信计费系统的账号管理,业务管理,资费管理,管理员和角色管理的功能。
2)项目技术架构
主要采用Struts2,JDBC,Ajax,jQuery技术。
采用MVC结构分层思想开发。
表现层(V):JSP,Ajax,jQuery
控制层(C):Struts2控制器Filter+Action
业务层(M):JavaBean,Bean组件
数据访问层(M):DAO+JDBC
3)项目工程结构
a.开发包引入
--Struts2开发包(5个核心+1个json响应包)
--Oracle驱动包
b.src源码
org.tarena.netctoss.action
org.tarena.netctoss.action.fee
org.tarena.netctoss.action.account
org.tarena.netctoss.service
org.tarena.netctoss.dao
org.tarena.netctoss.pojo
org.tarena.netctoss.util
org.tarena.netctoss.test
c.配置文件
web.xml : 配置struts2控制器
struts.xml:struts2框架主配置
--struts-fee.xml : 资费模块配置
--struts-account.xml:账号模块配置
d.WebRoot
WebRoot/WEB-INFO/jsp/fee/资费页面
WebRoot/styles/样式文件
WebRoot/js/脚本文件
WebRoot/images/页面图片
=========记录检索=========
search.action-->SearchAction-->DAO
1.项目评审
1)自我简介,项目需求描述,功能演示
2)典型功能实现讲解
2.共通问题处理
1)表单校验处理(参考“jQuery表单校验插件”目录的资料)
可以利用jQuery编写校验函数,
也可以基于现有的jQuery扩展插件来实现
例如validate和formvalidate等插件都可以
2)Struts2全局异常处理
当异常抛出Action的业务方法时,可以利用全局异常处理方式解决(在struts.xml追加配置)。
3)将页眉导航部分提取成一个head.jsp,
通过include方式引入各个页面。
4)设置一个默认响应Action。
遇到不能识别的Action请求,或找不到请求对应的Action时,调用该默认Action做响应