Struts 2配置详解

学习内容

 Struts 2配置文件

 Action的配置

 Result的配置

 属性驱动与模型驱动

能力目标

 熟练进行Struts 2配置

 熟练使用属性驱动和模型驱动

本章简介

上一章我们初步学习了Struts 2框架,包括Struts 2体系结构和运行流程,并通过登录案例介绍了使用Struts 2进行开发的基本步骤。

本章将深入学习Struts 2框架,主要内容是Struts 2的配置文件,包括Action的配置、Result的配置等等,只有掌握了配置文件,才能更好的使用和扩展Struts 2的功能。

核心技能部分

4.1 Struts 2的配置文件

通过上一章的学习,我们知道Struts 2框架默认的配置文件是struts.xml,该文件通常放在WEB-INF\classes目录下,该目录下的struts.xml会自动被Struts 2框架加载。本节将详细介绍该配置文件各元素的含义和使用。

4.1.1 中文乱码处理

在Struts 2中,如果客户端请求中包含有中文数据,那就很容易出现中文乱码问题。当然我们会有很多方法来解决这个问题,在struts.xml中可以通过配置更简单的解决中文乱码问题,参考代码如下所示。





/index.jsp
/fail.jsp


在struts.xml中,元素用来配置常量,配置时必须指定两个属性:name和value。上述代码中的struts.i18n.encoding是Struts 2中已经存在的常量属性,用来配置Web应用程序默认的编码集,相当于调用HttpServletRequset对象的setCharacterEncoding方法。

4.1.2 包配置

