概述
常用的MVC框架
目前常用的MVC框架,除了Struts 2的两个前身外,还有一些非常流行的MVC框架,这些框架都提供了较好的层次分隔能力。在实现良好的MVC 分隔的基础上,还提供一些辅助类库,帮助应用的开发。
目前常用的MVC框架还有如下一些。
1.JSF
准确地说,JSF是一个标准,而不是一个产品。目前,JSF已经有两个实现产品可供选择,包含Sun的参考实现和Apache的MyFaces。大部分的时候,我们所说的JSF都是指Sun的参考实现。目前,JSF是作为JEE 5.0的一个组成部分,与JEE 5.0一起发布。
JSF的行为方法在POJO中实现,JSF的Managed Bean无需继承任何特别的类。因此,无需在表单和模型对象之间实现多余的控制器层。JSF中没有控制器对象,控制器行为通过模型对象实现。
当然,JSF也允许生成独立的控制器对象。在Struts 1中,Form Bean包含数据,Action Bean包含业务逻辑,二者无法融合在一起。在JSF中,既可以将二者分开,也可以合并在一个对象中,提供更多灵活的选择。
JSF的事件框架可以细化到表单中每个字段。JSF依然是基于JSP/Servlet的,仍然是JSP/Servlet架构,因而学习曲线相对简单。在实际使用过程中,JSF也会存在一些不足:
— 作为新兴的MVC框架,用户相对较少,相关资源也不是非常丰富。
— JSF并不是一个完全组件化的框架,它依然是基于JSP/Servlet架构的。
— JSF的成熟度还有待进一步提高。
2.Tapestry
Tapestry并不是一种单纯的MVC框架,它更像MVC框架和模板技术的结合,它不仅包含了前端的MVC框架,还包含了一种视图层的模板技术,使用Tapestry完全可以与Servlet/JSP API分离,是一种非常优秀的设计。
通过使用Tapestry,开发者完全不需要使用JSP技术,用户只需要使用Tapestry提供的模板技术即可,Tapestry实现了视图逻辑和业务逻辑的彻底分离。
Tapestry使用组件库替代了标签库,没有标签库概念,从而避免了标签库和组件结合的问题。Tapsetry是完全组件化的框架。Tapestr只有组件或页面两个概念,因此,链接跳转目标要么是组件,要么是页面,没有多余的path概念。组件名,也就是对象名称,组件名称和path名称合二为一。
Tapestry具有很高的代码复用性,在Tapestry中,任何对象都可看作可复用的组件。JSP开发者是真正面向对象,而不是URL解析。对于对页面要求灵活度相当高的系统,Tapestry是第一选择。精确地错误报告,可以将错误定位到源程序中的行,取代了JSP中那种编译后的提示。
因此,笔者一直对Tapestry情有独钟:如果技术允许,使用Tapestry会带给整个应用更加优雅的架构,更好的开发效率。
但是,在实际开发过程中,采用Tapestry也面临着一些问题必须考虑:
— Tapestry的学习曲线相对陡峭,国内开发群体不是非常活跃,文档不是十分丰富。官方的文档太过学院派,缺乏实际的示例程序。
— Tapestry的组件逻辑比较复杂,再加上OGNL表达式和属性指定机制,因而难以添加注释。
3.Spring MVC
Spring提供了一个细致完整的MVC框架。该框架为模型、视图、控制器之间提供了一个非常清晰的划分,各部分耦合极低。
Spring的MVC是非常灵活的,它完全基于接口编程,真正实现了视图无关。视图不再强制要求使用JSP,可以使用Velocity、
XSLT或其他视图技术。甚至可以使用自定义的视图机制——只需要简单地实现View接口,并且把对应视图技术集成进来。
Spring的Controllers由IoC容器管理。因此,单元测试更加方便。
Spring MVC框架以DispatcherServlet为核心控制器,该控制器负责拦截用户的所有请求,将请求分发到对应的业务控制器。
Spring MVC还包括处理器映射、视图解析、信息国际化、主题解析、文件上传等。所有控制器都必须实现Controller接口,
该接口仅定义ModelAndView handleRequest(request,response)方法。通过实现该接口来实现用户的业务逻辑控制器。
Spring MVC框架有一个极好的优势,就是它的视图解析策略:它的控制器返回一个ModelAndView对象,该对象包含视图名字和Model,
Model提供了Bean的名字及其对象的对应关系。视图名解析的配置非常灵活,抽象的Model完全独立于表现层技术,不会与任何表现层
耦合:JSP、Velocity或者其他的技术——都可以和Spring整合。
但相对于Tapestry框架而言,Spring MVC依然是基于JSP/Servlet API的。
总体上来看,Spring MVC框架致力于一种完美的解决方案,并与Web应用紧紧耦合在一起。这都导致了Spring MVC框架的一些缺点:
— Spring的MVC与Servlet API耦合,难以脱离Servlet容器独立运行,降低了Spring MVC框架的可扩展性。
— 太过细化的角色划分,太过烦琐,降低了应用的开发效率。
— 过分追求架构的完美,有过度设计的危险。
1.2 Struts 2的起源和背景
Struts 2以WebWork优秀的设计思想为核心,吸收了Struts 1的部分优点,建立了一个兼容WebWork和Struts 1的MVC框架,
Struts 2的目标是希望可以让原来使用Struts 1、WebWork的开发人员,都可以平稳过渡到使用Struts 2框架
一个webwork的简单例子
1 导包
2 web.xml 配置 核心控制器
<servlet>
<servlet-name>webwork</servlet-name>
<servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>webwork</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
3 src下建立xwork.xml 配置例子如下
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE xwork PUBLIC
"-//OpenSymphony Group//XWork 1.1//EN"
"http://www.opensymphony.com/xwork/xwork-1.1.dtd">
<xwork>
<!-- 导入WebWork默认配置文件-->
<include file="webwork-default.xml" />
<package name="default" extends="webwork-default">
<action name="login" class="com.LoginAction">
<result name="input">/login.jsp</result>
<result name="error">/error.jsp</result>
<result name="success">/success.jsp</result>
</action>
</package>
</xwork>
4 访问
strut2.0版本
Struts1和Struts2的区别和对比
1 Struts1要求Action类继承一个抽象基类。Strut1的一个普遍问题是使用抽象类编程而不是接口。
2 Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。
Struts2提供一个ActionSupport基类去 实现 常用的接口。Action接口不是必须的,
任何有execute标识的POJO(javabean)对象都可以用作Struts2的Action对象。
3 线程模式:
Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,
并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,
并且不会导致性能和垃圾回收问题)
4 Servlet 依赖:
Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。
Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。
但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。
5 可测性:
测试Struts1 Action的一个主要问题是execute方法暴露了servlet API(这使得测试要依赖于容器)。
一个第三方扩展--Struts TestCase--提供了一套Struts1的模拟对象(来进行测试)。
Struts 2 Action可以通过初始化、设置属性、调用方法来测试,"依赖注入"支持也使测试更容易。
6 捕获输入:
Struts1 使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。
因为其他JavaBean不能用作ActionForm,开发者经 常创建多余的类捕获输入。
动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,
开发者可能是在重新描述(创建)已经存 在的JavaBean(仍然会导致有冗余的javabean)。
Struts 2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。
输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过 web页面上的taglibs访问。
Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。
这种 ModelDriven 特性简化了taglib对POJO输入对象的引用。
7 表达式语言:
Struts1 整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。
Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--"Object Graph Notation Language" (OGNL).
8 绑定值到页面(view):
Struts 1使用标准JSP机制把对象绑定到页面中来访问。
Struts 2 使用 "ValueStack"技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。
ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。
9 类型转换:
Struts 1 ActionForm 属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。
Struts2 使用OGNL进行类型转换。提供基本和常用对象的转换器。
10 校验:
Struts 1支持在ActionForm的validate方法中手动校验,或者通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。
Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性
11 Action执行的控制:
Struts1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
Struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。
简化版
1 struts1于struts2的区别从显示的页面标签上来说,struts1使用的是JSTL标签。
2 struts2也使用了JSTL标签,但是它又引入了ognl。
3 struts1对于请求是单例模式的,虽然是线程安全的,但是却影响了性能和action的其他功能的增加
4 struts2是每一个请求产生一个实例,没有线程安全的问题,也不会存在性能和垃圾回收的问题。
5 struts1过于依赖servlet API,在所有的执行方法中都要通过参数传入。
6 struts2对其进行了解耦,
7 struts1要继承一个抽象基类,再重写execute方法。
8 struts2不强制你,可以根据你要使用到的来继承或者实现
9 struts1由于过于依赖servlet API,所以对它进行测试的时候存在着很大的问题。
10 struts2解耦了,还可以通过初始化,方法调用等来进行测试
11 struts1要通过ActionFrom类来进行数据交换,还要将它放入pojo送到后台与数据库交互。
struts2不用那么复杂,直接在action里定义属性,或者配置好驱动类。
12 struts1验证只有String型的
struts2因为有ognl标签,所有很多基本类型以及自己定义的对象都可以验证。
Struts2请求流程
1、客户端发送请求 (客户端初始化一个指向Servlet容器(如Tomcat)的请求)
2、请求经过一系列过滤器(如ActionContextCleanUp、SiteMesh等),ActionContextCleanUp-->FilterDispatcher
3、FilterDispatcher通过ActionMapper来决定这个Request需要调用哪个Action
4、如果ActionMapper决定调用某个Action,FilterDispatcher把请求的处理交给ActionProxy,
5、ActionProxy通过Configuration Manager询问Struts配置文件(Struts.xml),找到需要调用的Action类。
6、ActionProxy创建一个ActionInvocation的实例
7、ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用
8、Action执行完毕,ActionInvocation创建Result并返回,返回结果前要做些动作也是可以的
一 导包
二 web.xml 配置过滤器
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
三 src目录下建立 struts.xml
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="struts-default.xml" />
<package name="取个名字" extends="struts-default" >
<action name="取个名字" >
<result name="success">/xxx.jsp</result>
</action>
</package>
</struts>
一些常识:
可以有多个包,每个包有一个命名空间
一个包里有多个action,一个action里有多个result
访问action时先以命名空间查找
.action
不带类名与包名 逐层访问路径 默认 result success 继承extends="struts-default"(为什么呢,后面拦截器会有说明)
包名 命名空间 寻找顺序(最精确匹配)
先找命名空间,如果在该命名空间不存在该action则报错[unknown location]
,当命名空间存在该action时,则可以在该命名空间下后面紧跟其它路径逐层访问 当逐层访问路径与有命名空间冲突时,以有命名空间的为主。
<jsp:forward 问题 struts2 jsp:forward 404错误
原因: struts 2 使用 filter 实现, <jsp:forword /> 的机制是 servlet
目前解决办法是:
1、用form表单提交
2、<meta http-equiv="refresh" content="0;URL=xxx.action">;
3、<script language="javascript">location.replace(URL)</script>
4、通过过滤器改变请求地址。
配置web.xml
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern >/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
5、可以用s:url标签
类名
实现Action与不实现action接口
不extends ActionSupport 重写execute方法
extends ActionSupport 重写execute方法
?传值注意 method=post
表单提交 用 method=post
struts2 中 Actionsupport 的作用
(1)实现Action接口 具备5个常量
(2)数据校验,Action接口的基础上还定义了一个validate()方法
(3)国际化
(4)
Action 跟 Actionsupport 的区别
当我们在写action的时候,可以实现Action接口,也可以继承Actionsupport这个类.到底这两个有什么区别呢?
Action接口有:
public static final java.lang.String SUCCESS = "success";
public static final java.lang.String NONE = "none";
public static final java.lang.String ERROR = "error";
public static final java.lang.String INPUT = "input";
public static final java.lang.String LOGIN = "login";
public abstract java.lang.String execute() throws java.lang.Exception;
而Actionsupport这个工具类在实现了Action接口的基础上还定义了一个validate()方法,
重写该方法,它会在execute()方法之前执行,如校验失败,会转入input处,必须在配置该Action时配置input属性。
另外,Actionsupport还提供了一个getText(String key)方法还实现国际化,该方法从资源文件上获取国际化信息.
这样在自定义标签时可以定义一个变量为new actionsupport对象实现国际化。
ActionSupport类的作用
struts2不要求我们自己设计的action类继承任何的struts基类或struts接口,
但是我们为了方便实现我们自己的action,大多数情况下都会继承com.opensymphony.xwork2.ActionSupport类,
并重写此类里的public String execute() throws Exception 方法。因为此类中实现了很多的实用接口,提供了很多默认方法,
这些默认方法包括国际化信息的方法、默认的处理用户请求的方法等,这样可以大大的简化Acion的开发。
Struts2中通常直接使用Action来封装HTTP请求参数,因此,Action类里还应该包含与请求参数对应的属性,并且为属性提供对应的getter和setter方法。
开发模式的常量设置:将<constant name="struts.devMode" value="false" />中的value值改为true。
四 从 action属性到页面的传值 bean list bena中bean bean中list list中bean map
??? 根据hashmap的键取值 ??/ ---0903
<s:property value="map.get('aaa')"/>
<s:property value="map.get('aaa')"/>//根据键取值
//迭代键 后根据键取值
<s:iterator value="map.keySet()" id="xxx">
<s:property value="map.get(#xxx).name"/>
<s:property />
action属性必须提供get方法
struts2标签引入
<%@taglib prefix="s" uri="/struts-tags" %>
<s:iterator value="list" status="xxx">
<s:property value="name"/>
</s:iterator>
<s:iterator value="map">
<s:property value="value.name" />
</s:iterator>
<!--bean-->
<s:property value="tb.name"/>
<s:property value="tb.sex"/>
<s:property value="tb.age"/>个
字符串判断
<s:if test='属性名=="男"'>
数字类型判断
<s:if test='属性名==111'>
form基本标签
<h3>使用s:radio生成多个单选框</h3>
<s:form>
<!-- 使用简单集合来生成多个单选框 -->
<s:radio name="a" label="请选择" labelposition="top"
list="{'a' , 'b' , 'c'}" value="选中的里面的值集合"/>
<!-- 使用简单Map对象来生成多个单选框 -->
<s:radio name="b" label="请选择您想选择出版日期" labelposition="top"
list="#{'a':'111' , 'b':'222' , 'c':'333'}"
listKey="key"
listValue="value" value="选中的里面的值集合"/>
<s:bean name="包名.类名" id="bs"/>
<!-- 使用集合里放多个JavaBean实例来生成多个单选框 -->
<s:radio name="c" label="请选择" labelposition="top"
list="#bs.bean中集合属性名"
listKey="集合里JavaBean实例属性1"
listValue="集合里JavaBean实例属性2" value="选中的里面的值集合"/>
<s:radio name="d" label="请选择您想选择出版日期" labelposition="top"
list="#bs.map"
listKey="key"
listValue="value"/>
</s:form>
<h3>使用s:select生成下拉选择框</h3>
<s:form>
<!-- 使用简单集合来生成下拉选择框 -->
<s:select name="a" label="请选择" labelposition="top" multiple="true"
list="{'a' , 'b' , 'c'}" value="选中的里面的值集合"/>
<!-- 使用简单Map对象来生成下拉选择框 -->
<s:select name="b" label="请选择" labelposition="top"
list="#{'a':'111' , 'b':'222' , 'c':'333'}"
listKey="key"
listValue="value" value="选中的里面的值集合"/>
<s:bean name="包名.类名" id="bs"/>
<!-- 使用集合里放多个JavaBean实例来生成下拉选择框 -->
<s:select name="b" label="请选择" labelposition="top" multiple="true"
list="#bs.bean中集合属性名"
listKey="集合里JavaBean实例属性1"
listValue="集合里JavaBean实例属性2" value="选中的里面的值集合"/>
</s:form>
<h3>使用s:checkboxlist生成多个复选框</h3>
<s:form>
<!-- 使用简单集合来生成多个复选框 -->
<s:checkboxlist name="a" label="请选择" labelposition="top"
list="{'a' , 'b' , 'c'}"/>
<!-- 使用简单Map对象来生成多个复选框 -->
<s:checkboxlist name="b" label="请选择" labelposition="top"
list="#{'a':'111' , 'b':'222' , 'c':'333'}"
listKey="key"
listValue="value"/>
<s:bean name="包名.类名" id="bs"/>
<!-- 使用集合里放多个JavaBean实例来生成多个复选框 -->
<s:checkboxlist name="b" label="请选择" labelposition="top"
list="#bs.bean中集合属性名"
listKey="集合里JavaBean实例属性1"
listValue="集合里JavaBean实例属性2"/>
</s:form>
<s:bean> 可以设置值
<s:bean id="xxx" name="包名.类名">
<s:param name="属性名" value="属性值"/> ???取action的值设置不成功??? 经测试value="name"时貌似存在bug
</s:bean>
<constant name="struts.action.extension" value="action" />修改后缀名的常量
学生管理系统
动态方法
1感叹号定位方法 action名!方法名.action
2 method定位方法 配置多个action
例如:在实际应用中,常有以下配置:
<action name="user_*" class="cn.itcast.action.UserAction" method="{1}“ >
<result name="success">/message.jsp</result>
<result name="input">/addUser.jsp</result>
</action>
UserAction中有以下两个处理方法:
public String add() throws Exception{
....
}
public String update() throws Exception{
....
}
3 通配符匹配法(action中的name class method都支持通配符 result也支持)
action的匹配顺序 先找最精确匹配 当用通配符匹配时 配置在前面的优先匹配
全局结果与局部结果 全局异常与局部异常
<global-results><result name="ggg">/ggg.jsp</result></global-results>
全局结果 所有的action共用 局部结果 自己的action用 当两者重名时,优先选择局部结果
全局异常与局部异常
<global-exception-mappings>
<exception-mapping result="geror"(此处的geror是指result中的配置(包含局部与全局 ,然后跳转到result中的配置中的jsp页面))
exception="异常类型如java.lang.Exception" >
</exception-mapping>
</global-exception-mappings>
如假设有result中配置<result name="geror">/geror.jsp</result>
则在geror.jsp页面可以使用Struts2的标签来输出异常信息
<s:property value="exception.message"/>
<s:property value="exceptionStack"/>
全局异常 所有的action共用 局部异常 自己的action用 当两者重名时,优先选择局部异常
跳转结果(源文件与重定向) struts2结果类型说明
在struts-default.xml配置文件中,提供了以下几种支持的结果类型:
结果类型(result-type)
说明
chain
Action链式处理的结果类型(两个action之间有一定的数据传递关系)
dispatcher
用于与jsp整合的结果类型
freemarker
用于与FreeMarker整合的结果类型
httpheader
用于控制特殊的HTTP行为的结果类型
redirect
用于直接跳转到其他URL的结果类型
redirect-action
用于直接跳转到其他Action的结果类型
stream
用于向浏览器返回一个InputStream的结果类型
velocity
用于整合Velocity的结果类型
xslt
用于整合XML/XSLT的结果类型
plaintext
用于显示某个页面原始代码的结果类型
<result type="plaintext">
<param name="location">/plainText.jsp</param>
<param name="charSet">GBK</param>
</result>
</action>
另外在struts2-jasperreports-plugin-2.0.6.jar的sturts-plugin.xml中定义了jasper类型的结果类型用于
整合JasperReport的结果类型;在struts2-jfreechart-plugin-2.0.6.jar的sturts-plugin.xml文件中定义了
chart类型的结果类型用于整合JFreeChart的结果类型;在struts2-jsf-plugin-2.0.6.jar的sturts-plugin.xml
中定义了jsf类型的结果类型用于整合JSF的结果类型; 定义了tiles类型的结果类型用于整合Tiles的结果类型
五 模型驱动与属性驱动
action类实现 ModelDriven<javabean类名> 接口,采用泛型
在 action类中定义javabean属性,实现getModel方法
六 资源文件与国际化
struts2自己的常量定义在rgapachestruts2的default.properties
资源文件配置
<constant name="struts.custom.i18n.resources" value="ApplicationResource"></constant>//ApplicationResource前缀
可以在资源文件中定义日期及其数字的格式化,例如在.properties文件中如下定义
product.orderDate = {0,date,dd-MM-yyyy}
format.number = {0,number,###,###.##}
format.discount = {0,number,###.#######%}
{}里面的意思如下
{ 参数序号(0表示格式第一个参数,1表示格式第2个参数,如下所示), 格式类形(number | date | time | choice),
格式样式}
用<s:text>引用
<s:text name="format.number">
<s:param name="format.discount" value="myDate"/>//第一个参数
<s:param name="format.number" value="myDate"/>第2个参数
</s:text>
国家化步骤 国际化要从.action请求 使i18n拦截器起作用
注意 :strut2只拦截action
action范围国际化信息(action类名_zh_CN.properties)->包范围国际化信息(package_en_US.properties)->全局国际化信息
action范围---包范围国际化信息--->全局国际化信息
全局国际化步骤
1 进行国际化资源文件配置
<constant name="struts.custom.i18n.resources" value="ApplicationResource"></constant>//ApplicationResource前缀,如上的配置是假设资源文件在src目录下,
如果该资源文件不在src目录下,而在其他包名下,则应加上包名,假设ApplicationResource.properties在com包下,则配置修改为
<constant name="struts.custom.i18n.resources" value="com/ApplicationResource"></constant>
2 国际化 设置session 属性 WW_TRANS_I18N_LOCALE 属性值 语言对象 Locale
struts2提供了一个i18n拦截器 自动把参数名为request_locale的值转化成 Local对象并存在名
为WW_TRANS_I18N_LOCALE 的session中
具体解释 国际化的实现过程
Struts2的I18nInterceptor拦截器会拦截所有的Action,它主要做的事情为
从客户端发送过来的请求参数中寻找是否存在名为request_locale的参数
若有,则将request_locale的value转化为locale保存起来
该locale是保存在以WW_TRANS_I18N_LOCALE所命名的session里面的
详见源码的第124行session.put(attributeName, locale)
其中attributeName即79行的protected String attributeName = DEFAULT_SESSION_ATTRIBUTE
和第75行的public static final String DEFAULT_SESSION_ATTRIBUTE = "WW_TRANS_I18N_LOCALE"
于是便可通过这种方式实现一个选择页面所显示的语言环境的功能
比如选择中文,那么就可以将request_locale设置为zh_CN
如果我们不做任何配置的话,客户端是不会发送request_locale参数的
它默认会用request的getLocale()方法得到默认locale,将其存放在session中
这样就使得以后客户的所有操作都是在同一个国际化的环境下执行
就不需要我们每一次都手工判断用户的国际化环境了
因为拦截器只拦截.action,所以访问jsp时,可采用访问.action的形式,可采用以下配置达到要求
<action name="*" >
<result >/{1}.jsp</result>
</action>
jsp页面元素的国际化
JSP页面输出:<s:text name=”username” /> 用name属性来加载资源文件的key值
表单元素的Label里输出:直接用对应表单的key属性。
action中获取国际化信息
输出带占位符号的国际化信息
JSP页面输出:在<s:text name=”username” />标签中使用多个<s:param …/> 传递参数值,如
<s:text name="welcome">
<s:param><s:property value="realname"/></s:param>
<s:param>学习</s:param>
</s:text>
Action中访问:使用getText(String aTextName,List args)或者getText(String key,String[] args
校验文件中获取国家化信息
在Struts 2框架中有3种配置常量的方式,或者说是可以在3类文件中配置常量,这3种配置常量的方式如下所述。
(1) 在struts.properties文件中配置常量:这种配置常量的方式是从WebWork框架继承下来的,目的是为了
保持与WebWork框架的兼容性。在Sturts 2中病不建议使用这种方式来配置常量。
(2) 在struts.xml文件及其他Struts 2配置文件中配置常量:这是Struts 2推荐的配置常量的方式。在
Struts 2中可以使用<constant>标签来配置常量。
(3) 在web.xml文件中配置常量:这种配置常量的方式在前面已经提到过。对于和Web整合的框架来说,应尽量
避免在web.xml文件中配置常量。
既然Struts 2可以再多个文件中配置常量,那就涉及一个搜索和加载顺序的问题,Struts 2框架将按如下的搜索顺序.
加载Struts 2常量:
Struts-default.xml:该文件保存在Struts-core-2.0.11.2.jar文件中。
Struts-plugin.xml:该文件是Struts 2插件配置文件,保存在类似Struts2-Xxx-2.0.11.jar这样的文件中。
Struts.xml:该文件中Struts 2框架默认的配置文件。
Struts.properties:该文件时Struts 2框架默认的属性文件(为了保持和WebWork框架兼容)。
Web.xml:该文件时Web应用程序的核心配置文件。
如果在多个文件中配置同一常量,则后一个配置文件中的常量会覆盖前一个配置文件中的同名常量的值。虽然在不同的配
置文件中配置常量的方式不同,但配置常量都需要两个必须的属性:
Name:该属性指定了常量名。
Value:该属性指定了常量值。
如在struts.xml中通过devMode属性来设置Struts 2的工作模式,可以按照如下的代码来设置:
<struts>
<!--了Struts 2的工作模式为开发模式 -->
<constant name=”truts.devMode”value=”true” />
</struts>
Struts.properties文件中的属性和属性值是key-value对,其中key对应于Struts 2常量的name属性,而value对应于
Struts 2常量的value属性,配置代码如下:
# 设置了Struts 2处于开发模式
Struts.devMode = true
在web.xml文件中配置Struts 2常量,可通过<filter>标签的<init-param>子标签来指定,每个<init-param>标
签配置了一个Struts 2常量。下面的代码通过web.xml文件来配置devMode属性。
<filter>
<!-- 配置核心Filter的名字 -->
<filter-name>struts</filter-name>
<!-- 配置核心Filter的类名 -->
<filter-class>org.apache.Struts2.dispatcher.FilterDispatcher</filter-class>
<!—通过init-param元素配置Struts 2常量 -->
<init-param>
<param-name>configProviders</param-name>
<param-value>providers.MyConfigurationProvider</param-value>
</init-param>
</filter>
注意:在实际的开发中,最好不要再struts.properties和web.xml文件中配置常量,而Struts 2推荐在struts.xml中配置常量。
七 类型转换与校验
1 内建类型转换器
常规类型的转换:
比如表单提交的信息有 用户名,生日, 年龄, Action中对应的数据类型分别是 String, Date, int. Struts2会自动成.
Struts2内建了常用的类型转换器,如String ,boolean,char,int,long,float,double,Date,
数组(假定元素是String类型), 集合(假定元素是String类型,用ArrayList封装)
利用ognl的内建支持
<1>直接把页面属性转换成action中的bean属性,页面属性分开,假设action中bean属性名为user 则如下
<input type=text name="user.username">
<input type=text name="user.password">
在页面输入值后 action中bean属性名为user可以直接拿到值
<2>直接把页面属性转换成action中的集合属性,不需要转换器
map版
第一个用户名:<input type="text" name="action中的集合属性名['one'].name"/>
第一个密码:<input type="text" name="action中的集合属性名['one'].pass"/>
第二个用户名:<input type="text" name="action中的集合属性名['two'].name"/>
第二个密码:<input type="text" name="action中的集合属性名['two'].pass"/>
list或数组版本
第一个用户名:<input type="text" name="action中的集合属性名[0].name"/>
第一个密码:<input type="text" name="action中的集合属性名[0].pass"/>
第二个用户名:<input type="text" name="action中的集合属性名[1].name"/>
第二个密码:<input type="text" name="action中的集合属性名[1].pass"/>
2 自定义
从范围来讲 有局部转换器和全局转换器 从实现的角度而言有基于ognl的转换和基于strut2转换
(1) 写自定义类型转换类
ognl的转换 extends DefaultTypeConverter 重写convertValue方法
举例 把字符串转换成bean对象 bean作为action中的一个属性 bean名为User,action中的bean属性名为user
假设把页面上的用户名和密码,用户名和密码在同一个文本框输入,用,好隔开, 代码如下
public Object convertValue(Map context, Object value, Class toType)
{
if (toType == User.class )
{
String[] params = (String[])value;
User user = new User();
String[] userValues = params[0].split(",");
user.setName(userValues[0]);
user.setPass(userValues[1]);
return user;
}
else if (toType == String.class )
{
User user = (User) value;
return "<" + user.getName() + "," + user.getPass() + ">";
}
return null ;
}
基于struts2的转换 extends StrutsTypeConverter 编写convertFromString与convertToString方法
public Object convertFromString(Map context, String[] values, Class toClass)
{
User user = new User();
String[] userValues = values[0].split(",");
user.setName(userValues[0]);
user.setPass(userValues[1]);
return user;
}
@Override
public String convertToString(Map context, Object o)
{
User user = (User)o;
return "<" + user.getName() + "," + user.getPass() + ">";
}
(2) 配置类型转换(局部配置与全局配置)
局部类型转换器只在本action内有效,全局类型转换器在所有action内有效
如果是配置局部类型转换器,则在与Action类同包目录下建立Action类名-conversion.properties文件,里面配置
action中的bean属性名=包名.转换器类名
如果是配置全局类型转换器,则在src目录下建立xwork-conversion.properties,里面配置
包名.被转换的类名(action中的bean属性类名)=包名.转换器类名
把页面属性转换成action中的集合属性 有2种实现方式
(1 action中集合属性使用泛型指定里面元素类型 2 在局部配置文件配置Element_action中的集合属性名=包名.集合
元素里的类名)
页面信息
请输入用户1信息:<input type="text" name="action中的集合属性名"/>
请输入用户2信息:<input type="text" name="action中的集合属性名"/>
3 错误处理
当类型转换出现错误,自动转发到input页面, 在页面用<s:fielderror />显示全部错误
在资源文件修改默认的错误显示信息
xwork.default.invalid.fieldvalue={0}字段类型转换失败!
校验
1 继承Actionsupport 重写validate方法
this.addFieldError("username", this.getText("xxx"));
this.addFieldError("username1", "yyy");
在页面用<s:fielderror />显示全部错误
显示某个错误
<s:fielderror>
<s:param value="%{'username1'}" />
</s:fielderror>
2 validateXxx方法
3 strut2的校验流程
4 校验框架
每个Action类有一个校验文件,命名 Action类名-validation.xml,且与Action类同目录,
当校验文件的取名为Action类名-validation.xml时,会对 action中的所有处理方法实施输入验证。
如果你只需要对action中的某个action方法实施校验,那么,校验文件的取名应为
:Action类名-ActionName-validation.xml,
其中ActionName为struts.xml中action的名称。例如:在实际应用中,常有以下配置:
<action name="user_*" class="cn.itcast.action.UserAction" method="{1}“ >
<result name="success">/WEB-INF/page/message.jsp</result>
<result name="input">/WEB-INF/page/addUser.jsp</result>
</action>
UserAction中有以下两个处理方法:
public String add() throws Exception{
....
}
public String update() throws Exception{
....
}
要对add()方法实施验证,校验文件的取名为: UserAction-user_add-validation.xml
要对update()方法实施验证,校验文件的取名为: UserAction-user_update-validation.xml ld>
客户端校验:功能不咋的 不建议使用
1,form的主题(theme)一定不能设定为simple
2,将form的validate属性设置为true
字段校验 字段用什么校验器来校验
非字段校验是用校验器校验什么字段
通俗点讲: 字段校验:校验谁,用什么方法 非字段校验:用什么校验,校验谁
字段校验
举例
内建的校验器在 xwork lib包中 如 xwork-2.0.7comopensymphonyxwork2validatorvalidatorsdefault.xml
文件中
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="name">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>必须输入名字</message>
</field-validator>
<field-validator type="regex">
<param name="expression"><![CDATA[(w{4,25})]]></param>
<message>您输入的用户名只能是字母和数组,且长度必须在4到25之间</message>
</field-validator>
</field>
<field name="pass">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>必须输入密码</message>
</field-validator>
<field-validator type="regex">
<param name="expression"><![CDATA[(w{4,25})]]></param>
<message>您输入的密码只能是字母和数组,且长度必须在4到25之间</message>
</field-validator>
</field>
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年纪必须在1到150之间</message>
</field-validator>
</field>
<field name="birth">
<field-validator type="date">
<param name="min">1900-01-01</param>
<param name="max">2050-02-21</param>
<message>年纪必须在${min}到${max}之间</message>
</field-validator>
</field>
</validators>
非字段校验
<validator type="校验器名称">
<param name="fieldName">需要被检验的字段</param>
<param name="参数名">参数值</param>
<message key="i18nkey">校验错误信息</message>
</validator>
举例
<validators>
<validator type="requiredstring">
<param name="fieldName">name</param>
<param name="trim">true</param>
<message>${getText("name.requried")}</message>
</validator>
<validator type="regex">
<param name="fieldName">name</param>
<param name="trim">true</param>
<param name="expression"><![CDATA[(w{4,25})]]></param>
<message>${getText("name.regex")}</message>
</validator>
<validator type="requiredstring">
<param name="fieldName">pass</param>
<param name="trim">true</param>
<message>${getText("pass.requried")}</message>
</validator>
<validator type="regex">
<param name="fieldName">pass</param>
<param name="trim">true</param>
<param name="expression"><![CDATA[(w{4,25})]]></param>
<message>${getText("pass.regex")}</message>
</validator>
<validator type="int">
<param name="fieldName">age</param>
<param name="min">1</param>
<param name="max">150</param>
<message>${getText("age.range")}</message>
</validator>
<validator type="date">
<param name="fieldName">birth</param>
<param name="min">1900-01-01</param>
<param name="max">2050-02-21</param>
<message>${getText("birth.range")}</message>
</validator>
</validators>
校验国际化配置<message key="name.regex"/> 如该种方式出错,则采用${getText("key名")}
向资源文件传递参数 ${getText("key名",{'aa','bb'})}
模型驱动的校验
模型驱动的配置 需要为模型驱动的action中bean提供get方法,当然喜欢加上set方法也可以
配置
<field name="action中bean属性名.name">
<field-validator type="requiredstring">
<message>模型驱动用户的:</message>
</field-validator>
visitor校验 2个校验文件 当属性为 复合类型 (如javabean)时的校验 区别于模型驱动
第一个校验文件 Action类名-validation.xml中的配置如下
<validators>
<field name="action中的bean属性名">
<field-validator type="visitor">
<param name="context">userContext</param>
<param name="appendPrefix">true</param>
<message>用户的:</message>
</field-validator>
</field>
</validators>
jsp页面输入框示例
<input type=text name="action中的bean属性名.bean中的属性"> 如
<input type=text name="user.name">
第2个校验文件 bean类名-第一个校验文件context参数值-validation.xml 如User-userContext-validation.xml,
该文件中就可以写校验bean中的属性,如同前面的校验
八 访问 servletapi 重点掌握
1. 非IoC方式
要获得上述对象,关键Struts 2.0中com.opensymphony.xwork2.ActionContext类。我们可以通过它的静态方
法getContext()获取当前Action的上下文对象。
另外,org.apache.struts2.ServletActionContext作为辅助类(Helper Class),可以帮助您快捷地获得这几个对象。
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
HttpSession session = request.getSession();
如果你只是想访问session的属性(Attribute),你也可以通过ActionContext.getContext().getSession()获取或添
加session范围(Scoped)的对象。
2. IoC方式
要使用IoC方式,我们首先要告诉IoC容器(Container)想取得某个对象的意愿,通过实现相应的接口做到这点
实现相关接口SessionAware, ServletRequestAware, ServletResponseAware
private Map att;
private HttpServletRequest request;
private HttpServletResponse response;
publicvoid setSession(Map att) {
this.att = att;
}
publicvoid setServletRequest(HttpServletRequest request) {
this.request = request;
}
publicvoid setServletResponse(HttpServletResponse response) {
this.response = response;
}
九 struts2 中的ognl 以及标签语法
两个栈 第一个是值栈ognl上下文(value stack) 第2个是ActionContext 栈 访问里面的数据ActionContext 栈通过
前面加上 #号实现
Struts 2中的表达式语言
Struts 2支持以下几种表达式语言:
1. OGNL(Object-Graph Navigation Language),可以方便地操作对象属性的开源表达式语言;
2. JSTL(JSP Standard Tag Library),JSP 2.0集成的标准的表达式语言;
3. Groovy,基于Java平台的动态语言,它具有时下比较流行的动态语言(如Python、Ruby和Smarttalk等)的
一些起特性;
4. Velocity,严格来说不是表达式语言,它是一种基于Java的模板匹配引擎,具说其性能要比JSP好。
Struts 2默认的表达式语言是OGNL,原因是它相对其它表达式语言具有下面几大优势:
1. 支持对象方法调用,如xxx.doSomeSpecial();
2. 支持类静态的方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名 | 值名],例
如:@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME;
3. 支持赋值操作和表达式串联,如price=100, discount=0.8, calculatePrice(),这个表达式会返回80;
4. 访问OGNL上下文(OGNL context)和ActionContext;
5. 操作集合对象。
“#”主要有三种用途:
1. 访问OGNL上下文和Action上下文,#相当于ActionContext.getContext();下面有几个ActionContext中有用的
属性:
parameters
包含当前HTTP请求参数的Map
#parameters.id[0]作用相当于request.getParameter("id")
request
包含当前HttpServletRequest的属性(attribute)的Map
#request.userName相当于request.getAttribute("userName")
session
包含当前HttpSession的属性(attribute)的Map
#session.userName相当于session.getAttribute("userName")
application
包含当前应用的ServletContext的属性(attribute)的Map
#application.userName相当于application.getAttribute("userName")
attr
用于按request > session > application顺序访问其属性(attribute)
#attr.userName相当于按顺序在以上三个范围(scope)内读取userName属性,直到找到为止
2. 用于过滤和投影(projecting)集合,如books.{?#this.price<100};
<s:iterator value="books.{?#this.price > 35}">
<li><s:property value="title" /> - $<s:property value="price" /></li>
</s:iterator>
<s:property value="books.{?#this.title=='Code Complete, Second Edition'}.{price}[0]"/>
问题:?#this的多个判断问题 &&
3. 构造Map,如#{'foo1':'bar1', 'foo2':'bar2'}。 之前讲过
%的特性 计算表达式 类似javascript中的eval函数 可用s:url举例说明%用法
“%”符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值
举例:用s:url或<s:set举例
<s:textfield key="state.label" name="state" value="%{'CA'}" />
计算boolean值(带有转义符的):
<s:select key="state.label" name="state" multiple="%{true}"/>
带有属性的:
<s:select key="state.label" name="state" multiple="allowMultiple"/>
既带有转义符又带有属性的:
<s:select key="state.label" name="state" multiple="%{allowMultiple}"/>
表示式语言符号
1.在Freemarker、Velocity或者JSTL的表达式语言的JavaBean对象的标准文本
<p>Username: ${user.username}</p>
2.在值栈中的一个username属性
<s:textfield name="username"/>
3. 引用值栈中的属性的另一种方式
<s:url id="es" action="Hello">
<s:param name="request_locale">
es
</s:param>
</s:url>
<s:a href="%{es}">Espanol</s:a>
4. 在Session Context中获得user对象的userName属性
<s:property name="#session.user.username" />
5. 在一个静态map中,像("username","trillian")一样
<s:select label="FooBar" name="foo" list="#{'username':'trillian', 'username':'zaphod'}" />
$”有两个主要的用途
1. 用于在国际化资源文件中,引用OGNL表达式,参考前面的国际化校验配置
2. 在Struts 2配置文件中,引用OGNL表达式,如
<action name="AddPhoto" class="addPhoto">
<interceptor-ref name="fileUploadStack" />
<result type="redirect">ListPhotos.action?albumId=${albumId}</result>
</action>
3
十 拦截器
拦截器,用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。
拦截器是AOP(Aspect-Oriented Programming 面向方面编程)中的一种实现策略。
1 用代理模式与动态代理编写拦截器
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,
然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。
当然啦,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,
在生成它的实例时你必须提供一个handler,由它接管实际的工作
那么如何实现动态代理呢
1 写一个动态代理,implements InvocationHandler接口,实现方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(sub,args);
return null;
}
2 调用 ,
真实实现类名 xxx = new 真实实现类名(); // 在这里指定被代理类
InvocationHandler ds = new DynamicSubject(xxx); // 初始化代理类
接口 接口变量= (接口) Proxy.newProxyInstance(xxx.getClass().getClassLoader(), xxx.getClass().getInterfaces(), ds);
接口变量.方法
/**
* 从以上可以看出,动态代理可以任意指定被代理的类,即真实对象类,而代理模式则无法实现该功能,
* 因为他把真实对象的写死在代理类里,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是
* 实际使用时,一个真实角色或其接口必须对应一个代理角色,如果大量使用会导致类的急剧膨胀
*/
2 struts2拦截器
解压strut2的核心包,根目录下struts-default.xml中有struts2的拦截器配置
拦截器几乎完成了Struts2框架70%的工作,包括解析请求参数、将请求参数赋值给Action属性、执行数据校验、
文件上传……,Struts2设计的灵巧性,更大程度地得益于拦截器设计,当需要扩展Struts2功能时,只需要提供对应拦
截器,
并将它配置在Struts2容器中即可;如果不需要该功能时,也只需要取消该拦截器的配置即可。
这种可插拔式的设计,正是软件设计领域一直孜孜以求的目标。
实际上,Struts2的精髓就在于拦截器,掌握了Struts2的拦截器机制,你就可以说精通了Struts2。
从某个角度来看,我们可以把Struts2框架理解成一个空壳,而这些拦截器像一个一个抽屉,随时可以
插进入,也可以拔出来——这是软件产品一直追求的目标。
如果你喜欢,你可以把Struts2的全部插件拔出,那么Struts2就成了一个空容器——
而这种空,正是 Struts2的魅力,你可以把任何自己想要的东西填入进去,甚至包括自己完全实现这个框架。
另一方面,因为Struts2的插件机制,Struts2提供了无限扩展的可能性,你可以把自己想要的任何
东西做成插件,然后填入Struts2——这样的结果是:一个企业,一个团队,可以把自己业务相关的东西
做成插件,随时随地地复用。
也就是说:如果你想要,你可以把Struts2改造成属于自己的框架。
当然,Struts2也内建了大量的拦截器,这些拦截器以name-class对的形式配置在struts-default. xml文件中,其中name是拦
截器的名字,就是以后使用该拦截器的唯一标识;class则指定了该拦截器的实现类,如果我们定义的package继承了Struts2的
默认struts-default包,则可以自由使用下面定义的拦截器,否则必须自己定义这些拦截器。
下面是Struts2内建拦截器的简要介绍:
alias:实现在不同请求中相似参数别名的转换。
autowiring:这是个自动装配的拦截器,主要用于当Struts2和Spring整合时,Struts2可以使用自动装配的方式来访问Spring
容器中的Bean。
chain:构建一个Action链,使当前Action可以访问前一个Action的属性,一般和<result type="chain" .../>一起使用。
conversionError:这是一个负责处理类型转换错误的拦截器,它负责将类型转换错误从ActionContext中取出,并转换成
Action的FieldError错误。
createSession:该拦截器负责创建一个HttpSession对象,主要用于那些需要有HttpSession对象才能正常工作的拦截器中。
debugging:当使用Struts2的开发模式时,这个拦截器会提供更多的调试信息。
execAndWait:后台执行Action,负责将等待画面发送给用户。
exception:这个拦截器负责处理异常,它将异常映射为结果。
fileUpload:这个拦截器主要用于文件上传,它负责解析表单中文件域的内容。
i18n:这是支持国际化的拦截器,它负责把所选的语言、区域放入用户Session中。
logger:这是一个负责日志记录的拦截器,主要是输出Action的名字。
model-driven:这是一个用于模型驱动的拦截器,当某个Action类实现了ModelDriven接口时,它负责把getModel()方法的结果
堆入ValueStack中。
scoped-model-driven:如果一个Action实现了一个ScopedModelDriven接口,该拦截器负责从指定生存范围中找出指定的Modol,并将通过setModel方法将该Model传给Action实例。
params:这是最基本的一个拦截器,它负责解析HTTP请求中的参数,并将参数值设置成Action对应的属性值。
prepare:如果action实现了Preparable接口,将会调用该拦截器的prepare()方法。
static-params:这个拦截器负责将xml中<action>标签下<param>标签中的参数传入action。
scope:这是范围转换拦截器,它可以将Action状态信息保存到HttpSession范围,或者保存到ServletContext范围内。
servlet-config:如果某个Action需要直接访问Servlet API,就是通过这个拦截器实现的。
注意:尽量避免在Action中直接访问Servlet API,这样会导致Action与Servlet的高耦合。
roles:这是一个JAAS(Java Authentication and Authorization Service,Java授权和认证服务)拦截器,只有当浏览者取得合适的授权后,才可以调用被该拦截器拦截的Action。
timer:这个拦截器负责输出Action的执行时间,这个拦截器在分析该Action的性能瓶颈时比较有用。
token:这个拦截器主要用于阻止重复提交,它检查传到Action中的token,从而防止多次提交。
token-session:这个拦截器的作用与前一个基本类似,只是它把token保存在HttpSession中。
validation:通过执行在xxxAction-validation.xml中定义的校验器,从而完成数据校验。
workflow:这个拦截器负责调用Action类中的validate方法,如果校验失败,则返回input的逻辑视图。
大部分时候,开发者无需手动控制这些拦截器,因为struts-default.xml文件中已经配置了这些拦截器,只要我们定义的包继承了系统的struts-default包,就可以直接使用这些拦截器。
当然,Struts2的拦截器机制并不是来自于Struts1,而是来自于webWork。
3 自定义拦截器
(1)编写拦截器类,继承AbstractInterceptor类 重写intercept(ActionInvocation arg0)方法
调用用参数类ActionInvocation的invoke方法,即 String result= arg0.invoke(); 返回该result=
invoke就是回调使用了该拦截器的action得相应方法,此时可在该方法执行前后加入我们想要的代码,达到我们拦截action的目的
利用 arg0.getAction()方法还可以得到拦截器拦截的action实例
public String intercept(ActionInvocation arg0) throws Exception {
// LoginAction loginaction=LoginAction(arg0.getAction());
System.out.println("执行ction之前");
String result= arg0.invoke();
System.out.println("执行ction之后");
return result;
}
、(2)在struts.xml配置拦截器
<package name="default" extends="struts-default" namespace="/">
<!--配置拦截器 -->
<interceptors>
<interceptor name="拦截器名" class="包名.拦截器类名">
<!--设置拦截器类属性值,如没有则不用设置 -->
<param name="拦截器类属性名">属性值</param>
</interceptor>
</interceptors>
<action name="login" class="com.LoginAction" >
<result name="success">/welcome.jsp</result>
<result name="error">/error.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref><!--struts-default包默认的拦截器栈 -->
<!--使用拦截器 -->
<interceptor-ref name="拦截器名"></interceptor-ref>
</action>
</package>
多个拦截器在一起组成一个拦截器栈
<interceptor-stack name="拦截器栈">
<interceptor-ref name="拦截器名1"/>
<interceptor-ref name="拦截器名2"/>
<interceptor-ref name="拦截器名3"/>
</interceptor-stack>
使用拦截器栈 与 使用拦截器一样语法
当配置一个包时,可以为其指定默认的拦截器,每个包只能有一个默认的拦截器,一旦为某个包指定了默认的拦截器,
如果该包中的action没有指定自己的拦截器,则action使用包指定的默认拦截器,但是一旦为action指定了自己拦截器,
则包的默认拦截器将会失效,如果还想使用原来包的默认拦截器,则必须显示的指定,如
<action name="login" class="com.LoginAction" >
<interceptor-ref name="defaultStack"></interceptor-ref><!--struts-default包默认的拦截器栈 -->
<!--使用我们的拦截器 -->
<interceptor-ref name="拦截器名"></interceptor-ref>
</action>
我们的包继承另一个包时,也继承了另一个包的默认拦截器,以及包里面的其他如(<result-types>)
当然我们可以定义自己包的默认拦截器覆盖之
从这里可以解释为什么我们刚刚开始学struts2的时候老是extends="struts-default"
因为struts-default包中的默认拦截器以及result-types是个好东东
如果不继承它,将失去result-types以及里面定义的拦截器功能,可以试着extends="struts-default"去掉,此时如
果struts.xml里有
result的配置的话,则tomcat启动的时候No result type specified for result named 。。。。。错误。我们可以把
里面的 result配置去掉
重新启动tomcat,去测试页面属性值是否能够封装到action属性中,答案不会 因为我们没有继承struts-default
配置默认拦截器 在 包package中 <default-interceptor-ref name="拦截器名或拦截器栈"></default-interceptor-ref>
放后面,action配置的前面
上面拦截器会拦截action中的所有方法 要想拦截某个方法怎么办 拦截指定方法
编写拦截器类,extends MethodFilterInterceptor MethodFilterInterceptor是AbstractInterceptor的子类
重写doIntercept方法
public String doIntercept(ActionInvocation invocation)
throws Exception
{
// LoginAction loginaction=LoginAction(arg0.getAction());
System.out.println("执行action方法之前");
String result= arg0.invoke();
System.out.println("执行action方法之后");
return result;
}
使用配置指定拦截的方法
<!-- 拦截器一般配置在result元素之后! -->
<interceptor-ref name="拦截器名1">
<param name="excludeMethods">execute,haha</param> <!-- 不拦截! -->
<param name="includeMethods">execute</param> <!-- 拦截! -->
</interceptor-ref>
可以重复使用一个拦截器或配置多个拦截器与拦截器栈,拦截器的执行顺序是在方法执行前,先配先执行,在方法执
行后,后配先执行
拦截结果的监听 是在action结束后,返回result之前的一个监听器,可以在该监听器里写我们的代码,
以便在返回结果前执行,
这个监听器通过手动注册在拦截器内部的
监听器示例代码 implements PreResultListener
public class MyPreResultListener implements PreResultListener
{
public void beforeResult(ActionInvocation invocation,String resultCode)
{
System.out.println("返回的逻辑视图为:" + resultCode);
}
}
拦截器示例代码
public class BeforeResultInterceptor extends AbstractInterceptor
{
public String intercept(ActionInvocation invocation) throws Exception
{
invocation.addPreResultListener(new MyPreResultListener()); //注册监听器
System.out.println("execute方法执行之前的拦截...");
String result = invocation.invoke();
System.out.println("execute方法执行之后的拦截......");
return result;
}
}
监听器代码在action结束后,返回结果前执行,和在action结束后在拦截器内部写的代码相比, 监听器代码把action结束
后的代码
放到监听器似乎更精确和清晰些
给拦截器栈传递参数时会出现一个问题,当拦截器栈中拦截器类中的属性名相同时,不知道这个参数到底要传给那个拦
截器,为了
解决这个问题,可作如下配置
<interceptor-ref name="拦截器栈名">
<param name="拦截器名(不是类名,注意哦).属性名">属性值</param>
</interceptor-ref>
十一 struts2的四个主题simple,xhtml(默认主题),css_xhtml和ajax,这4个主题的模板文件放在Struts2的核心类库里
(struts2-core.jar包)。template目录下
也可以自定义自己的主题,建议继承它的已有主题
simple主题是最简单的主题,它是最底层的结构,主要用于构建附加的功能或者行为(例如在此主题基础上进行扩展),
使用simple主题时,每个UI标签只生成一个简单的HTML元素,不会生成其他额外的内容。
Struts2的xhtml, css_xhtml主题都是对simple主题的包装和扩展。
xhtml主题是Struts2的默认主题,它对simple主题进行扩展,在该主题的基础上增加了如下附加的特性:
1,针对HTML标签(如textfield和select标签)使用标准的两列表格布局。
2,每个HTML标签的Label,即可以出现在HTML元素的左边,也可以出现在上边,这取决于labelposition属性的设置。
3,自动输出校验错误信息。
4,输出JavaScript的客户端校验。
css_xhtml主题则对原有的xhtml主题进行了扩展,在xhtml主题基础上加入了CSS样式控制。
ajax主题目对xhtml主题目进行了扩展,在xhtml主题上为每个标签提供了额外的Ajax支持。
ajax主题的Ajax支持是以Dojo和DWR为基础的。ajax主题在xhtml主题基础上增加了如下特性:
1,支持Ajax方式的客户端校验。
2,支持远程表单的异步提交(最好和submit标签一起使用)。
3,提供高级的div标签,允许实现局部更新部分HTML的功能。
4,提供高级的a标签,允许动态加载并执行远端的javaScript代码。
5,提供支持ajax的tabbedPanel。
6,提供"富客户端"模型的pub-sub事件模型。
5,Struts2的表单标签
表单元素属性值
labelposition属性:top/left
自定义主题
如果简单改变个别 UI 的风格,继承就是最为高效的了。此继承与 Java 的继承(即extends) 也是一马事。
要改变或要新加的用自己定义的,其他的延用父主题的。举个例子,自定义一个名为 custom 的主题继承自 xhtml,
只改变 select 标签的风格,你要做的就是编辑自己的一个 select.ftl 放在 WEB-INF/classes/template/custom 下,
同时也拷贝ontrolheader.ftl,controlfooter.ftl,在Eclipse中可以使src/template/custom目录
select.ftl 中的内容
我是新张
<#include "/${parameters.templateDir}/xhtml/controlheader.ftl" />
<#include "/${parameters.templateDir}/simple/select.ftl" />
<#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />
并在此目录下放一文件 theme.properties,内容是:
#指定该主题以 xhtml 为基础进行扩展
parent=xhtml
使 用可指定给 UI 标签,例如 <s:form name="aa" theme="custom" ...,效果就会是 form 下的 select 使用了
在 custom 目录下
自定义的 select.ftl,其他的直接使用父主题 xhtml 中的模板文件
十二 零配置
零配置 约定大于配置原则 使用注解编程替代xml配置 首先要澄清一点,这里说的零配置并不是一点配置都没有,
只是说配置很少而已
2.0的零配置
web.xml配置加载的action所在的包名,在过滤器中作如下修改
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
<init-param>
<param-name>actionPackages</param-name>
<param-value>com</param-value>
</init-param>
</filter>
意思是说要加载com包下的aciton 命名空间默认为"/"
在action类中配置
@Namespace("/aaa")
@Results({
@Result(name="success", type=NullResult.class, value="/success.jsp")
})
例如
@Results({
@Result(name="success",type=NullResult.class,value="/xxx.jsp")
})
public class Test extends ActionSupport {
public String execute() throws Exception {
return super.execute();
}
直接访问http://localhost:8080/struts20zero/类名(首字母小写).action
当类名以Action结尾时候,则Action可以省略
如类名XxxAction,则访问 直接访问http://localhost:8080/struts20zero/xxx.action
如果有Xxx类的action。如果实在同一命名空间下,前面的应该会得到匹配
如果是早不同的包名下,则与包名的字母排列顺序有关,后面的一半会执行
2.1的Convention Plugin插件
包命名习惯来指定Action位置
"
命名习惯制定结果(支持JSP,FreeMarker等)路径
"
类名到URL的约定转换
"
包名到命名空间(namespace)的约定转换
"
遵循SEO规范的链接地址(即:使用my-action 来替代 MyAction)
"
基于注解的Action名
"
基于注解的拦截机(Interceptor)
"
基于注解的命名空间(Nameespace)
"
基于注解的XWork包
"
默认action以及默认的结果(比如:/products 将会尝试寻找com.example.actions.Products 或
com.example.actions.products.Index进行处理)
struts2.1版本 web.xml配置
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern >*.action</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
struts.xml 配置启用Convention Plugin插件
<?xml version="1.0" encoding="GBK" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.convention.action.packages" value="struts"></constant>
</struts>
Convention会默认所有的结果页面都存储在WEB-INF/content下 。可以修改
<constant name="struts.convention.result.path" value="/"/>
Convention使用以下方法来搜索类路径,首先,Convention会从根package中寻找包名含有struts, struts2, action
or actions 的任意packages
1) 如果该Action类名包含Action后缀,将该Action类名的Action后缀去掉。否则不做任何处理。
2) 将Action类名的驼峰写法(每个单词首字母大写、其他字母小写的写法)转成中画线写法(所有字母小写,单词与单
词之间以中画线隔开)
如:LoginAction映射的Acion的name属性为login,GetBooks映射的Action的name的属性为get-books,
AddEmployeeAction映射的Action的name属性为add-employee
默认情况下。Convention总会到WEB应用的WEB-INF/content路径下定位物理资源,定位资源的约定是:
actionRUL+resultCODE+suffix。当某个逻辑视图找不到对应的试图资源时,Convention会自动视图使用
ActionURL+suffix作为物理视图资源。
如:actions.fore.LoginAction 返回success字符串时,Convention优先考虑使用WEB-INF/content/fore目录
下的login-success.jsp作为视图资源。
如果找不到该文件,login.jsp也可作为对应的试图资源。如果返回input字符串,Convention会
将WEB-INF/content/fore里面查找login-input.jsp
如果此时访问http://localhost:8080/应用名/hello,则Convention Plugin插件会去搜索Hello的Action类是否存在
提供的搜索包
(struts, struts2, action or actions)中,找不到,则报找不到错误,如果找到了Hello类,则看一下常量struts.convention.result.path
的结果放在哪里(如jsp的目录),到相应的目录下寻找hello.jsp页面
如果此时访问http://localhost:8080/应用名/hello-world,则寻找HelloWorld类和hello-world.jsp -在对应规
则中单词的首字母大写
有如下语法
@ResultPath("/WEB-INF/jsps")
@Namespace("/custom")
@InterceptorRefs({
@InterceptorRef("timer"),
@InterceptorRef("defaultStack")
})
public class LoginAction extends ActionSupport {
private String str;
@Actions({
@Action(value="login1",params={"str","这是已经注入的了!"}),
@Action(value="login2")
})
public String login(){
return "str";
}
@Action(value="ggg")
public String abc(){
return "abc";
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
我们可以通过/login1.action访问,而在访问时,str这个属性已经有值,为str=”这是已经注入的!”
返回的视图是login1-str.jsp
当我们用/login2.action访问时,str的值为null。返回的视图为 login2-str.jsp
而我们通过/ggg.action调用的是abc()方法,返回的视图为/ggg-abc.jsp
actioncontext是被存放在当前线程中的,获取ActionContext也是从ThreadLocal中获取的。所以在执行拦截器、 action和result的过程中,
由于他们都是在一个线程中按照顺序执行的,所以可以可以在任意时候在ThreadLocal中获取 ActionContext。
ActionContext包括了很多信息,比如Session、Application、Request、Locale、ValueStack等,
其中 ValueStack可以解析ognl表达式,来动态去一些值,同时可以给表达式提供对象。值栈是建立在ognl的基础之上的。1 Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
返回 extraContext 该map信息如下
把 servlet 中paramtert request session application 转换成map,这样struts2就脱离了servlet api
并且 把我们转化后的一个个map放入ActionContext,同时在返回的extraContextke中有paramter,request,session,application,attr
这些key值所对应的value值就是上面的一个个map
还有 一个attr AttributeMap request-sessio-application
最后
以上就是顺利电脑为你收集整理的struts2课堂笔录_1001的全部内容,希望文章能够帮你解决struts2课堂笔录_1001所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复