struts2概述:
- struts2 应用于javaEE中三层框架的web层。
- struts2 是在struts1和webwork上面发展起来的全新框架。
- struts2 解决的问题:
第一个案例:
- 导入jar包
- web.xml配置过滤器
- 创建Action,默认执行execute方法
- 创建配置文件,名称(struts.xml)和位置固定(src下)
- 配置访问路径。
执行过程:
- 浏览器器请求
- 经过过滤器
- 过滤器通过请求路径拿到actionname
- 再从src中的struts.xml中找到actionname和action标签中的name属性匹配的action,再通过提供的class和反射技术来调用相应的方法。
- 通过得到的返回值,根据result的name属性配置,跳转到相应的页面。
struts2核心配置:
- 具有package,action,result三种主要的标签。
- package标签:
2.1 name属性:与功能本身并没有关系,目的只是为了区分不同的Action,可以随便写,但是同一个配置文件中name不能相同。
2.2 extends:是固定值,struts-default 只有配置了这个,Action的配置才有效。
2.3 namespace: 它的值与Action中的name构成访问路径,例如namespace=”/a” action中的name=”hello” 那访问路径是/a/hello,一般填写”/”,且默认值也为”/”。 - action标签:
3.1 主要配置访问路径
3.2 在一个package中可以有多个action标签,但是action的name属性值不能相同的。
3.3 name属性:与namespace的值构成访问路径。
3.4 class属性:action类的全路径名(包名+类名)
3.5 method 属性:可以让action中的多个方法执行 - result标签:
4.1 根据action的返回值进行相应的跳转。
4.2 name属性:方法返回值
4.3 <result>/xxx</result>其中的’/’是固定写法。
4.4 type属性:配置跳转的方式,例如转发或者重定向,默认是转发。 常量配置(constant标签):
5.1 一般封装了一种功能。
5.2 name属性:常量的名字。
5.3 value属性:常用的值。
5.3 最常用的常量:struts.i18n.encoding=utf-8,解决post提交的编码问题,不需要自己处理。分模块开发:
有多个xml文件,直接在src中的struts.xml中引入这些文件即可,通过<include file=”renwen/com/hello.xml></include>”,记住最前面没有’/’,
action的编写方式:
- 创建普通类,不继承任何类,也不实现任何接口
创建一个类,实现接口Action
2.1 实现execute()方法。
2.2 Action接口中所定义的常量:
2.2.1 ERROR = “error”;
2.2.2 INPUT = “input”;
2.2.3 LOGIN = “login”;
2.2.4 NONE = “none”;
2.2.5 SUCCESS =”success”;创建一个类,继承ActionSupport(最常用)
3.1 ActionSupport 已经实现了Action接口
访问action的方法
- 直接在action标签中的method属性中填写需要执行的方法
- 使用通配符:在action的name属性中写上*来充当通配符,*代表任意字符
- 动态访问实现(基本不用)
- 注意事项:
4.1 action中的方法可以没有返回值,但是如果有,一定是string
4.2 action中的返回值在result中如果没有找到,会404。
4.3 没有返回值,可以通过方法写void,或者返回字符串NONE(none),则result中不需要配置。
结果页面的配置:
- result标签配置action方法的返回值进行相应的跳转。
- 全局结果页面配置:当多个action的返回值有重复的,且跳转的目的地也是相同的,就可以用全局结果页面配置,且只在同一个package中有效.
改为:
- 局部结果界面配置:
当局部和全局进行了相同的结果配置,局部优先。 - result的type属性配置:确定以何种方式进行跳转。
4.1 type的属性值:dispatcher(转发,默认值),redirect,chain(转发到action,一般不用),redirectAction(重定向到action),前面两种一般针对跳转到页面,而后面的针对跳转到action。
获取表单数据的方式:
- 通过ActionContext
注意得到map集合后,通过get(key)得到的是一个Object[]数组。 - 通过ServletActionContext
大部分是静态方法,直接可以通过类名调用。 - 通过接口注入(一般不用)
获取域对象:
- ruquest获取:ServletActionContext.getRequst();
- response获取: ServletActionContext.getResponse();
- session获取:通过request来获取。
- ServletContext获取: ServletActionContext.getServletContext();
数据封装:
- 原始数据封装:
属性封装:
public class BookAction extends ActionSupport{ private String sname; private String spwd; public String update(){ System.out.println(sname); System.out.println(spwd); return NONE; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSpwd() { return spwd; } public void setSpwd(String spwd) { this.spwd = spwd; } }
注意属性的名字必须和表单项的name属性一样,且必须实现属性的setter和getter方法。
使用模型驱动封装(重点)
public class BookAction extends ActionSupport implements ModelDriven<Student>{ private Student stu=new Student(); public String update(){ System.out.println(stu); return NONE; } @Override public Student getModel() { // TODO Auto-generated method stub return stu; } }
必须实现ModeDriven接口,且实体类的属性必须和表单的name属性一致,实体类有getter和setter方法,且在action中创建实体类对象。
表达式封装:
public class BookAction extends ActionSupport{ private Student stu; public String update(){ System.out.println(stu); return NONE; } public Student getStu() { return stu; } public void setStu(Student stu) { this.stu = stu; } }
必须声明该实体类对象,同时创建setter和getter方法,在表单应该使用表达式语言,以及注意表单name属性如何书写。
<form action="update.action" method="post"> <input type="text" name="stu.sname"/> <input type="text" name="stu.spwd"/> <input type="submit"/> </form>
- 注意:
5.1 不能既用属性封装又用模型驱动封装,两个都用,只会执行模型驱动。
5.2 表达式封装可以把数据封装到不同的实体类中,但是模型驱动封装只能把数据封装到同一个实体类中
将表单数据封装到集合中
封装数据到List中
public class BookAction extends ActionSupport{ private List<Student>list;//声明list集合,提供setter和getter方法 public String update(){ for(Student stu:list){ System.out.println(stu); } return NONE; } public List<Student> getList() { return list; } public void setList(List<Student> list) { this.list = list; } }
<form action="update.action" method="post"> <input type="text" name="list[0].sname"/> <input type="text" name="list[0].spwd"/> <input type="submit"/> </form>
封装数据到Map中
public class BookAction extends ActionSupport{ private Map<String,Student>map;//声明map集合 public String update(){ System.out.println(map.get("one")); return NONE; } public Map<String, Student> getMap() { return map; } public void setMap(Map<String, Student> map) { this.map = map; } }
<form action=”update.action” method=”post”>
<input type=”text” name=”map[‘one’].sname”/>
<input type=”text” name=”map[‘one’].spwd”/>
<input type=”submit”/>
</form>
OGNL概述:
之前在javaweb阶段学过EL表达式,用于获取域对象里面的值,而OGNL也是类似于EL的一种表达式,只是功能更加的强大。
但是在struts2中主要的作用是操作值栈中的数据,和struts2中的标签联合使用(后面讲到),但是OGNL并不是struts2的一部分,只是他们经常一起使用,使用之前,得先导入OGNL的jar包。
小案例:
上面的例子会输出字符串的长度,但是在web.xml中过滤器<url-pattern>中必须中/*,现在暂时不知道为什么。
值栈
- 什么是值栈
之前在javaweb阶段,数据是存储在域对象中,在页面中进行获取,现在提供了一种类似的机制—-值栈。 - servlet和action的区别
2.1 默认在第一次访问的时候创建,只是创建一次,线程不安全。
2.2 action也是访问的时候创建,每次访问创建一次,线程安全。
2.3 每个action中存在一个值栈对象,仅有一个。
获取值栈的方式:
- 通过ActionContext,常用
- 值栈的内部结构
2.1 第一部分结构:root(List集合,常用)
2.2 第二部分接口:context(Map集合),存储的一些对象的引用
向值栈中放数据:
- 获取值栈对象,调用值栈中set的方法。
- 获取值栈对象,调用push方法。
- 在action定义一个成员变量,生成变量的get方法。
- 在值栈中的栈底保存了对于action的引用,同时如果采用第三种方法进行放入数据,则该数据是放在action中的。
从值栈中获取数据:
利用ognl和struts2的标签来进行数据的获取。
获取字符串
public class BookAction extends ActionSupport{ private String name="123"; public String update(){ return "ok"; } public String getName() { return name; } } <s:property value="name"/>
从值栈中获取对象:
public class BookAction extends ActionSupport{ private Student stu=new Student(); public String update(){ stu.setSname("zhangsan"); stu.setSpwd("123"); return "ok"; } public Student getStu() { return stu; } public void setStu(Student stu) { this.stu = stu; } } <s:property value="stu.sname"/> <s:property value="stu.spwd"/>
从值栈中获取List
3.1: 第一种方式,不常用,写死了。public class BookAction extends ActionSupport{ private List<Student>list=new ArrayList<Student>(); public String update(){ Student s1=new Student(); Student s2=new Student(); s1.setSname("123"); s1.setSpwd("455"); s2.setSname("789"); s2.setSpwd("897"); list.add(s1); list.add(s2); return "ok"; } public List<Student> getList() { return list; } } <s:property value="list[0].sname"/> <s:property value="list[0].spwd"/> <s:property value="list[1].sname"/> <s:property value="list[1].spwd"/>
3.2 第二种
java代码与第一种方法一样<s:iterator value="list" > <s:property value="sname"/> <s:property value="spwd"/> </s:iterator>
3.3 第三种
java代码与第一种方法一样<s:iterator value="list" var="it"> <s:property value="#it.sname"/> <s:property value="#it.spwd"/> </s:iterator>
为什么不是it.sname呢?因为it存储在context中,it.sname,默认it在root中获取,为了以示区分所以加了#。
3.4 获取在action中调用ValueStack通过set方法设置的值<s:property value="setName"/>
3.5 获取在action中调用ValueStack通过push方法设置的值,他会把数据放到数组中去,这个数组的名字是top.
为什么EL表达式也能获取值栈中的数据:
- 向域对象中设置属性,会使用setAttrubutem,向域对象中获取属性,会调用getAttribute方法。
- 首先从request域中进行获取
- 如果存在,则直接进行返回,否则从值栈中获取,然后放入request域中,然后在从request域中进行获取。
- 一般不用EL表达式来取值栈的值,因为性能较差。
‘#’的使用:
用来取context中的值,前面加个context
‘%’的使用:
拦截器:
- 拦截器的创建时间:在服务器启动的时候开始创建。
- struts2封装了很多的功能,这些功能都是封装在拦截器里面。
- 拦截器的执行时间:在action对象创建之后,在action方法执行之前。
- 在struts2中定义了许多的拦截器,但是并不是所有的拦截器都会执行,他有默认执行的拦截器。
4.1 默认拦截器的位置:在struts的核心包里。 - 拦截器的相关原理:AOP,责任链设计模式
5.1 AOP面向切面编程,浅显的说就是在添加功能的同时不需要改变源代码。
5.2 责任链设计模式:类似于过滤链,会有很多个拦截器执行。 - 过滤器与拦截器的区别:
6.1 过滤器可以过滤任意内容,例如servlet,html,jsp都可以过滤,但是拦截器只可以拦截action. 自定义拦截器:
7.1 struts2中已经定义好的拦截器可能没有我们需要的功能,所以我们需要自己去创建。
7.2 拦截器的基本结构:
继承AbstractInterceptor或者实现Interceptor接口
接口中有三个方法:init(初始化方法),destory(销毁方法),intercept()拦截逻辑操作,但是在开发中一般使用继承MethodFilterInterceptor类实现,因为可以让我们对action某个方法不进行拦截,采用配置文件的方式让拦截器和action建立关系。
7.3 创建拦截器:
首先编写拦截器类:public class MyInterceptor extends MethodFilterInterceptor{ @Override protected String doIntercept(ActionInvocation arg0) throws Exception { // TODO Auto-generated method stub HttpSession session=ServletActionContext.getRequest().getSession(); String username=(String) session.getAttribute("username"); if(username==null){ return "login";//返回,必须和result的值相匹配。 }else{ return arg0.invoke();//执行下面一个拦截器,或者action的方法。 } } }
再到action所在package中进行注册: