1.系统开发流程分层
2.struts2属于Mvc层
Struts2的下载地址
目录结构:
3.struts2的例子
(1)解压struts2-blank.war包
(2)创建web项目;
(3)导入必要的jar包;
struts2-core-2.3.32.jar Struts2的核心包
xwork-core-2.3.32.jar WebWork的核心包
ognl-3.0.19.jar 对象图导航语言(Object Graph Navigation Language)Struts2 通过其读写对象属性
freemarker-2.3.22.jar Struts2的UI标签的模板使用freeMark编写
commons-logging-1.1.3.jar 日志包
commons-fileupload-1.3.2.jar 文件上传组件
commons-io-2.2.jar 传文件依赖的jar包
commons-lang3-3.2.jar 对java.lang包的增强
javassist-3.11.0.GA.jar 分析,编辑和创建Java字节码的类库
(4)编写jsp页面;
(5)编写action服务器端处理逻辑;
Action就是处理request请求的动作类,类似于servlet
(6)进行框架配置web.xml,struts.xml
struts.xml配置
1)在src文件下创建struts.xml文件,从APP例子中直接复制struts.xml的内容,然后删除<struts>标签里的内容,作为struts.xml的空文件
2)配置xml/success.jsp
web.xml配置
直接拷贝App例子中的web.xml文件,然后<web-app>标签里面只留<filter>和<filter-mapping>标签
struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter struts2 /*
(7)测试;
4.struts的运行流程
1.启动服务(tomcat),加载web.xml,实例化StrutsPrepareAndExecuteFilter过滤器
2.在实例化StrutsPrepareAndExecuteFilter过滤器时会执行其中的init()方法,加载struts.xml文件
3.浏览器发起请求,会被StrutsPrepareAndExecuteFilter过滤器拦截到,根据请求的url(hello),找到相应的Action类,并且创建Action对象,执行相应的hello方法。
4.返回视图success.jsp
5.struts2的配置文件
配置文件名称 | 位置 | 存储的内容 | 说明 |
---|---|---|---|
default.properties | struts2-core-2.3.32.jar/org/apache/struts2/default.properties | 通过属性的形式来配置struts2的参数 | 不能直接修改 |
struts-default.xml | struts2-core-2.3.32/struts-default.xml | struts2的核心配置文件 | 不能直接修改 |
struts-plugin.xml | ****-plugin.jar | 插件相关配置 | 不能直接修改 |
struts.xml | 放在classpath | 应用配置文件 | 开发人员使用,可以修改 |
struts.properties | 放在classpath | 应用配置文件(较少使用,不推荐) | 开发人员使用,可以修改 |
以上配置文件,服务器启动时会被加载,加载次序从上到下加载(启动服务-->过滤器StrutsPrepareAndExecuteFilter-->过滤器的init方法加载配置文件),按着配置文件的加载顺序,后加载的文件会把前面文件相同的配置的值覆盖。
(1)default.properties
参数名称 | 参数默认值 | 说明 |
---|---|---|
struts.i18n.encoding | UTF-8 | 框架使用的编码 |
struts.action.extension | action,, | 请求的后缀名 |
struts.serve.static.browserCache | true | 是否开启浏览器的静态缓存 |
struts.configuration.xml.reload | false | 每当struts.xml被修改时是否热部署 |
struts.devMode | false | 是否是开发者模式(包含上一属性) |
通过<constant>标签配置来间接修改default.properties的属性,name和参数名称相同,value设置属性值
例如:
(2)struts-default.xml
该文件是struts的核心文件,里面提供了结果的返回类型和拦截器以及业务bean
结果的返回类型
拦截器
(3)struts.xml
开发人员编写的文件
包:package根据项目模块来划分的一个单元,一般在项目开发中,一个模块一个package
属性:
name:包的名称,必须属性;
extends:包的继承,默认情况下我们必须继承struts-default,否则无法使用struts框架;
abstract:抽象包,不能包含action,其他的都可以有;
namespace:包的命名空间,值必须为“/***”格式,namespace用于请求访问某一个包的指定路径,目的区分不同的包的相同的action
动作:action每次请求所访问的方法
属性:
name : 必须要有,访问方法是根据name来访问,http://localhost:8080/struts2_01/hello.action,后缀无需指定
class : 要访问的方法所在类
method : 方法名
结果:result要跳转的视图
属性:
name:result的名称,是唯一的,返回哪一个视图是由action的返回值决定的,action返回值和result的name属性做匹配传,从而返回相应的视图
type : 跳转视图的方式,默认跳转方式是请求转发,地址栏没变
6.struts2的动作类(Action)
1)动作类的创建方式:
(1)使用普通方式JavaBean作为动作类,不需要继承任何父类,不需要实现任何接口
.方法一定是公用的(public)
.返回值是字符串来决定跳转到哪个视图
.不需要参数
.方法名自定义,如果不自定义,默认访问execute
(2)创建动作类,实现action接口
接口:com.opensymphony.xwork2.Action
提供的一些常量
常量 | 值 | 说明 |
---|---|---|
SUCCESS | success | 返回成功页面 |
NONE | none | 不返回任何页面 |
ERROR | error | 返回错误页面 |
INPUT | input | 当提交表单发生错误时,就返回提交表单页面 |
LOGIN | login | 返回登录页面 |
(3)*创建动作类,继承父类(推荐使用)
父类:com.opensymphony.xwork2.ActionSupport
2)Action动作类的生命周期
创建:Action动作类每次请求的时候都会创建一个实例对象
销毁:当Action动作类的请求响应完后就消失了
跟JavaWeb中HttpServletRequest的生命周期是一样的,struts2是多例的,线程安全的
3)Action动作类的访问
通配符
原则:约定优于配置
在action中每个方法都要有一定的规则,eg.都以User结尾
*是请求是输入的
注意:一个包里面只能有一个通配符Action
动态方法调用
首先打开动态方法的开关
我们的action动作类无需指定方法的method属性了,而是直接在url上指定
语法:!方法名[.后缀名]
eg.
4).结果视图的配置
/success.jsp
result : 要返回的视图
属性
name:action动作类要返回的值,如果的action动作类的返回值与result的name值匹配,则进行相应的跳转
type:跳转方式
type的常用值有:
dispatcher : 请求转发,地址栏不发生变化,相当于JavaWeb中的forward,当我们没有指定type的类型时,默认值是dispatcher
redirect:页面重定向,地址栏发生变化
chain:请求转发到一个action动作类,地址栏不发生变化(我测试的时候地址栏发生了变化,暂时不明白为什么)
redirectAction:重定向到一个action动作类,地址栏发生变化
同包的重定向:
不同包的重定向:result需要通过param来指定包的namespace和action的name
stream:文件的上传和下载
5).局部结果视图和全局结果视图
在一个action中配置的result是局部结果视图,外部的action是不能使用这个result的
包级别的全局 (包内的全局结果视图对包内的每一个action动作类都有效)
/error.jsp hello2 /success2.jsp
全局的结果视图 (整个系统的全局结果视图通过继承的方式来实现)
/error.jsp /success2.jsp
7.struts动作类获取ServletAPI
(1).使用ServletActionContext获得ServletAPI
public String hello(){ /*获得page域对象,获取不到(很少使用)*/ PageContext pageContext=ServletActionContext.getPageContext(); /*获取request*/ HttpServletRequest request=ServletActionContext.getRequest(); /*获取response*/ HttpServletResponse response=ServletActionContext.getResponse(); /*获取session*/ HttpSession session=request.getSession(); /*获取application(请求)*/ ServletContext context=ServletActionContext.getServletContext(); System.out.println(pageContext); System.out.println(request); System.out.println(response); System.out.println(session); System.out.println(context); return "success"; }
(2).通过实现接口的方式来获取ServletAPI
接口:
org.apache.struts2.interceptor.ServletRequestAware;
org.apache.struts2.interceptor.ServletResponseAware; org.apache.struts2.util.ServletContextAware;public class HelloAction2 implements ServletRequestAware,ServletResponseAware,ServletContextAware{ HttpServletRequest request; HttpServletResponse response; ServletContext context; public String hello(){ System.out.println(request); System.out.println(response); System.out.println(request.getSession()); System.out.println(context); return "success"; } @Override public void setServletRequest(HttpServletRequest request) { this.request=request; } @Override public void setServletResponse(HttpServletResponse response) { this.response=response; } @Override public void setServletContext(ServletContext context) { this.context=context; }}
8.参数的封装
(1)静态封装
在运行器不发生变化的数据,或者一些配置相关的数据可以做静态封装
Action动作类 (用于接受静态的参数,必须提供set和get方法)
public class HelloAction { private String name = "zb"; private String nn; public String hello() { System.out.println(name); System.out.println(nn); return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNn() { return nn; } public void setNn(String nn) { this.nn = nn; }}
struts.xml配置 (<param>的name在action中必须有相应的属性,并且必须提供set和get方法,否则无法注入)
dzb NN /success.jsp
(2)动态参数的封装
系统运行期间用户提交表单,Ajax请求,url访问。
1)动作类充当模型对象
动作类和模型合为一体,可以在动作类定义要接收的属性的值,对每个属性必须要提供set和get方法,动作类model属性一定要和表单中的name一致,否则无法注入。
jsp页面:
动作类model;
public class HelloAction2 { private String name; private int age; private String sex; public String hello() { System.out.println(name); System.out.println(age); System.out.println(sex); return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; }}
2)动作类和模型对象分离封装参数(推荐使用)
动作充当模型对象,动作类既是C层又是M层,可读性差,重用性差,很难维护,不推荐使用第一种方式。 这种方式接受参数对提交的表单中的name有要求,name值需要使用Action动作类的Model属性的名字加上点再加上要接收的属性名,如person.name;
jsp页面:
model类:
public class Person { private String name; private int age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { System.out.println(name); return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]"; }}
Action动作类:
public class HelloAction3 { private Person person; public String hello() { System.out.println(person); return "success"; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; }}
3)模型驱动方式封装参数(推荐使用)
第二种方式对页面上的文本域name的属性值有要求,必须要用model属性加上点再加上接受属性的名称,这样的话页面和Action动作类有侵入性(耦合性),模型驱动方式解决了这个问题,我们需要实现一个ModelDriven接口,使用接口中的getModel方法指定要接受的Model的类型,并且Action动作类中的model对象必须手动创建,否则无法注入属性。
jsp页面:
Model类跟上一种方式的一样:
public class Person { private String name; private int age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { System.out.println(name); return "Person [name=" + name + ", age=" + age + ", sex=" + sex + "]"; }}
Action动作类:
public class HelloAction4 implements ModelDriven{ private Person person = new Person(); public String hello() { System.out.println(person); return "success"; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } @Override public Person getModel() { // TODO Auto-generated method stub return person; }}
9.类型转换
(1)类型转换认识
页面所提交过来的数据都是字符串类型的数据,而model里面的数据并不都是字符串数据,有各种各样的类型,我们使用servlet接收数据时都是自己手动转换的,而struts2可以为我们自动转换,转换的前提是前端页面提交过来的字符串数据可以和model中相应的数据类型可以转换。
提交表单时:字符串类型 ----> 其他数据类型
展示页面时:其他数据类型 ---> 字符串类型
表单提交数据时,数据的转换虽然struts2做了大多数,但是时间类型往往需要我们根据实际情况手动做相应的转换。
(2)自定义类型转换
日期转换:
默认情况下,struts2解析的时间的格式是“yyyy-MM-dd”
第一步:通过自定义类型转换器继承StrutsTypeConverter做日期转换器
public class DateConverter extends StrutsTypeConverter { @Override public Object convertFromString(Map context, String[] values, Class toClass) { Date date = null; if (values != null && values.length > 0) { if (toClass == Date.class) { try { date = new SimpleDateFormat("yyyy/MM/dd").parse(values[0]); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException(); } } } return date; } @Override public String convertToString(Map context, Object o) { String dateStr = null; if (o != null && o.getClass() == Date.class) { dateStr = new SimpleDateFormat("yyyy/MM/dd").format((Date) o); } return dateStr; }}
第二步:注册日期类型转换器
局部注册:
在要转换的model类的同级包下创建一个属性文件, 命名要求:model类名-conversion.properties 在文件的内部,key是要转换的属性名,value是转换器的全路径
全局注册:
如果有很多的Model类都有时间类型,那么使用局部注册就需要创建很多注册文件,这样不合理,这种情况就需要全局注册
(3)获取页面的错误提示信息:
在jsp页面添加
<%@ taglib uri="/struts-tags" prefix="s"%>
然后用
输出错误
把错误信息转换成中文:
第一步:在Action的同级包下创建一个属性文件,规则:和Action类同名.properties
第二步:Key : invalid.fieldvalue.[要转换的表单中文本域的name],value是中文的提示信息,中文在properties文件中以Unicode编码的形式存在
10.struts2数据校验
验证的方式有两种
客服端校验:使用js结合正则表达式来校验,不和服务器打交道,开发简单,安全性差,
服务端校验:请求服务器,开发量比较大,安全性好,需要和数据库交互必须使用服务端校验。
实际开发中,客服端校验较多,最好是客服端和服务端都校验。
strusts2的校验属于服务器端校验。
1.struts2编程式校验
编程式校验,重写父类(ActionSupport)的validate方法,在这个方法中,对每一个字段做校验,如果参数不符合正则表达式,我们可以添加提示信息,由addFieldError方法来向页面输出提示信息。但是校验的前提是提交过来的参数是能互相转换的,如果转换不了的话会由param,modelDriven拦截器来负责。
/** * validate在封装参数之前做验证,如果不通过就跳转到表单页面 */ @Override public void validate() { if (!person.getName().matches("[a-zA-Z]{3,8}")) { /* 添加提示信息,第一个参数是表单中文本域的name,第二个参数是提示信息*/ addFieldError("name","姓名只能是3到8位大小写字母!"); } if (!(person.getAge()+"").matches("\\d{1,3}")) { /* 添加提示信息,第一个参数是表单中文本域的name,第二个参数是提示信息*/ addFieldError("age","年龄只能是1到3位数字!"); } }
2.struts2声明式校验