struts2

更新时间:2024-06-06 07:22:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

.Struts2课件

1 Mvc与servlet

1.1 Servlet的优点

1、 是mvc的基础,其他的框架比如struts1,struts2,webwork都是从servlet

基础上发展过来的。所以掌握servlet是掌握mvc的关键。

2、 Servlet把最底层的api暴漏给程序员,使程序员更能清楚的了解mvc的各

个特点。

3、 程序员可以对servlet进行封装。Struts2就是从servlet中封装以后得到的

结果。

1.2 Servlet的缺点

1、 每写一个servlet在web.xml中都要做相应的配置。如果有多很servlet,会

导致web.xml内容过于繁多。 2、 这样的结构不利于分组开发。

3、 在servlet中,doGet方法和doPost方法有HttpServletRequest和

HttpServletResponse参数。这两个参数与容器相关,如果想在servlet中作单元测试,则必须初始化这两个参数。

4、 如果一个servlet中有很多个方法,则必须采用传递参数的形式,分解到每

一个方法中。

2 重构servlet

针对servlet以上的特点,我们可以对servlet进行重构,使其开发起来更简单。更容易,更适合团队协作。 重构的目标:

1、 只写一个serlvet或者过滤器,我们这里选择过滤器。

2、 不用再写任何的servlet,这样在web.xml中写的代码就很少了。 3、 原来需要写serlvet,现在改写action。

4、 在action中把HttpServletRequest参数和HttpServletResponse参数传递

过去。

5、 在过滤器中通过java的反射机制调用action。 6、 详细过程参照cn.itcast.action包中的内容

3 Struts2介绍

1、 struts2是apache组织发明的开源框架。是struts的第二代产品。 2、 struts2是在struts和webwork基础上整合的全新的框架。 3、 struts2的配置文件组织更合理,是企业开发很好的选择。 4、 struts2的拦截器为mvc框架注入了全新的概念。

4 Struts2入门

4.1 新建一个工程为struts2 4.2 修改工程的编码为utf-8 4.3 导入jar包

在新建的工程下创建一个文件夹名为lib,把相应的jar包到入到lib文件夹中,并且放入到classpath中,jar包有: 4.4 创建test.jsp文件

4.5 创建HelloWorldAction

package cn.itcast.struts2.action; import com.opensymphony.xwork2.Action; public class HelloWorldAction implements Action{ public String execute() throws Exception { // TODO Auto-generated method stub System.out.println(\ return \ } } 4.6 编写success.jsp文件

This is my JSP page.
base命名空间下的HelloWorld运行完成! 4.7 编写struts配置文件

该文件放在src下即可 4.8 编写web.xml文件

4.9 运行结果

测试struts2,输出有命名空间的helloWorld
测试struts2,输出没有命名空间的helloWorld
4.10 加载struts.xml过程

说明:

1、 在启动的时候加载了三个配置文件

struts-default.xml、struts-plugin.xml、struts.xml

2、 如果这三个文件有相同的项,后面覆盖前面的。

6.2.3 redirectAction类型

6.2.3.1 说明

1、 把结果类型重新定向到action 2、 可以接受两种参数

a) actionName: action的名字 b) namespace:命名空间

6.2.3.2 例子

第一种方式:

7 Action原型模式

7.1 回顾servlet

在servlet中,一个servlet只有一个对象,也就是说servlet是单例模式。如果把一个集合写在servlet属性中,则要考虑线程安全的问题。

7.2 Action多例模式

但是在struts2的框架中,并不存在这种情况,也就是说struts2中的action,只要访问一次就要实例化一个对象。这样就不存在线程安全的问题。这也是struts2框架的一个好处。

7.3 实验

