exception="异常类型如java.lang.Exception" >
如假设有result中配置/geror.jsp
则在geror.jsp页面可以使用Struts2的标签来输出异常信息
全局异常 所有的action共用 局部异常 自己的action用 当两者重名时,优先选择局部异常
12类型转换
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 则如下
在页面输入值后 action中bean属性名为user可以直接拿到值
<2>直接把页面属性转换成action中的集合属性,不需要转换器
map版
第一个用户名:
第一个密码:
第二个用户名:
第二个密码:
list或数组版本
第一个用户名:
第一个密码:
第二个用户名:
第二个密码:
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信息:
请输入用户2信息:
3 错误处理
当类型转换出现错误,自动转发到input页面, 在页面用 显示全部错误
在资源文件修改默认的错误显示信息
xwork.default.invalid.fieldvalue={0}字段类型转换失败!
13 校验
1 继承Actionsupport 重写validate方法
this.addFieldError("username", this.getText("xxx"));
this.addFieldError("username1", "yyy");
在页面用 显示全部错误
显示某个错误
2 validateXxx方法
3 strut2的校验流程
4 校验框架
每个Action类有一个校验文件,命名 Action类名-validation.xml,且与Action类同目录,
当校验文件的取名为ActionClassName-validation.xml时,会对 action中的所有处理方法实施输入验证。
如果你只需要对action中的某个action方法实施校验,那么,校验文件的取名应为:ActionClassName-ActionName-validation.xml,
其中ActionName为struts.xml中action的名称。例如:在实际应用中,常有以下配置:
/WEB-INF/page/message.jsp
/WEB-INF/page/addUser.jsp
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.7\com\opensymphony\xwork2\validator\validators\default.xml 文件中
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
必须输入名字
您输入的用户名只能是字母和数组,且长度必须在4到25之间
必须输入密码
您输入的密码只能是字母和数组,且长度必须在4到25之间
年纪必须在1到150之间
年纪必须在${min}到${max}之间
非字段校验
校验错误信息
举例
${getText("name.requried")}
${getText("name.regex")}
${getText("pass.requried")}
${getText("pass.regex")}
${getText("age.range")}
${getText("birth.range")}
校验国际化配置 如该种方式出错,则采用${getText("key名")}
向资源文件传递参数 ${getText("key名",{'aa','bb'})}
模型驱动的校验
模型驱动的配置 需要为模型驱动的action中bean提供get方法,当然喜欢加上set方法也可以
配置
模型驱动用户的:
visitor校验 2个校验文件 当属性为javabean时的校验 区别于模型驱动
第一个校验文件 Action类名-validation.xml中的配置如下
用户的:
jsp页面输入框示例
如
第2个校验文件 bean类名-第一个校验文件context参数值-validation.xml 如User-userContext-validation.xml,
该文件中就可以写校验bean中的属性,如同前面的校验
14访问 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;
}
15 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};
- $
问题:?#this的多个判断问题 &&
3. 构造Map,如#{'foo1':'bar1', 'foo2':'bar2'}。 之前讲过
%的特性 计算表达式 类似javascript中的eval函数 可用s:url举例说明%用法
“%”符号的用途是在标志的属性为字符串类型时,计算OGNL表达式的值
计算boolean值(带有转义符的):
带有属性的:
既带有转义符又带有属性的:
表示式语言符号
1.在Freemarker、Velocity或者JSTL的表达式语言的JavaBean对象的标准文本
Username: ${user.username}
2.在值栈中的一个username属性
3. 引用值栈中的属性的另一种方式
es
Espanol
4. 在Session Context中获得user对象的userName属性
5. 在一个静态map中,像("username","trillian")一样
$”有两个主要的用途
1. 用于在国际化资源文件中,引用OGNL表达式,参考前面的国际化校验配置
2. 在Struts 2配置文件中,引用OGNL表达式,如
ListPhotos.action?albumId=${albumId}
16拦截器
拦截器,用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。
拦截器是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包,则可以自由使用下面定义的拦截器,否则必须自己定义这些拦截器。
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配置拦截器
/welcome.jsp
/error.jsp
多个拦截器在一起组成一个拦截器栈
使用拦截器栈 与 使用拦截器一样语法
当配置一个包时,可以为其指定默认的拦截器,每个包只能有一个默认的拦截器,一旦为某个包指定了默认的拦截器,
如果该包中的action没有指定
自己的拦截器,则action使用包指定的默认拦截器,但是一旦为action指定了自己拦截器,
则包的默认拦截器将会失效,如果还想
使用包的默认拦截器,则必须显示的指定,我们的包继承另一个包时,也继承了另一个包的默认拦截器,
当然我们可以定义自己包的默认拦截器覆盖之
从这里可以解释为什么我们刚刚开始学struts2的时候老是extends="struts-default"
因为struts-default包中的默认拦截器是个好东东
配置默认拦截器 在 包package中 放后面
上面拦截器会拦截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;
}
使用配置指定拦截的方法
可以重复使用一个拦截器,拦截器的执行顺序是在方法执行前,先配先执行,在方法执行后,后配先执行
拦截结果的监听 是在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结束后的代码
放到监听器似乎更精确和清晰些
给拦截器栈传递参数时会出现一个问题,当拦截器栈中拦截器类中的属性名相同时,不知道这个参数到底要传给那个拦截器,为了
解决这个问题,可作如下配置
17 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的表单标签
18零配置
零配置 约定大于配置原则 使用注解编程替代xml配置 首先要澄清一点,这里说的零配置并不是一点配置都没有,只是说配置很少而已
2.0的零配置
web.xml配置加载的action所在的包名,在过滤器中作如下修改
struts2
org.apache.struts2.dispatcher.FilterDispatcher
表名要加载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。如果实在同一命名空间下,前面的应该会得到匹配
如果是早不同的包名下,则与包名的字母排列顺序有关,后面的一半会执行
19Convention Plugin插件
包命名习惯来指定Action位置
"
命名习惯制定结果(支持JSP,FreeMarker等)路径
"
类名到URL的约定转换
"
包名到命名空间(namespace)的约定转换
"
遵循SEO规范