目录
Struts 2对Action的具体操作
MVC模式
MVC模式的核心是控制器对所有请求的进行统一的处理。
Struts 2的控制器StrutsPrepareAndExecuteFilter由Servlet API中的Filter充当,当Web容器接收到登录请求后,将请求交由在web.xml中配置的过滤器StrutsPrepareAndExecuteFilter。
Struts 2执行流程
(1)当Web容器接收到请求后,将请求交由在web.xml中配置的struts 2框架的控制器StrutsPrepareAndExecuteFilter(核心控制器)
(2)由StrutsPrepareAndExecuteFilter确定请求对应的Action(业务控制器)
(3)框架根据Action返回的结果字符串,由StrutsPrepareAndExceuteFilter(核心控制器)选择对应的result,将结果呈现给用户
1.web.xml
任何一个Web应用程序都是基于请求/响应模式进行构建的,无论是采用哪种MVC框架,都离不开web.xml的文件配置。web.xml是所有Java Web应用程序都需要的核心文件。
Struts 2框架需要在web.xml中配置其核心控制器StrutsPrepareAndExecuteFilter,用于对框架进行初始化,以及处理所有的请求。
StrutsPrepareAndExecuteFilter可以包含一些参数,经常使用config,要加载的XML形式的配置文件列表(多个配置文件以逗号分隔),如果没有设置这个参数,Struts 2框架将默认加载struts-default.xml、struts-plugin.xml和struts.xml。
StrutsPrepareAndExecuteFilter作为一个Filter在Web应用中运行,它负责拦截所有的用户请求,当用户请求到达时,该Filter或过滤用户请求。如果用户请求以“.action”结尾,该请求将被输入Struts 2框架进行处理。
Struts 2核心控制器
<filter>
<filter-name>demo</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>demo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.struts.xml
包含package、action、result等的配置,主要负责管理Struts 2框架的业务控制器。
constant元素用于配置常量,通过配置常量可以改变Struts 2框架的一些行为,包含两个属性:
①name:常量的名称
②value:常量的值
当Struts 2接收到一个请求时,框架会将URL分为namespace和action名称两部分,框架首先在namespace命名空间中查找这个action,若没有找到则再在默认命名空间中查找。
|- package元素:Struts 2框架会将action、result等组织在一个名为package(定义包)的逻辑单元中,从而简化维护工作,提高重用性。每个包都包含了将要用到的action、result等的定义
|- package属性
|- name属性:必须的并且唯一的,用来指定包的名称(被其他包引用)
|- extends属性:类似于Java的extends关键字,指定要扩展的包。在开发的过程,除非有令人信服的理由,否则所定义的包应该总是扩展struts-default 包。Struts-default包由Struts 2框架定义,其中配置了大量常用的Struts 2的特性;若是没有这些特性,则连简单地在action中获取请求数据都无法完成
|- namespace属性:可选,该属性定义该包中action的命名空间。若果没有设置该属性,则action被放入默认命名空间中。
Struts 2框架使用action的名称和它所在包的命名空间来标识一个action,默认命名空间用””表示,也可以使用”/”定义一个根命名空间,二者是由区别的。
当请求Web应用程序根路径下的action,框架在根命名空间中查找对应的action,如果在根路径下未找到,则再到默认命名空间中去查找
处理中文乱码
<constant name=”struts.il8n.encoding” value=”utf-8”/>
可配置的常量均可在struts2-core-2.3.16.3.jar下的org/apache/struts2/default.properties文件中找到
另外一种方式,是在源文件根目录下创建struts.properties来覆盖default.properties中的设置,格式和default.properties相同,都属于properties文件。
拆分配置文件
将所有的配置文件都放在一个配置文件中必然会导致struts.xml文件变得臃肿,为了提高struts.xml文件的可读性,Struts 2允许将一个配置文件拆分为多个配置文件,但默认只加载WEB-INF/classes下的struts.xml文件
一旦拆分后,还要能够拆分过的文件整合在一起,可以在struts.xml文件中通过include元素将其他配置文件包含进来。include元素提供file属性,该属性指定了被包含配置文件的文件名。
<struts>
<!-- 文件名,默认加载WEB-INF/classes下的struts.xml文件 -->
<include file=”struts-name.xml”>
</struts>
被保存在WIN-INF/classes下,与struts.xml的结构完全一样。当系统加载struts.xml后便会自动加载所包含的所有配置文件。
struts-default.xml
Struts 2框架的默认配置文件,为框架提供默认的设置,该配置文件会自动加载
struts-plugin.xml
Struts 2插件使用的配置文件,如果不是开发插件,则不需要编写这个配置文件
基本结构:
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="world"uri class="全限定类名" method="world"Action方法名>
<result name="success"Action返回值>/show.jsp</result>
</action>
</package>
</struts>
Result的配置
result元素的作用是实现结构视图的调用,并决定视图以哪种形式展现给客户端(用来设定Action处理结果后,下一步准备干什么)
Struts 2的Action处理用户请求结束后,返回一个普通字符串(逻辑视图名称)。必须在struts.xml文件中完成逻辑视图和物理视图资源的映射,才可以让系统转到实际的视图资源。
通过strtuts.xml文件中使用<result……/>元素类配置结果
Result的配置有两部分组成:
①一部分是Result所代表的实际资源的位置及Result名称(name)
②另一部分是Result的类型(type)
Result的类型(type) | 描述 |
---|---|
dispather | Action元素的type属性的默认结果类型,采用转发的形式请求指定的视图资源,请求中数据信息不回丢失 |
redirect | 采用重定向的方式请求指定的视图资源,通过HttpServletResponse对象的sendRedirect()方法重新生成一个请求,原请求中的数据或丢失 |
redirectAction | 采用重定向的方式请求一个新的Action,原请求中的数据信息会丢失 |
常用的结果类型(type)
1.dispatcher类型
默认结果类型,用来转发;Action的处理结果将自动以dispatcher(转发)方式请求指定的视图资源
2.redirect类型
与上者的主要差别是在于,使用dipatcher结果类型是将请求转发(forward)到指定的视图资源,所以请求中包含数据信息依然存在;而redirect结果类型是在内部使用HttpServletResponse对象的sendRedirect()方法将请求重定向至指定的URL,这意味着请求中包含的参数、属性、Action实例及Action封装的属性将全部丢失
3.redirectAction类型
与redirect类型相似。都使用HttpResponse对象的sendRedirect()方法将请求重定向至指定的URL。但redirectAction类型主要用于重定向到另一个Action。也就是说,当请求处理完成后,需要在另一个Action中继续处理请求时,就要使用redirectAction结果类型重定向到指定的Action。
redirect和redirectAction
对于redirect和redirectAction二者,都是使用HttpResponse对象的sendRedirect()方法将请求重定向至指定的URL
redirect主要用于执行对一个具体的资源(JSP)
redirectAction主要用于执行对一个Action的请求(也就是URL)
动态结果
动态结果就是指在配置时,不知道执行后的结果是哪一个,只有在运行时才能知道哪个结果作为视图显示给用户,即在配置时使用表达式,在运行时,由框架根据表达式的值来确定要使用哪个结果。
例如:
使用${nextDispose}来获取Action中nextDispose属性的值,这个值在运行时才能知道,框架在解析出${nextDispose}的值,然后将请求重定向到指定的Action。
需要注意的是,使用${属性名}语法访问的属性一定要在Action中存在(也就是咋class属性绑定的Action类中存在nextDispose属性),并且Action要提供该属性的getter方法。
全局结果
也就是多个Action共享一个结果。全局结果也在包中定义(package元素),这个包中的多有Action都可以共享这个结果(result)。
语法:
<global-results>
<result name=”” type=””></result>
</global-results>
全局结果也同样使用result元素配置,与action配置result元素的区别在于,全局结果需要在global-results元素中嵌套result元素,如果在Action中的result元素的名称与全局结果的名称相同时,Action中的result元素会覆盖global-results的属性。
也就是说,当Action处理用户请求结束后,会首先在本Action中result结果中搜索与逻辑视图名称对应的结果,只有在Action中无法找到与逻辑视图对应的结果时,才会去全局结果中寻找并执行(可以说Java类的全局变量和局部变量的关系)
3.Action
Action类在处理完用户请求后,会返回一个处理结果。这个结果是一个简单的字符串,框架根据这个字符串选择对应的Result,因此称其为逻辑视图名称;这个逻辑视图名称由result元素的name属性来表示。
result元素的值用来指定这个逻辑视图对应的物理视图资源(JSP,HTML,CSS)的位置;逻辑视图名称只有与物理视图资源联系在一起,才能发挥作用
注意:
1.Action作为业务控制器,只负责返回结果,而不与视图相关联,这样做的优势在于,当视图发生变化时,无须修改Action类的代码,仅需修改配置文件
2.当StrutsPrepareAndExecuteFilter(核心控制器)调用相应的视图时,默认采用转发的形式跳转到指定的JSP页面
使用Struts 2框架,主要的编码工作就是编码处理 请求的Action类。在Struts 2中,提供了多种实现Action的方式。在Action中,所有用于处理请求的方法都必须返回一个字符串类型的逻辑结果,用于标识要呈现给用户的结果
可以直接使用Action类的属性接收用户的输入,即当表单提交时,Struts 2自动对请求参数进行转换,并对具有相同名字的Action属性进行赋值(通过setter方法)
Action类的属性除了可以用来接收请求的数据,还可以用来输出处理结果,依然借助setter和getter方法对结果属性进行处理。只要对属性设置了setter和gettter方法,该属性就可以被自动处理。此外,Action类中还可以使用复杂的属性,如用户自定义类、数组、集合对象等
Action接口中常量字符串的逻辑含义
常量 | 值 | 逻辑含义 |
---|---|---|
SUCCESS | “success” | 表示程序处理正常,并返回给用户成功后的结果 |
NONE | “none” | 表示处理正常结束,但不返回给用户任何显示 |
ERROR | “error” | 表示处理结果失败 |
INPUT | “input” | 表示需要更多用户输入才能顺利执行 |
LOGIN | “login” | 表示需要用户正确登录后才能顺利执行 |
在Struts 2中控制器由两部分组成
(1)核心控制器(Filter):用于拦截请求,对请求进行处理
(2)业务控制器(Action):调用相应的Model类实现业务处理,返回结果
在Struts 2配置文件中,包含了以下元素:
(1)package元素
用于定义处理请求的逻辑单元,name属性为必需的并且唯一,用来指定包的名称(被其他包引用);
①extends属性
类似于Java的关键字,用于指定扩展的包;namespace指定命名空间
(2)package元素的action子元素
用于配置Struts 2框架的“工作单元”Action类。action元素将一个请求的URI(action的名字)对应到一个Action类。name属性是必需的,用来表示action的名字;class属性可选,用于设定Action类的全限定名。
result元素用来设定Action类处理结束后,系统下一步将要做什么。name属性表示result的逻辑视图名称,必须与Action类返回的字符串进行匹配;而result元素的值表示与逻辑视图名称对应的物理资源之间的映射,用来指定这个结果对应的实际资源的位置
Filter用来接收用户请求,在对数据进行简单处理后创建Action实例,然后调用Action的方法;Action实例的创建由web.xml中的配置决定
在配置文件中将一个请求的URI(action的名字)对应到一个Action类,当一个请求匹配某个Action的名字时,框架就会使用者个Action类处理请求。
action元素中的name属性是必需的,表示action的名字,用于匹配请求的URI;class属性表示Action类的全限定类名,即决定了该action的实现类。
Action配置
Action的作用?
Action是应用的核心。开发者需要提供大量的Action类,并在struts.xml文件中配置Action
主要有三个作用:
①Action最重要的作用是为给定的请求封装需要做的实际工作(调用特定的业务处理类)
②为数据的转移提供场所
③帮助框架决定由哪个结果呈现请求响应
1.封装工作单元
可以把Action看作控制器的一部分,其主要职责就是控制业务逻辑,通常Action使用execute()方法实现这一功能(execute也是默认调用的方法)
2.数据转移的场所
Action作为数据转移的场所,也许有人会认为这会使Action变得复杂,实际上使得Action更加简洁
由于数据保存在Action中,在控制业务逻辑的过程中可以非常方便地访问它们
或许一系列的JavaBean属性增加Action的代码量,但执行execute()方法时引用这些属性中保存的数据,会使Action的代码变得简洁(不再需要从request对象中获取请求数据),并且使我们开发的Action与Servlet API解耦
3.返回结果字符串
Action的最后一个作用是返回结果字符串。Action根据业务逻辑执行的返回结果判断返回何种结果字符串,架构根据Action返回的结果字符串选择对应的视图组件呈现给用户
method属性
在执行程序中,每实现一个功能都会去创建一个Action,并完成相应的方法;method属性的值,对应着Action内的方法,是哪个就调用哪个
查找执行方法有两种:
(1)查找与method属性值完全一致的方法
(2)查找doMethod()形式的方法
例如:method=”login”,会先查找login()、找不到则会查找doLogin()方法。
Action中动态方法调用
为了减少配Action的数量,可以使用动态方法进行处理。
动态方法调用(Dynamic Method Invocation,DMI)是指表单元素的action并不是直接等于某个Action的名称,而是通过在Action的名称中使用感叹号(!)来标识要调用的方法名称,格式为actionName!methodName.action。
动态调用可能会带来安全隐患(通过URL可以执行Action中的任意方法),所以在确定使用动态方法调用时,应该确保Action中的所有方法都是普通的,开放的方法。基于这个原因Struts 2框架默认禁止动态方法调用,有default.properties中的struts.enable.DynamicMethodInvocation属性设置,默认为false,可以在struts.xml文件中,通过constant元素将其设置为true启用动态方法调用。
<constant name=”struts.enable.DynamicMethodInvocation” value=”true” />
使用method属性调用不同的方法与动态方法调用的适用条件如下
(1)如果同一个Action的不同方法要处理的请求使用相同的配置(result等),则使用动态方法调用
(2)如果不同方法的调用需要不同的配置,那么就要使用action元素的method属性,为同一个Action配置多个名称,但使用method属性会导致配置文件中存在大量的Action配置
从安全角度出发,建议采用method属性来实现同一个Action的不同方法处理不同的请求。但是随着Action的逐渐增多,导致在struts.xml文件中存在大量的Action配置
Action中通配符的使用
在配置<action ……/>元素时,需要指定name、class、method属性,其中name属性支持通配符,在class、method属性中可以使用表达式
这种使用通配符的方式是另一种形式的动态方法调用通配符用星号(*)表示,用于配置0个或多个字符串
配置默认的Action
Struts 2框架允许指定一个默认的Action,即如果没有一个Action匹配请求,那么默认的Action将被执行
配置默认的Action通过<default-action-ref……/>元素完成,在每个package下只能有一个<default-action-ref……/>元素。
指定其name属性为当前package、子package、父package三者中的一个Action name,如果在某一继承树上的个package中分别定义了多个<default-action-ref……/>元素
则搜索顺序:由子package往父package寻找,找到即停止
获取表单中的数据
(1)使用ActionContext类获取(继承ActionSupport)
方法 | 描述 |
---|---|
static ActionContext getContext() | 获取当前线程的ActionContext对像 |
Map<String,Object> getParameters() | 返回一个含有HttpServletRequest参数信息 |
(1)因为方法不是静态的,需要创建ActionContext类对象
(2)这个ActionContext对象不是new出来的
对于表单input元素的name属性的值可以随便命名
@Override
public String execute() throws Exception {
//第一种使用ActionContext获取
//1、获取ActionContext对象方法,获取表单数据源
ActionContext context = ActionContext.getContext();
//2、调用方法得到表单数据
//key是表单name属性value是值
Map<String, Object> map=context.getParameters();
Set<String> keys=map.keySet();
for(String key:keys){
//根据key得到value
//数组形式:因为输入项里可能有复选框
Object obj[]=(Object[])map.get(key);
System.out.println(key+":"+Arrays.toString(obj));
}
return NONE;
}
(2)使用ServletActionContext获取(继承ActionSupport)
方法 | 描述 |
---|---|
static HttpServletRequest() | 获取Web应用的 HttpServletRequest对象 |
static HttpServletResponse() | 获取Web应用的 HttpServletResponse对象 |
static ServletContext.getServletContext() | 获取Web应用的 ServletContext对象 |
static PageContext.getPageContext() | 获取Web应用的 PageContext对象 |
调用类里头的静态方法,得到request对象
对于表单input元素的name属性的值跟getParameter()中的值对应。
@Override
public String execute() throws Exception {
// 第二种种使用ServletActionContext获取
// 1、用ServletActionContext获取request
HttpServletRequest request = ServletActionContext.getRequest();
// 2、调用request里面的方法得到结果
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:" + username);
System.out.println("password:" + password);
return NONE;
}
(3)使用接口注入方式获取(了解,实现ServletRequestAware接口)
实现的接口 | 描述 |
---|---|
ServletRequestAware | 实现该接口的action可以直接访问Web应用的HttpServletRequets实例 |
ServletResponseAware | 实现该接口的action可以直接访问Web应用的HttpServletResponse实例 |
SessionAware | 实现该接口的action可以直接访问Web应用的HttpSession实例 |
ServletContextAware | 实现该接口的action可以直接访问Web应用的ServletContext实例 |
//定义成员request
private HttpServletRequest req;
@Override
public void setServletRequest(HttpServletRequest req) {
this.req=req; //获取到request
}
@Override
public String execute() throws Exception {
String username = req.getParameter("username");
return NONE;
}
获取并封装表单中的数据
(1)采用属性封装(继承ActionSupport)
/* 属性封装
* 提供成员变量、属性值和表单输入项的name一致需要提供set方法
*/
private String username;
private String password;
//**************省略setter方法
// 注意:表单的name值要跟属性名一致
// 默认走这个方法,NONE没有返回值
public String execute() {
User user = new User();
user.setUsername(username);
user.setPassword(password);
return NONE;
}
(2)模型驱动封装(重点)(继承ActionSupport并且实现ModelDriven)
private User user = new User();
@Override
public User getModel() {
return user;
}
// 默认走这个方法,NONE没有返回值
public String execute() {
System.out.println(user.getPassword() + " " + user.getUsername());
return NONE;
}
(3)表达式封装对象(继承ActionSupport)
/**
* 不能实例化,框架会自动实例化
* /
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
// 表单中的name属性使用user.username;username与User中的属性对应;也就是 实体对象.属性名
// 默认走这个方法,NONE没有返回值
public String execute() {
System.out.println((User)user);
return NONE;
}
(4)List集合封装(继承ActionSupport)
private List<User> list;
public List<User> getList() {
return list;
}
public void setList(List<User> list) {
this.list = list;
}
// 表单name属性,list[0].username 集合名[下标].User类属性
public String execute() {
System.out.println(list);
return NONE;
}
(5)Map集合封装(继承ActionSupport)
private Map<String,User> map;
public Map<String, User> getList() {
return map;
}
public void setList(Map<String, User> map) {
this.map = map;
}
// 表单元素name属性 map[‘one’].username Map名[‘键’].User属性
public String execute() {
System.out.println(map);
return NONE;
}
将数据发送至表示页面
操作request、session、application
/**
* 操作作用域:request session application
* /
// 默认走这个方法,NONE没有返回值
// 如果使用request转发,将需要改变result的type的值为“dispatcher”
public String execute() {
HttpServletRequest req = ServletActionContext.getRequest();
req.setAttribute("req", "reqqqqq");
req.getSession().setAttribute("sess", "sesssss");
ServletActionContext.getServletContext().setAttribute("app", "appssssssssss");
return "success";
}