可以写一个类,如下: package cn.itcast.struts2.action.moreinstance; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings(\ public class MoreInstanceAction extends ActionSupport{ public MoreInstanceAction(){ System.out.println(\ } public String execute(){ System.out.println(\ return SUCCESS; } }

配置文件为: 请求两次http://localhost:8080/struts2/moreinstance/moreinstanceAction.action路径,如果构造函数中的”create new action”输出两次,说明创建了两次对象。

8 通配符

8.1 Execute方法的弊端

假设有这样的需求:

1、 有一个action为PersonAction。

2、 在PersonAction中要实现增、删、改、查四个方法。 3、 但是在action中方法的入口只有一个execute方法

4、 所以要想完成这样的功能,有一种方法就是在url链接中加参数。

那么在action中的代码可能是这样的:

PatternAction public class PatternAction extends ActionSupport{ private String method; public String execute(){ if(method.equals(\ //增加操作 }else if(method.equals(\ //修改操作 }else if(method.equals(\ //删除操作 }else{ //查询操作 } return \ } }

可以看出这样写结构并不是很好。而通配符的出现解决了这个问题。

8.2 method属性

Pattern.jsp 访问PersonAction的add方法:

Struts-pattern.xml PatternAction public String add(){ return \} 说明:从上述代码可以看出只要配置文件中的method属性的值和方法名称一样就可以了。但是这种写法有弊端。如果aciton中需要5个方法。则在struts的配置文件中需要写5个配置,这样会导致配置文件的篇幅很大。而且重复性也很大

8.3 动态调用方法

在url中通过action名称!方法名称可以动态调用方法。 Pattern.jsp 动态调用PatternAction中的add方法:
struts-pattern.xml 说明:这样的情况在配置文件中不需要method属性 8.4 通配符映射 8.4.1 映射一

需求:a_add.action、b_add.action、c_add.action全部请求PatternAction的add方法 Pattern.jsp 通配符映射实例1:
说明:不管是a_add还是b_add还是c_add的路径都指向PatternAction的add方法。 struts-pattern.xml 上述结构是很差的,经过改进如下:

请求PersonAction和StudentAction的add方法 Pattern.jsp 通配符映射实例2:
请求studentAction的add方法

Struts-pattern.xml name=\ 说明:*和{1}是相对应的关系。

method=\method=\class=” 8.4.3 映射三

需求:在TeacherAction中有增、删、改、查的方法。这个时候配置文件怎么写比较简单? Pattern.jsp 通配符映射实例3: struts-pattern.xml 说明:*和method的属性值保持一致。 延伸: 第一个*匹配{1},第二个*匹配{2} 9 全局结果类型

9.1 说明

当很多提交请求跳转到相同的页面,这个时候,这个页面就可以成为全局的页面。在struts2中提供了全局页面的配置方法。

9.2 例子

Struts-pattern.xml 注意: * 这个配置必须写在action配置的上面。dtd约束的规定。 * 如果在action的result中的name属性也有success值,顺序为 先局部后全局。 10 Struts2与serlvet接口

10.1 说明

通过前面的练习大家都知道,在action的方法中与servlet的所有的API是没有任何关系的。所以在struts2中做到了aciton与serlvet的松耦合,这点是非常强大的。但是如果没有HttpServletRequest,HttpServletSession,ServletContext有些功能是没有办法完成的。比如购物车程序,需要把购买的物品放入session中。所以就得找一些路径使得在struts2中和serlvet的API相结合。

10.2 实现一

Struts2中提供了ServletActionContext类访问servlet的api。

Servlet.jsp 通过SerlvetActionContext类访问servlet的API:
ServletAction public String testServletAPI(){ //访问request HttpServletRequest request = ServletActionContext.getRequest(); //访问session 对session进行了二次封装 Map sessionMap = ServletActionContext.getContext().getSession(); //访问application ServletContext servletContext = ServletActionContext.getServletContext(); return \10.3 实现二 ServletAction public class ServletAction extends ActionSupport implements ServletContextAware, SessionAware,ServletRequestAware{ private HttpServletRequest request; private Map sessionMap; private ServletContext servletContext; public String testServletAPI2(){ System.out.println(this.servletContext); System.out.println(this.sessionMap); System.out.println(this.request); return \ } public void setServletContext(ServletContext context) { // TODO Auto-generated method stub this.servletContext = context; } public void setSession(Map session) { // TODO Auto-generated method stub this.sessionMap = session; } public void setServletRequest(HttpServletRequest request) { // TODO Auto-generated method stub this.request = request; } }

11 拦截器

假设有一种场景:

在aciton的一个方法中,要进行权限的控制。如果是admin用户登入,就执行该方法,如果不是admin用户登入,就不能执行该方法。

11.1 实现方案一

AccessAction public String testAccess(){ if(this.username.equals(\ //执行业务逻辑方法 return SUCCESS; }else{ //不执行 return \ } } 说明:

1、 这样做,程序的结构不是很好。原因是权限的判断和业务逻辑的

方法紧密耦合在了一起。如果权限的判断很复杂或者是业务逻辑很复杂会造成后期维护的非常困难。所以结构不是很好

2、 这种形式只能控制一个action中的一个方法。如果很多action中

的很多方法都需要这种控制。会导致大量的重复代码的编写。

11.2 实现方案二

动态代理可以实现。请参见cn.itcast.struts.jdkproxy包下的类。

11.3 实现方案三

在struts2中,用拦截器(interceptor)完美的实现了这一需求。在struts2中, 内置了很多拦截器,在struts-default.xml文件中可以看出。用户还可以自定义

自己的拦截器。自定义拦截器需要以下几点: 1、 在配置文件中: 包括两个部分:声明拦截器栈和使用拦截器栈

struts-interceptor.xml 说明:红色部分是struts2内部的拦截器。可以从struts-default.xml文件中得到内 容。自己定义的栈必须引入struts2默认的栈。因为我们在访问action时,属性 的赋值等一些工作都是由内部的栈完成的。如果不怎么写,struts2的功能将发挥 不出来。可以用debug进行调试也能得出结论。

2、 在拦截器类中

一个类如果是拦截器,必须实现Interceptor接口。

public class PrivilegeInterceptor implements Interceptor{ public void destroy() { // TODO Auto-generated method stub } public void init() { // TODO Auto-generated method stub } public String intercept(ActionInvocation invocation) throws Exception { // TODO Auto-generated method stub System.out.println(\ //得到当前正在访问的action System.out.println(invocation.getAction().toString()); //得到Ognl值栈 System.out.println(invocation.getInvocationContext().getValueStack()); //请求路径action的名称,包括方法 System.out.println(invocation.getProxy().getActionName()); //方法名称 System.out.println(invocation.getProxy().getMethod()); //命名空间 System.out.println(invocation.getProxy().getNamespace()); String method = invocation.invoke(); return null; } } 说明: 这个类中init、1、intercept和destroy三个方法说明了一个拦截器的生命周期。 2、 在interceptor方法中的参数invocation是执行action的上下文,可以从 这里得到正在访问的action、Ognl值栈、请求路径、方法名称、命名空间 等信息。以帮助程序员在拦截器中做相应的处理工作。 红色部分是关键部分,就是调用action的方法。这里可以成为目标类的 3、 目标方法。 4、因为这里配置的拦截器是针对包的,所以只要是包的action都起作用。

12 验证

12.1 需求

验证用户名不能为空,密码也不能为空,并且长度不能小于6位数。

12.2 实现

13 国际化

13.1 说明

一个系统的国际化就是根据操作系统的语言,页面上的表现形式发生相应的变化。比如如果操作系统是英文,页面的文字应该用英语,如果操作系统是中文,页面的语言应该是中文。

13.2 步骤 13.2.1 建立资源文件

资源文件的命名规则: 默认的命名为: 文件名前缀.properties 根据语言的命名为: 文件名前缀.语言种类.properties 例如: 中文: resource_zh_CN.properties 内容: item.username=用户名 item.password=密码 英文: resource_en_US.properties

内容: item.username=username_en item.password=password_en 默认: resource.properties 内容: item.username=username item.password=password

13.2.2 配置文件中

需要在配置文件中加入:

value=\ 说明:

1、 这样struts2就会去找你写的资源文件

2、 name的值可以在org/apache/struts2/default.properties中找到。 3、 如果放置的资源文件在src下,则value的值可以直接写,如果在

包中则可以写成包名.resource。

4、 在这里resource是个基名,也就是说可以加载以resource开头的文件。

name=\

13.2.3 页面中

利用就可以把资源文件中的内容输出来。 I18n/login.jsp 说明: 1、 标红色部分的是要从资源文件中找的内容。item.username和 item.password代码key的值。 2、 也可以利用%{getText('item.username')}方式来获取资源。采取的是 OGNL表达式的方式。 3、 getText的来源: 从源代码可以看出ActionSupport实现了TextProvider接口。 Provides access to {@link ResourceBundle}s and their underlying text messages.意思是说提供了访问资源文件的入口。而 TextProvider中提供了getText方法,根据key可以得到value。

13.2.4 在action中

可以利用ActionSupport中的getText()方法获取资源文件的value值。 I18n/LoginAction public class LoginAction extends ActionSupport{ public String login(){ String username = this.getText(\ System.out.println(username); String password = this.getText(\ System.out.println(password); return \ } } 说明:通过this.getText()方法可以获取资源文件的值。

14 OGNL

14.1 介绍

OGNL表达式是(Object-Graph Navigation Language)是对象图形化导航语言。OGNL是一个开源的项目,struts2中默认使用OGNL表达式语言来显示数据。与serlvet

中的el表达式的作用是一样的。OGNL表达式有下面以下特点:

1、 支持对象方法调用,例如:objName.methodName(); 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、 操作集合对象

14.2 回顾el表达式

在servlet中学习的el表达式实际上有两步操作:

1、 把需要表现出来的数据放入到相应的作用域中

(req,res,session,application)。

2、 利用el表达式把作用域的值表现在页面上

14.3 ognl类图

14.4 ValueStack 14.4.1 说明

1、 ValueStack是一个接口,在struts2中使用OGNL表达式实际上是使用

实现了ValueStack接口的类OgnlValueStack,这个类是OgnlValueStack的基础。

2、 ValueStack贯穿整个action的生命周期。每一个action实例都拥有一

个ValueStack对象。其中保存了当前action对象和其他相关对象。 3、 Struts2把ValueStack对象保存中名为struts.valueStack的request域中。

14.4.2 ValueStack内存图

14.4.3 ValueStack的组织结构

从图上可以看出OgnlValueStack和我们有关的内容有两部分:即OgnlContext

和CompoundRoot。所以把这两部分搞清楚很重要。

14.4.4 14.4.4.1

OgnlContext组织结构

_values

从上述可以看出,OgnlContext实际上有一部分功能是Map。所以可以看出_values就是一个Map属性。而运行一下下面的代码就可以看到: //在request域中设置一个参数 ServletActionContext.getRequest().setAttribute(\name\//在request域中设置一个参数 ServletActionContext.getRequest().setAttribute(\//在session域中设置一个参数 ActionContext.getContext().getSession().put(\\//在session域中设置一个参数 ActionContext.getContext().getSession().put(\\//获取OGNL值栈 ValueStack valueStack = ActionContext.getContext().getValueStack(); 在_values的map中: key value application ApplicationMap request RequestMap action 自己写的action com.opensymphoSessionMap ny.xwork2.ActionContext.session

而request中的值为: { req_psw=req_psw, req_username=req_username, __cleanup_recursion_counter=1, struts.valueStack=com.opensymphony.xwork2.ognl.OgnlValueStack@3d58b2, struts.actionMapping=org.apache.struts2.dispatcher.mapper.ActionMapping@1f713ed }

可以看出在程序中存放在request作用域的值被放入到了_values的request域中。

com.opensymphony.xwork2.ActionContext.session的值为:

{session_username=session_username, session_psw=session_psw} 从上图中可以看出在程序中被加入到session的值在_values中也体现出来。

14.4.4.2 _root

从图中可以看出_root实际上CompoundRoot类,从类的组织结构图中可以看出,这个类实际上是继承了ArrayList类,也就是说这个类具有集合的功能。而且在默认情况下,集合类的第一个为ValueStackAction,也就是我们自己写的action。

14.5 总图

说明:

1、 上图是ognl完整的数据结构图,可以清晰得看出数据的组成。 2、 Context中的_root和ValueStack中的root(对象栈)里的数据结构和值是

一样的。

3、 这就意味着我们只需要操作OgnlContext就可以完成对数据的存和取

的操作。

4、 ValueStack内部有两个逻辑的组成部分:

a) ObjectStack

Struts会把动作和相关的对象压入到ObjectStack中。

b) ContextMap

Struts会把一些映射关系压入到ContextMap中

14.6 存数据 14.6.1 Map中存数据

14.6.1.1 方法1

//向map中存放数据 ServletActionContext.getRequest().setAttribute(\ServletActionContext.getRequest().setAttribute(\ActionContext.getContext().getSession().put(\ActionContext.getContext().getSession().put(\上面的代码都是往ContextMap中存放数据。因为这些值都是具有映射关系的。

14.6.1.2 方法2

ActionContext.getContext().put(\

通过执行上述代码把”msg”和”msg_object”放入到了ContextMap中。

14.6.2 值栈中存数据

14.6.2.1 方法1

/* * 把对象放入到值栈中 */ //方法一:先得到root,把一个对象压入到root中 ValueStack valueStack = ActionContext.getContext().getValueStack(); valueStack.getRoot().add(new Person()); valueStack.getRoot().add(new Student()); 运行以后的值栈结构图:

从内存图中可以看出最后被压入到list中的对象在最下面。

图为ArrayList中的add方法的解释: 追加的指定的元素放到集合的最下面。

14.6.2.2 方法2

/* * 方法二:先得到root,利用add(index,Object)把一个对象压入到root中 * 这里index指的是集合中的位置 */ ValueStack valueStack = ActionContext.getContext().getValueStack(); valueStack.getRoot().add(new Person()); valueStack.getRoot().add(0, new Student());

运行后的结构图为:

从图中可以很明显看出新创建的Student对象被放到了第一个位置,因为 Index的值为0,所以是第一个位置。

14.6.2.3 方法3

把一个键值对存放在对象栈中,做法为: /* * 方法三: * 把一个键值对存放在对象栈中 */ ValueStack valueStack = ActionContext.getContext().getValueStack(); valueStack.set(\ 对象栈图为:

从图中可以看出上面的代码执行过程为:

1、 先把”msg”和”msg_object”两个字符串封装成Map 2、 再把封装以后的Map放入到对象栈中。

14.6.2.4 方法4

/* * 方法4 * 利用ValueStack的push方法可以把一个对象直接压入对象栈的第一个位置 */ ValueStack valueStack = ActionContext.getContext().getValueStack(); valueStack.push(new Person());

执行完push方法以后,对象栈的情况如下:

Push方法把新创建的Person对象放入到了对象栈的首个位置。

14.7 OGNL Context

14.7.1 说明

1、 上图为OGNL Context的结构图

2、 当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action。然后

把action压入到值栈中。所以action的实例变量可以被ognl访问。所以利用ognl表达式可以访问action。

14.8 ActionContext

/* * ActionContext的作用 */ ActionContext.getContext().getSession().put(\ActionContext.getContext().getSession().put(\ValueStack valueStack = ActionContext.getContext().getValueStack(); 14.8.1 说明

从上面的代码中可以看出来,struts2中的ActionContext的作用是提供了对ognl数 据的操作。并且可以通过ActionContext获取到经过struts2封装了的session等参数。

14.9 ServletActionContext

/* * 返回servlet中的request和servletcontext */ ServletActionContext.getRequest().setAttribute(\ServletActionContext.getRequest().setAttribute(\ServletActionContext.getServletContext(); //得到ActionContext ServletActionContext.getContext(); 14.9.1 说明

1、 可以通过ServletActionContext得到servlet中的一些类,比如

HttpServletRequest,ServletContext等

2、 可以通过ServletActionContext返回ActionContext

14.10 Ognl表达式 14.10.1 简述

从9.6到9.9讨论了ognl的结构、如何存数据。9.10重点讨论如何把ognl结构中的数据呈现在页面上。所以Ognl表达式的作用就是把OgnlContext中的数据输出到页面上。

14.10.2 el表达式例子

参照课堂演示例子

14.10.3 ognl表达式例子

14.10.3.1 用法1(#号用法)

说明:

1、 访问OGNL上下文和action上下文,#相当于ActionContext.getContext(); 2、 如果访问的是map中的值而不是对象栈中的值,由于map中的数据不是

根对象,所以在访问时需要添加#前缀。 名称 parameters request 作用 包含当前HTTP请求的Map 包含当前HttpServletRequest属性的Map 包含当前HttpSession属性的Map 包含当前ServletContext属性的Map 用于按照request>session>application顺序访问其属性

注:也可以写为#request[?username?] #sesssion[?username?] #application[?username?]

例子 #parameters.id[0]=request.getParameter(“id”) #request.username=request.getAttribute(“username”); #session.username=session.getAttribute(“username”); #application.username=application.getAttribute(“username”); #attr.username相当于按照顺序在以上三个范围内读取username的属性,直到找到为止。 session application attr

本文来源:https://www.bwwdw.com/article/tdd6.html

Top