在Java项目中,类通常都需要“包(package”来进行组织和管理。同样的道理,struts.xml中众多的Action也需要进行统一的组织和管理,所以Struts 2的配置文件也引入了“包(package”,并通过“包(package”来管理Action和拦截器。

在struts.xml文件中,package元素用来配置包,配置时通常需要指定以下三个属性:

 name:必需属性,指定包的名字,不允许重复。

extends:可选属性,指定要继承的包,可以继承其它包中定义的action、拦截器等。

 namespace:可选属性,定义该包的命名空间(命名空间将在下一节讲述)。




/index.jsp
/fail.jsp




在上述代码中,我们使用package元素配置了一个包,名字是“admin”,并继承了“struts-default”包。

通过使用extends,你可以指定本package继承另外一个package的所有的配置。当某个package继承了另外一个package的所有配置,那么你就无需对父package中已经声明过的配置定义做再次的定义。 
   同时,如果重复定义父package中已声明过的配置定义,那么这些重复定义声明将覆盖父package中的相关定义。

上例中,我们让admin包继承了struts-default包,那么这个包又在哪里呢?struts-default是Struts 2默认的包,该包定义在struts-defalut.xml文件中。该文件是Struts 2框架自带的配置文件,为框架提供诸多默认配置并在运行时自动被加载。

现在我们打开struts2-core-2.1.8.1.jar,在jar包中可以找到struts-default.xml文件。打开struts-default.xml后,内容如下:


      
          
          
      
          
          
          
          
      
          
          
          
          
           
        
          
              
                  
                  
                  
      
                  
       
              
      
              
                  
                  
      
                  
      
                  
                  
                      
                      
                      
                      
                      
                      
                  
      
                  
                  
                      
                      
                      
                      
                      
                      
                      
                      
                      
                      
                      
                      
                      
                      
                      dojo\..*  
                      
                      
                      
                        input,back,cancel,browse  
                      
                      
                        input,back,cancel,browse  
                      
                  
      
                  
      
             
      
              
      
          
      
    

在上面的内容中,我们看到定义了一个包 名字是struts-default,其中引用了一系列的系统拦截器,并定义了一些拦截器栈和默认拦截器,admin包就是继承了这个struts-default包,也就继承到了里面的默认拦截器的配置,通过第一章的学习,我们知道action运行前后会有一系列的拦截器自动运行,也就是因为继承了struts-default包的缘故!

因此,正常情况下,自定义的action在配置的时候要确保直接或者间接的继承到struts-default,否则action可能无法正常工作。因为struts2很多核心功能都是拦截来实现的,如,从请求中把请求参数封闭到action,文件上传和数据验证等都是通过拦截器实现的,struts-default定义了这些拦截器和Result类型,可以这么说,当包继承了struts-default才能使用struts2提供的核心功能,

 4.1.3 命名空间配置

在实际应用中,同一个struts.xml文件中可能会出现同名的Action,为了便于管理,Struts 2通过命名空间来区分同名Action,即Struts 2通过Action的逻辑名和其所在的命名空间来标识一个Action。一个命名空间中不能存在同名的Action,不同的命名空间可以存在同名的Action。在struts.xml文件中,通过给包(package)指定namespace属性来为Action设置命名空间。




/list.jsp


在上述代码中,我们配置了一个名字是“admin”的包,并指定其命名空间为“/admin”,这时在访问包中名字是“query”的action时,应该这样写:

http://localhost:8080/web应用名/admin/query.action

在URL中必须指明命名空间的名字,如果一个包没有配置命名空间,那默认为“”。此时就应该这样写:

http://localhost:8080/web应用名/query.action

4.1.4 包含配置

在实际应用中,Struts 2配置文件可能会变的十分庞大,这不利于管理和维护。这时我们可以把一个Struts 2配置文件拆分成若干个配置文件,这样也有利用团队协作开发。




/index.jsp
/fail.jsp


/list.jsp


query.action
/error.jsp


    

上述代码配置了三个Action,下面我们把这个配置文件拆分成两个,分别是struts1.xml和struts2.xml,代码如下所示。

struts1.xml




/index.jsp
/fail.jsp


   
struts2.xml



/list.jsp


query.action
/error.jsp



拆分之后,在struts.xml文件中通过include元素来包含struts1.xml和struts2.xml,代码如下所示。





include元素的file属性用来设置配置文件的路径。

4.1.5 struts.properties

Struts 2框架有两个配置文件,一个是我们前面常用的struts.xml,另一个是struts.properties。这个文件是struts2框架的全局属性配置文件。

struts.properties是一个标准的Properties文件,该文件是由一系列的key-value(键值对)组成,每个key就是一个Struts 2的属性,该key对应的value就是一个Struts 2的属性值。该文件通常放在Web应用的WEB-INF/classes目录下,Struts 2框架会自动加载这个文件。

下面将该文件常用的配置属性详细地列举出来。

 struts.i18n.encoding

设置Web应用的默认编码集。此属性对于处理中文请求参数很有用,通常设置为GB2312或者utf-8。

 struts.multipart.parser

该属性设置处理multipart/form-data的MIME类型请求的框架,该属性支持cos、pell和jakarta等属性值,即分别对应使用cos的文件上传框架、pell上传及common-fileupload文件上传框架,该属性的默认值为jakarta。文件上传会在后面的章节中介绍。

 struts.multipart.saveDir

该属性设置上传文件的临时保存路径,该属性的默认值是javax.servlet.context.tempdir。

 struts.multipart.maxSize

该属性设置Struts 2文件上传中整个请求内容允许的最大字节数。

struts.action.extension

该属性设置需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts 2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。

 struts.configuration.xml.reload

该属性设置当struts.xml文件改变后,系统是否自动重新加载该文件。该属性的默认值是false。

 struts.custom.i18n.resources

该属性指定Struts 2应用所需要的国际化资源文件,如果有多个国际化资源文件,则多个资源文件的文件名以英文逗号(,)隔开。

 struts.configuration.files

该属性指定Struts 2框架默认加载的配置文件,如果需要指定默认加载多个配置文件,则多个配置文件的文件名之间以英文逗号(,)隔开。该属性的默认值为struts-default.xml,struts-plugin.xml,struts.xml,看到该属性值后大家应该明白为什么Struts 2框架会默认加载struts.xml文件了。

struts.properties文件的内容均可在struts.xml中以元素进行配置。如果这两个文件中的配置有冲突,那么将以struts.properties文件的内容为准,这与struts的搜索顺序有关。

sturt2中搜索加载常量的顺序是:

struts-default.xml     

struts-plugin.xml      

struts.xml             

sturts.propreties     

web.xml                

如果在struts.xml中做了主题的配置:

同时在struts.properties中做了主题的配置:

struts.ui.theme=simple

并使用struts2标签开发页面如下:

    

    

    

    

运行程序效果如图4.1.1所示:

Struts 2配置详解_第1张图片 

图4.1.1 主题配置

显然 Struts.properties中的主题配置效力高于struts.xml。

4.2 Struts 2的Action

在使用Struts 2框架进行开发时,开发者需要编写大量的Action,这是应用的核心。Action的主要作用有三个:

Ø 封装客户端请求数据

Ø 处理业务

Ø 返回结果

在上一章我们说过Struts 2的Action可以不用继承任何类和实现任何接口,它可以作为一个普通的JavaBean来使用。但是在实际应用中,我们仍需要一些接口和类来简化Action。

1. Action接口

为了使开发者编写的Action更加规范,Struts 2提供了Action接口,下面是该接口的源代码。

package com.opensymphony.xwork2;
public interface Action {
    public static final String SUCCESS = "success";
    public static final String NONE = "none";
    public static final String ERROR = "error";
    public static final String INPUT = "input";
    public static final String LOGIN = "login";
    public String execute() throws Exception;
}

由上述代码可知,Action接口中定义了五个字符串常量:SUCCESS、NONE、ERROR、INPUT、LOGIN。我们在execute方法中返回的字符串就来自这里。自定义的Action可以不实现该接口,开发者可以在execute方法中自定义返回字符串。

2. ActionSupport类

我们自定义的Action通常继承com.opensymphony.xwork2.ActionSupport类,此类实现了一些有用的接口(包括上面的Action接口),提供了国际化、数据验证等很多实用功能。该类的内容会在后续章节中学习。

4.2.1 动态方法调用

在上一章的任务实训部分,我们使用Struts 2实现了对管理员的增、删、改、查等操作。在实现过程中,每个请求都对应一个Action,例如:添加管理员对应的是AddAction,查询管理员对应的是QueryAction,删除管理员对应的是DelAction。在实际应用中,类似的情况经常出现,即:随着项目规模的扩大,我们不得不管理大量的Action,Struts 2框架提供了动态方法调用来解决这个问题,也就是说我们可以在一个Action中自定义不同的方法来处理多个请求。

DMI(Dynamic Method Invocation)即动态方法调用,这时不能只请求某个Action,还要使用感叹号(!)来标识出要调用的方面名,语法如下所示:

语法

Action的逻辑名!Action中的方法名 .action

示例4.1

下面我们把上一章的实训任务通过动态方法调用进行优化,这时只需要一个Action,代码如下所示:

public class AdminAction {
private int id;
private String logName;
private String logPwd1;
private AdminDao ad=new AdminDao();
public String addAdmin() {   //添加管理员
if(ad.addAdmin(logName, logPwd1))
return "success";
else
return "fail";
}
public String queryAdmin() {   //查询管理员
List adminList=ad.getAllAdmin();
Map req=(Map)ActionContext.getContext().get("request");
req.put("adminList", adminList);
return "list";
}
public String delAdmin(){    //删除管理员
if(ad.delAdmin(id))
return "success";
else
return "fail";
}
//省略getter和setter方法
}

在上述代码中,AdminAction没有继承任何类和实现任何接口,我们在该类中自定义了三个方法分别实现对管理员的增加、删除和查询,返回字符串也是自定义的。

提示

Action中的自定义方法必须是无参的,返回类型必须是String

下面是struts.xml的代码,这时的配置文件就精简了许多。






   admin!queryAdmin.action
/error.jsp
/list.jsp



addAdmin.html视图页面的表单修改如下,其他代码不变。
... ...


list.jsp视图页面的【删除】超链接修改如下,其他代码不变。

删除

 通过动态方法调用优化后的项目更加精炼,方便以后的维护。 

4.2.2 method属性

DMI在实际应用中可能会出现安全隐患,因为一旦某人知道了Action的名字和其中的方法名,他就可以通过URL进行任意的调用。Struts 2框架提供了一种更加安全的方式来实现DMI,在配置Action时可以通过method属性来指定需要调用的方法。

示例4.2

下面我们通过method属性来修改示例4.1,struts.xml代码如下所示:





/error.jsp
/list.jsp


query.action
/error.jsp


query.action
/error.jsp


   

在上述代码中,我们配置了三个,并分别设置了method属性,属性的值就是Action中的方法名。虽然通过method属性实现动态方法调用可以避免安全隐患,但是这种方法会导致struts.xml中的越来越多,不利于管理和维护,为了解决这个问题我们可以使用通配符。

4.2.3 通配符的使用

在struts.xml文件中可以通过通配符简化的配置,元素的name属性支持通配符,通配符用星号(*)表示,用于匹配0到多个字符串。

示例4.3

下面我们把示例4.2通过通配符进行优化,代码如下所示。




queryAdmin.action
/error.jsp
/list.jsp




name属性的值是“*”,表示允许这个Action可以匹配任何以“.action”结束的URL。method属性的值是“{1}”,表示该属性的值是name属性值中的第一个“*”。例如:

http://localhost:8080/Struts6/queryAdmin.action

当使用上面的URL请求时,“*”和“{1}”就变成了“queryAdmin”,正好是Action中实现添加管理员的方法名。

http://localhost:8080/Struts6/delAdmin.action?id=40

当使用上面的URL请求时,“*”和“{1}”就变成了“delAdmin”,正好是Action中实现删除管理员的方法名。

通过通配符实现动态方法调用不仅避免了安全隐患,还简化了struts.xml的配置,建议在实际应用中使用这种方法进行开发。

4.2.4 配置默认Action

如果请求的Action不存在,那么页面上可能会呈现HTTP 404错误,为了避免这种情况的发生,Struts 2框架可以指定一个默认的Action,如果没有一个Action匹配客户端请求,那么这个默认的Action就会被执行。

在struts.xml中,通过元素来配置默认的Action。每个元素配置一个默认的Action。




/error.jsp

... ...



如果action元素的class属性省略了,表示将使用默认的ActionSupport类。 

4.3 Result配置

Struts 2的Action处理完用户请求后会返回一个字符串,该字符串表示逻辑视图名。struts.xml文件通过元素配置逻辑视图名,并实现与物理视图资源的映射。元素的配置包含两部分:由name属性设置逻辑视图名,由type属性设置视图类型。

常用的结果类型有dispatcher类型和redirect类型。

Ø dispatcher类型相当于“转发”,request、session等对象都会被转发到视图页面。

Ø redirect类型相当于“重定向”,将会丢失request、session等对象。

如果不设置type属性,默认的类型是dispatcher。

4.3.1 动态结果

在某些情况下,事先并不能确定使用哪个结果视图,必须在程序运行期间才能确定,这时如何在struts.xml中进行配置呢?我们可以在配置时使用表达式,在程序运行时,由框架根据表达式的值来确定要使用哪个结果视图,这就是动态结果。

下面我们通过一个案例来演示动态结果的用法,完善登录案例,如果是普通用户就跳转到user.jsp,如果是管理员就跳转到admin.jsp。用户的身份必须在程序运行过程中才能确定。

示例4.4

需要在原来的Action中增加一个属性用来标识用户的身份,并提供getter/setter方法,代码如下所示。

public class AdminAction {
//省略其他属性
private String flag;
public String login()
{
if(user.isAdmin())
flag="admin";
else
flag="user";
return "success";
}
public String getFlag() {
return flag;
}
public void setFlag(String flag) {
this.flag = flag;
}
     //省略其他业务方法
    //省略其他getter和setter方法
}


如果是普通用户,那么flag属性的值被设置为“user”,如果是管理员,那么flag属性的值被设置为“admin”。

下面我们看一下在struts.xml文件中如何配置,代码如下所示。




/${flag}.jsp
/fail.jsp




注意上述代码中的加粗部分,我们把视图页面的名字用“${ flag }”代替,“${ flag }”表达式可以获得Action中flag属性的值。在程序运行时,如果是管理员登录,该表达式的值为admin,即跳转到admin.jsp;如图是普通用户登录,该表达式的值为user,即跳转到user.jsp。

4.3.2 全局结果

在之前的项目中,我们都是把元素配置到了元素内部,这些不能被其他Action使用。但是某些可能是公用的,即多个Action都需要同一个,这时我们可以通过配置全局结果来满足需求。

全局结果定义在包(package)中,而不是某个元素内部,包(package)中的所有Action都可以使用这个结果。




/error.jsp


/index.jsp




在struts.xml文件中使用元素配置全局结果,可以配置多个全局结果。

 

4.4 属性驱动与模型驱动

4.4.1 属性驱动

在Struts 1中,ActionForm用来封装客户端请求数据。在Struts 2中,没有了ActionForm,使用Action就可以封装客户端请求数据,例如登录案例中的Action,代码如下所示。

public class AdminAction extends ActionSupport {
private int id;
private String name;
private String pwd;
public String execute()
{
}
//省略getter和setter方法
}
与之对应的表单如下所示。
管理员登录
登录名称:
登录密码:

注意上述代码中的加粗部分,表单中每一个元素的name都对应Action中的一个属性,程序在运行时,Struts 2通过Action的属性来封装客户端请求数据,这种方式称之为属性驱动。我们前面所编写的案例采用的都是这种方式。

4.4.2 模型驱动

属性驱动使得Action既要封装客户端请求数据,还要处理业务,这就造成Action承担了过多的职责,分工也不够清晰。更好的解决办法就是采用单独的Model(模型)来封装请求数据,这就是模型驱动。模型驱动中的Model(模型)其实就是一个实体类或POJO,专门用来封装客户端请求数据。

示例4.5

下面我们把前面的登录案例改成模型驱动。首先需要一个封装登录表单的实体类Admin作为模型,代码如下所示。

public class Admin {

private String name;  //登录名称

private String pwd;   //登录密码

//省略getter和setter方法

}

Action就不需要分散的属性来封装表单数据了,只需要一个Admin类型的属性即可,代码如下所示。

public class LoginAction extends ActionSupport {
private Admin admin;
public String execute() {
AdminDao ad=new AdminDao();
if(ad.checkLogin(admin.getName(), admin.getPwd()))
return SUCCESS;
else
return ERROR;
}
public Admin getAdmin() {
return admin;
}
public void setAdmin(Admin admin) {
this.admin = admin;
}
}
与之对应的表单需要进行如下修改。
管理员登录
登录名称:
登录密码:

注意上述代码中的加粗部分,在使用模型驱动时,表单元素name属性的值由两部分组成,第一部分是Action中模型对象的名字,第二部分是模型对象的属性名。如果在实际应用中需要在页面上输出模型对象中属性的值,则可以使用“${ admin.name }”进行输出。

本章总结

 Struts 2的配置文件

(1)中文乱码处理。可以通过名为struts.i18n.encoding的常量配置字符集。

(2)包配置。包的常用属性有name, extends和namaspaces。

(3)包含配置。

 Struts 2的Action

(1)动态方法调用。

(2)Method参数和通配符的使用。

Result配置

属性驱动和模型驱动

任务实训部分

1:查询图书

训练技能点

Ø method属性

Ø 通配符

  需求说明

在第二章的核心任务部分我们讲解了一个查询图书的案例,现在要求使用Struts 2进行

重构,并应用method属性和通配符进行优化处理

  实现步骤

(1) 创建对应图书表的实体类Book.java

(2) 创建实现数据库连接和关闭的工厂类DaoFactory.java

(3) 创建实现图书查询功能的Dao类BookDao.java

(4) 创建实现图书查询业务的Action类QueryAction.java,参考代码如下所示。

public class QueryAction{
private String keywords;
private BookDao bd=new BookDao();
private List bookList=new ArrayList();
private ActionContext ac=ActionContext.getContext();
private Map req=(Map)ac.get("request");
public String queryByName()   //按书名查询
{
bookList=bd.getBooks("name", keywords);
req.put("bookList", bookList);
return "success";
}
public String queryByAuthor()  //按作者查询
{
bookList=bd.getBooks("author", keywords);
req.put("bookList", bookList);
return "success";
}
public String queryByPublisher()  //按出版社查询
{
bookList=bd.getBooks("publish", keywords);
req.put("bookList", bookList);
return "success";
}
//省略getter和setter方法
}

(5) 创建实现查询界面的视图query.html,参考代码如下所示。  


    
关键字:
按书名查询 按作者查询 按出版社查询

由于本案例要使用动态方法调用,所以在该页面我们需要使用JS动态合成请求URL,运行效果如图4.2.1所示。

Struts 2配置详解_第2张图片

图4.2.1 查询界面

(6) 编辑struts.xml,参考代码如下所示。





/list.jsp


(7) 显示查询结果的视图页面list.jsp这里不再多述,运行效果如图4.2.2所示。

 

Struts 2配置详解_第3张图片

图4.2.2 查询结果

2:实现简易计算器

训练技能点

 动态方法调用

 模型驱动

  需求说明

使用动态方法调用和模型驱动优化上一章的实训任务4

  实现步骤

(1) 创建实体类Calculator.java,参考代码如下所示。

public class Calculator {
private double num1;
private double num2;
private double result;
 //省略getter和setter方法
}

(2) 创建Action类CalculatorAction.java,参考代码如下所示。

public class CalculatorAction {
private Calculator cal;
public String jia()      //加法运算
{
cal.setResult(cal.getNum1()+cal.getNum2());
return "result";
}
public String jian()     //减法运算
{
cal.setResult(cal.getNum1()-cal.getNum2());
return "result";
}
public String cheng()    //乘法运算
{
cal.setResult(cal.getNum1()*cal.getNum2());
return "result";
}
public String chu()      //除法运算
{
cal.setResult(cal.getNum1()/cal.getNum2());
return "result";
}
//省略getter和sertter方法
}


(3) 创建计算器的视图页面,参考代码如下所示。

简易计算器
第一个数
第二个数
结果

由于要使用动态方法调用和通配符,所以在页面中我们使用JS合成请求URL。同时我们使用EL表达式输出了Action中属性的值。

(4) 配置struts.xml,代码如下所示。



/calculator.jsp

 
Struts 2配置详解_第4张图片

图4.2.3 计算器

3:模型驱动

训练技能点

模型驱动

  需求说明

把上一章的前三个实训任务使用模型驱动进行优化

巩固练习

一、选择题

1. 以下关于元素的说法错误的是()。

A. name属性是必须的

B. name属性不能唯一确定一个Action

C. method属性表示调用Action中的哪个方法

D. 通配符除了星号(*)还有下划线(_)

2. 以下关于元素的说法错误的是()。

A.  type属性指定结果类型

B.  name属性指定结果的逻辑名

C.  name属性的值必须是Action接口中的某个常量

D. 默认的类型是dispatcher

3. Struts 2的结果类型有()。

A.  dispatcher

B. redirect

C.  chain

D.  success

4. DMI的正确写法是()。

A. Action的逻辑名!Action中的方法名 .action

B. Action的逻辑名_Action中的方法名 .action

C. Action的逻辑名!Action中的方法名 .do

D. Action的逻辑名_Action中的方法名 .do

5. 以下关于Struts 2配置文件说法正确的是()。

A. struts.properties是必须的配置文件

E. struts.properties通过标签元素进行配置

F. 只能同时使用struts.properties与struts.xml中的一个

G. struts.properties中的配置通常都可以在struts.xml中进行配置 

二、上机练习

 把上一章的课后上机练习使用模型驱动和动态方法调用进行重构。运行效果图如下。 

图4.3.1 增加【修改】超链接

 

图4.3.2 密码修改页面

 

你可能感兴趣的:(基础框架,道本自然)