1.1 Struts2中的请求参数的封装
1.1.1 请求参数的封装
使用Servlet API接收到表单提交的参数,需要自己手动完成封装.
* Action---->Service---->Dao都需要在Action中将数据封装到一个Bean中传递到业务层.
Struts2中提供了一种方式完成对数据的封装.
* 数据的封装分成两大类:
* 属性驱动
* 1.在Action中声明属性并提供属性的set方法.
* 2.在页面OGNL表达式形式
* 模型驱动
* 3.实现ModelDriven接口
提供set方法的方式:(属性驱动)
页面:
<h1>数据的封装:</h1>
<form action="${ pageContext.request.contextPath }/user1.action" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="提交">
</form>
Action:
public class UserAction1 extends ActionSupport{
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
...
}
页面提供OGNL表达式的形式:(属性驱动)
要求:在Action中提供对象的get方法.
页面:
<h1>数据的封装:OGNL表达式的形式</h1>
<form action="${ pageContext.request.contextPath }/user2.action" method="post">
用户名:<input type="text" name="user.username"/><br/>
密码:<input type="password" name="user.password"><br/>
<input type="submit" value="提交">
</form>
Action:
public class UserAction2 extends ActionSupport{
private User user;
public void setUser(User user) {
this.user = user;
}
public User getUser() {
return user;
}
@Override
public String execute() throws Exception {
System.out.println(user.getUsername());
System.out.println(user.getPassword());
return NONE;
}
}
模型驱动:实现一个接口ModelDriven.
页面:
<h1>数据的封装:模型驱动的形式</h1>
<form action="${ pageContext.request.contextPath }/user3.action" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="提交">
</form>
Action:
public class UserAction3 extends ActionSupport implements ModelDriven<User>{
private User user = new User();//必须手动构建这个对象
public User getModel() {
return user;
}
@Override
public String execute() throws Exception {
System.out.println(user.getUsername());
System.out.println(user.getPassword());
return NONE;
}
}
1.1.2 数据封装的分析:
数据的封装实质上是由拦截器完成的!!!
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
企业开发中采用第三种形式:
* Struts2的结构中,一部分的结构围绕着模型驱动来设计的.提供了一个单独的拦截器!!!
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
综合比较第二种和第三种方式:
* 第三种只能封装到一个对象中.
* ModelDriven<User>----只能将数据封装到User对象中.
* 第二种更加的灵活,封装到多个对象中.
* <input type=”text” name=”user.username”>
* <input type=”text” name=”product.price”>
1.1.3 复杂数据的封装:
将数据封装到一个List集合中
页面:
<h1>批量添加商品</h1>
<form action="${ pageContext.request.contextPath }/product1.action" method="post">
商品名称:<input type="text" name="products[0].pname"><br/>
商品价格:<input type="text" name="products[0].price"><br/>
商品名称:<input type="text" name="products[1].pname"><br/>
商品价格:<input type="text" name="products[1].price"><br/>
<input type="submit" value="添加">
</form>
Action:
public class ProductAction1 extends ActionSupport{
private List<Product> products;
public void setProducts(List<Product> products) {
this.products = products;
}
public List<Product> getProducts() {
return products;
}
@Override
public String execute() throws Exception {
System.out.println(products);
return NONE;
}
}
将数据封装到Map集合
页面:
<h1>批量添加商品</h1>
<form action="${ pageContext.request.contextPath }/product2.action" method="post">
商品名称:<input type="text" name="map['one'].pname"><br/>
商品价格:<input type="text" name="map['one'].price"><br/>
商品名称:<input type="text" name="map['two'].pname"><br/>
商品价格:<input type="text" name="map['two'].price"><br/>
<input type="submit" value="添加">
</form>
Action:
public class ProductAction2 extends ActionSupport{
private Map<String,Product> map;
public Map<String, Product> getMap() {
return map;
}
public void setMap(Map<String, Product> map) {
this.map = map;
}
@Override
public String execute() throws Exception {
System.out.println(map);
return NONE;
}
}
1.2 数据类型的转换:
1.2.1 Struts2提供了大量内置类型转换器:
boolean 和 Boolean
char和 Character
int 和 Integer
long 和 Long
float 和 Float
double 和 Double
Date 可以接收 yyyy-MM-dd格式字符串
数组 可以将多个同名参数,转换到数组中
集合 支持将数据保存到 List 或者 Map 集合
1.2.2 类型转换过程中的错误处理:
<input type=”text” name=”age”>---需要输入的是整数.但是如果输入了abc.将不能完成类型的转换!!!
* 抛异常:java.lang.NoSuchMethodException: cn.itcast.vo.Emp.setAge([Ljava.lang.String;)
* 输入abc---->setAge(String name).但是实体类中没有setAge(String name).抛出了一个没有这个方法异常.
当类型转换出错的时候:报一个没有配置INPUT逻辑视图的异常.
* 配置一个INPUT逻辑视图.转向回提交的页面.
* 回显错误信息:
* 引入struts2的标签库:
* 使用<s:fielderror/>标签回显:
* INPUT逻辑视图的原理:
* 这些功能都是由拦截器完成的!!!
1.2.3 自定义类型转换器:(了解)
类型转换本身就是一个双向的过程:
* JSP---->Action:String---->要的类型
* Action---->JSP:要的类型---->String
自定义类型转换器的方式:
1.实现TypeConverter接口
*
2.继承DefaultTypeConverter
*
3.继承StrutsTypeConverter
* convertToString(java.util.Map context, java.lang.Object o)
* convertFromString(java.util.Map context, java.lang.String[] values, java.lang.Class toClass)
自定义类型转换器:
public class MyDateConverter extends DefaultTypeConverter{
@Override
/*
* context:
* value:转换之前的类型
* toType:要转换成的类型
*/
public Object convertValue(Map<String, Object> context, Object value,
Class toType) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
if(toType == Date.class){
// JSP --- Action
String[] arrs = (String[]) value;
String s = arrs[0];
Date date = null;
try {
date = dateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
throw new RuntimeException();
}
return date;
}else{
// Action --- JSP
Date date = (Date) value;
String s = dateFormat.format(date);
return s;
}
}
}
注册类型转换器:
* 全局注册:
* 针对所有的日期类型的注册:
* 在src下创建一个文件:xwork-conversion.properties
* 编写:java.util.Date=cn.itcast.action.demo3.MyDateConverter
* 局部注册:
* 模型驱动的方式:
* 在实体类所在的包下创建一个(实体类名)-conversion.properties格式的文件
* Emp-conversion.properties
* 编写:birthday=cn.itcast.action.demo3.MyDateConverter
* 属性驱动的set方法的方式:
* 在Action所在的包下创建一个(Action类名)-conversion.properties格式的
* 编写:birthday=cn.itcast.action.demo3.MyDateConverter
类型转换的错误:
* 显示中文错误信息:
* 在Action所在的包下创建一个属性文件(国际化的处理)
* Action类名_zh_CN.properties
* key=value:invalid.fieldvalue.属性名=中文错误信息
1.3 数据的有效性校验:
1.3.1 数据的校验:
前台校验:JS校验.
* JS的校验不是必须的.JS可以被绕行的.JS提升用户体验.
后台校验:编码校验.
* 必须的校验.
Struts2框架中提供了2类的数据校验:
* 1.手动编码的方式进行校验:
* 继承ActionSupport类
* 重写validate方法:----针对Action中的所有的方法.
* 在这个方法中就可以编写校验的代码了.
* 针对Action中的某个方法进行校验:例如校验Action中add方法 或者 modify的方法.
* 编写一个与validate方法格式相同的方法,方法名称validate要校验的方法名.
* validateAdd()validateModify()
* 2.采用配置文件的方式进行校验:
* 需要提供对应属性的get方法才可以!!!
* 针对Action中的所有的方法校验
* 在Action所在的包下创建一个与Action类名相同-validation.xml.
* 引入DTD:
* xwork-core-2.3.7.jar有一个xwork-validator-1.0.3.dtd
* 进行配置:
<validators>
<!-- name:要校验的字段名 -->
<field name="username">
<field-validator type="requiredstring">
<message>用户名不能为空!(XML)</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>密码不能为空!(XML)</message>
</field-validator>
</field>
</validators>
* requiredstring就是校验规则:从com.opensymphony.xwork2.validator.validators.default.xml中可以查看校验规则
* 针对Action中的某一个方法校验:
* 在Action所在的包下创建一个文件与Action类名-要访问的方法对应的action的名称-validation.xml
* <action name=”login2” class=”cn.itcast.action.demo4.LoginAction2” method=”execute”/>
* 创建校验execute的文件:
* LoginAction2-login2-validation.xml
* Struts2提供的校验器:
required (必填校验器,要求被校验的属性值不能为null)
requiredstring (必填字符串校验器,要求被校验的属性值不能为null,并且长度大于0,默认情况下会对字符串去前后空格)
stringlength(字符串长度校验器,要求被校验的属性值必须在指定的范围内,否则校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去除字符串前后的空格)
regex(正则表达式校验器,检查被校验的属性值是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)
int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)
double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)
fieldexpression(字段OGNL表达式校验器,要求field满足一个ognl表达式,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过)
email(邮件地址校验器,要求如果被校验的属性值非空,则必须是合法的邮件地址)
url(网址校验器,要求如果被校验的属性值非空,则必须是合法的url地址)
date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)
conversion(转换校验器,指定在类型转换失败时,提示的错误信息)
visitor(用于校验action中复合类型的属性,它指定一个校验文件用于校验复合类型属性中的属性)
expression(OGNL表达式校验器,它是一个非字段校验器, expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不可用在字段校验器风格的配置中)
1.3.2 自定义校验器:(了解)
* 若需要普通的验证程序, 可以继承 ValidatorSupport 类---校验的时候不是针对一个字段的(密码和确认密码)
* 若需要字段验证程序, 可以继承 FieldValidatorSupport 类---针对一个字段的校验(用户名的校验)
* 编写一个类继承FieldValidatorSupport :
public class MyAgeValidator extends FieldValidatorSupport {
public void validate(Object obj) throws ValidationException {
// 获得字段名称:
String name = this.getFieldName();
// 获得字段的值:
Object value = this.getFieldValue(name, obj);
if(value instanceof Integer){
int age = (Integer) value;
if(age < 0){
this.addFieldError(name, obj);
}
}
}
}
* 注册校验规则:
* 在src下创建一个validators.xml
* 引入一个DTD:
* xwork-core-2.3.7.jar下的xwork-validator-config-1.0.dtd
* 配置:
<validators>
<validator name="aaa" class="cn.itcast.action.demo6.MyAgeValidator"></validator>
</validators>
* 使用校验规则:
<validators>
<field name="age">
<field-validator type="aaa">
<message>年龄不能为负数!</message>
</field-validator>
</field>
</validators>
1.4 Struts2中的国际化:
1.4.1 国际化:
软件的国际化:软件根据来访者的不同,自动切换语言进行显示.
提供一组资源包:
* 资源包需要有相同的基本名称.
* 命名规则.基本名称_语言_国家.properties
* ResourceBundle读取资源包.
1.4.2 Struts2中的国际化:
全局的国际化:(*****)
提供一组资源包:
* 放在src下
* message_zh_CN.properties
* message_en_US.properties
* 需要在struts.xml中开启一个常量:
<constant name="struts.custom.i18n.resources" value="message"/>
如何在其他地方引用國際化文件中配置好的文本信息
在Action类中
* String name = getText("name");
在JSP中
* <s:text name="name"/>
在国际化配置文件中
* <message key="login.username"/>
Action范围的国际化:
在Action类中使用
* 在Action所在的包下创建一个属性文件.---名字与Action的类名一致!!!
包范围的国际化:
在当前包以及当前包的子包下使用.
* 在某个包下创建属性文件.----package_语言_国家.properties
临时的国际化:
在页面JSP中的使用
<s:i18n name="cn/itcast/action/demo7/package">
<s:text name="package"></s:text>
</s:i18n>
1.5 Struts2中拦截器:
1.5.1 拦截器的原理:
拦截器和过滤器区别?
拦截器:Interceptor
* 拦截器拦截对Action的访问.(拦截器能拦截HTML/JSP 不能拦截.)---拦截可以拦截细粒度的Action访问.比如拦截add方法.
* AOP思想实现,是可以插拔的.
* AOP:面向切面编程.
过滤器:Filter
* 过滤器过滤的是客服端向服务器发送的请求.
Struts2核心就是拦截器: