Action的三种实现方式,struts.xml配置的详细解释及其简单执行过程(二)

勿以恶小而为之,勿以善小而不为--------------------------刘备

劝诸君,多行善事积福报,莫作恶

上一章简单介绍了Struts2的'两个蝴蝶飞,你好' (一),如果没有看过,请观看上一章

一 Action的三种实现方式

上一章开发的HelloAction和HelloAction2,并没有继承任何类或者实现任何接口,但是必须有一个execute() 方法,方法返回值是String类型。

这样的代码不容易理解,更并不能使人看得出这个类是干什么的,甚至不能区分这个控制器类与普通的Java类有什么区别,通常开发中不这样做。

我们开发者在开发Struts2框架的时候,希望自己写的这个Action类能够具有易理解性,且已经支持某些功能,如参数接收,文件上传等。

一.一 第一种实现方式(普通Java类,里面只包含execute()方法)

package com.yjl.web.action;
import org.apache.log4j.Logger;
/**
* @author 两个蝴蝶飞
* @version 创建时间:2018年8月23日 上午9:41:32
* @description 第一种实现方式,普通java类,
* 有一个execute()方法,也可以多写几个方法,用action中的标签method来控制,可以正常访问。
*/
public class Hello1Action {
    private static Logger logger=Logger.getLogger(Hello1Action.class);
    public String execute() {
        logger.info("两个蝴蝶飞,web层你好");
        return "success";
    }
}

不具有开发时要求的规范性,且不支持某些struts2自身提供的功能。

方法名称只有一个 execute()

一.二 第二种实现方式(实现Action接口)

package com.yjl.web.action;
import com.opensymphony.xwork2.Action;
/**
* @author 两个蝴蝶飞
* @version 创建时间:2018年8月23日 上午10:54:03
* @description 第二种实现方式,实现Action接口,重写里面的execute()方法
* 有一个execute()方法和五个String类型的常量
*/
public class Hello2Action implements Action{
    @Override
    public String execute() throws Exception {
        return Action.SUCCESS;
        //return Action.ERROR;
        //return Action.LOGIN;
        //return Action.NONE;
        //return Action.INPUT;
    }
}

注意,Action接口是xwork2包下的接口。

实现了Action接口,使开发者能够看出来这是一个Action,具有了一定程度上的开发规范,

但是实现了Action接口,所以必须要重写execute()方法。

一般自己写Action,构思好之后上来就直接add(), edit(), delete(). select() 这些业务方法,

每次都要重写execute()方法,不太方便。 而且这种方式不具有struts2中某些功能,如验证框架和国际化。

Action中接口中有五个常用的结果字符串(好多方法都返回success,error,login,input,none,故将其封装了一下) .

这些字符串虽然是大写,然而真实的值是全部小写.

package com.opensymphony.xwork2;

public abstract 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 abstract String execute()
    throws Exception;
}

一.三 继承ActionSupport类(官方推荐)

    package com.yjl.web.action;
    import com.opensymphony.xwork2.ActionSupport;
    /**
    * @author 两个蝴蝶飞
    * @version 创建时间:2018年8月23日 上午11:04:20
    * @description 第三种方式,继承ActionSupport类。
    * ActionSupport类实现了Action接口,也有Action中的五个常量.
    */
    public class Hello3Action extends ActionSupport{
        public String list() {
            return "list";
        }
    }

继承了ActionSupport类,不需要重新写execute()方法,直接写业务方法即可。

ActionSupport类,已经实现了 Action接口。 其具备Action中的五个常量,并且该类还实现了其他接口,

源代码:

    public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable{
    ...
    public String execute() throws Exception
    {
        //默认返回的是 success 字符串 
         return "success";
    }
    ...
}

如验证框架(Validateable,ValidationAware),国际化(LocaleProvider)。

以后开发中,使用 继承 ActionSupport 类的形式。

二 配置文件 struts.xml中节点的详细解释

在src下有一个struts.xml的配置文件,它配置了开发者自己编写实现的Action,是struts2框架的核心,不能改变文件名称。(注意,是struts.xml,并不是struts2.xml,并没有那个2)。

在struts.xml中,最上面是一个约束,<struts></struts>是一个根节点。

二.一 修改常量节点 <constant></constant>

在struts-core.jar核心包下,有一个包org.apache.struts2包下,有一个default.properties属性文件,里面记录了很多常用的常量,

其中常见的有:

struts.i18n.encoding=UTF-8 
struts.multipart.maxSize=2097152
struts.action.extension=action,,
struts.enable.DynamicMethodInvocation = false
struts.devMode = false
struts.ui.theme=xhtml
struts.ognl.allowStaticMethodAccess=false

建议修改后的值为:

###国际化操作,编码格式为UTF-8
struts.i18n.encoding=UTF-8
###上传文件时最大的上传大小,默认为2M. 根据项目情况具体填写值,建议后面加两个00
struts.multipart.maxSize=209715200
###struts的访问后缀名, struts1框架默认的是 .do 
struts.action.extension=action,,
###struts是否可以访问静态方法
struts.enable.DynamicMethodInvocation =true
###struts是否是开发者模式
struts.devMode =true
###struts中ui标签的主题,建议为simple
struts.ui.theme=simple
###ognl中是否可以访问静态方法,为true
struts.ognl.allowStaticMethodAccess=true

可以在struts.xml中进行相应的修改,如

 <!--修改国际化编码 -->
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<!--修改是否为开发者模式 -->
<constant name="struts.devMode" value="true"></constant>

按照name,value值的形式进行填写。

也可以在src下新建一个struts.properties,然后将这些值放置进去,struts也会自动struts.propeties中的常量值的。

在这里插入图片描述

也可以在web.xml中,在<filter></filter>中,以<init-param></init-param>局部参数的形式传递进去。

在这里插入图片描述

建议使用第一种方式,在struts.xml中用<constant></constant>,毕竟这个文件常常打开,出错了也容易发现。

二.二 分模块开发<include file="" />

在实际的项目中,有很多的模块,如果所有的配置都放在一个struts.xml,那么一旦这个struts.xml被其他人误操作导致了错误,那么其他人的项目将无法运行的,当配置内容过多时,struts.xml的内容太长,不便于维护,所以最好是分模块开发,一个模块用一个配置文件,然后再利用<include file="具体文件名" /> 进行导入, 类似 于jsp中的<include file="" />静态包含一样。

所以建议每一个模块都写一个模块.xml,然后在struts.xml中引入即可。如有三个模块 User模块和Class,Course,那么可以将User的配置放置在user.xml中,Class配置放置在class.xml中,course模块放置在course.xml,在struts.xml中只需要

    <include file="user.xml"></include>
    <include file="class.xml"></include>
    <include file="course.xml"></include>

静态包含即可。 注意,file的文件路径引用是否输入正确。

正确的位置引用,点击ctrl+模块.xml时,可以跳转到相应的.xml文件中。如果没有跳转和反应,那说明位置引用错误,需要重新检查一下。

二.三 包节点 <package>

在struts.xml配置文件中,最重要的节点就是package节点。 package,分包。 可以将action进行分包处理。

这样每一个action或者每一组action用package进行隔开,便于维护,类似于java中package的概念。

二.三.一 <package> 节点的使用

<package name="hello" extends="struts-default" namespace="/">
        <!--具体的Action-->
</package>

package中name节点是package的名字,是独一无二的,不能够重复。 最好与模块名相同或者起一个有意义的名称。

extends节点表示继承,即package之间可以相互的继承,来避免重复化功能的编写。 默认为struts-default。

struts-default中struts已经定义了很多功能,开发者自己写的包只需要extends 这个包名struts-default,

就拥有了struts已经定义好的功能。 如拦截器功能,文件上传功能。

用户也可以自己继承自己所写的包 。如父包名为<package name="parent" extends="struts-default">

那么子包只需要<package name="child" extends="parent">, 这样child包不但拥有struts-default的功能,也拥有parent包中的特殊功能,这也是Java的多重继承的体现。 所以package的name 要符合标识符的规范,具有可读性。

namespace节点表示命名空间,以/开头,默认是"/" 。是为了在访问路径和访问请求url方面体现package的分包作用. package中的name是在配置文件中体现分包,namespace是在url中体现分包。 建议开发中,namespace的路径名与name保持一致。 package中的namespace的值与子节点action中name的值,共同构成了完整的访问请求路径。

二.三.二 <package></package> 子节点<action></action>节点的使用

在Hello3Action中定义两个方法,一个是list()查询,一个是add()添加的方法。

package com.yjl.web.action;
import org.apache.log4j.Logger;
import com.opensymphony.xwork2.ActionSupport;
/**
* @author 两个蝴蝶飞
* @version 创建时间:2018年8月23日 上午11:04:20
* @description 测试action标签中method的方法访问
*/
public class Hello3Action extends ActionSupport{
    private static final long serialVersionUID = 8737138848863458260L;
    Logger logger=Logger.getLogger(Hello3Action.class);
    public String list() {
        logger.info("执行list方法");
        return "list";
    }
    public String add() {
        logger.info("执行add方法");
        return "add";
    }
}

<action></action> 标签,有三个基本的属性,

    <action name="list" class="com.yjl.web.action.Hello3Action"
        method="list">

</action>

其中name为action的名字,表示区别一个package包下的不同的action。 其中这个name的值,不应该随便取,应该是要访问的方法名。

在浏览器客户端请求的url为 /项目名/package的namespace名称/action的name名称.action;

class为要访问的那个Action的全限定名称,是class,用.(点)进行分隔。

其中,class 可以省略, 省略默认为 ActionSupport 类, 全限定名称为: com.opensymphony.xwork2.ActionSupport
method为要访问的那个方法名称,类 extends ActionSupport 后,有很多很多的方法,如list(), add(), delete()等,那么怎么知道具体要访问哪个方法呢? 用method这个属性. method="要方法的方法名" ,是方法名。

action还有一个节点是converter,表示所用的是哪一个类型转换器。(后面会有相应的解释)

很清楚, action 中的 class指定了访问的是哪一个action, method 指定了访问的是哪一个具体的方法, 利用了反射技术实现。

在本实例了有两个方法,所以要进行写两个Action, 一个Action类中会有多个方法,难道要一个个配置多个Action吗?

Struts2提供了一些简单的方式

二.三.三 配置Action的三种形式

二.三.三.一 通过配置method的属性完成

简单举例如下:

    <action name="list" class="com.yjl.web.action.Hello3Action"
        method="list">
            
    </action>
  <action name="add" class="com.yjl.web.action.Hello3Action"
        method="add">
            
    </action>

缺点: 有几个方法,就要配置有几个action,当方法过多时,不易维护。

二.三.三.二 通过配置 通配符完成。

简单举例如下:

    <action name="Hello3_*" class="com.yjl.web.action.Hello3Action"
        method="{1}">
            
        </action>

name的值为: 类简写名(去掉Action后)_* method中的值取第一个{1},从1开始,不是从0开始。

这样访问Hello3Action中的list方法,访问路径就是 Hello3_list

访问Hello3Action中的add方法,访问路径就是Hello3_add

简化了action的相关配置。

也有的人配置的更狠, 会配置成_, 即:

    <action name="*_*" class="com.yjl.web.action.{1}Action"
        method="{2}">
            
        </action>

User类中的list就是User_list, User类中的add就是User_add,

Class类中的list就是Class_list,Class类中的add就是Class_add

这样虽说简化了开发,但却不利用 result 节点的维护 ,不建议这样配置。

好多类的好多方法返回值,都写在这一个action 下面,会乱。

二.三.三.三 动态方法访问

不是用 * 通配符,而是用! 号。 即:

想访问UserAction中list方法() 前端写url为 userAction!list.action
想访问UserAction中add方法() 前端写url为 userAction!add.action
想访问ClassAction中list方法() 前端写url为 classAction!list.action
想访问ClassAction中add方法() 前端写url为 classAction!add.action

这样访问也特别的方便。

这样的话, action中只需要配置name和class即可。 method已经由外部指定了,不需要写method的值了。

需要先添加变量 struts.enable.DynamicMethodInvocation, 使其变成 true,开启。

    <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>

如果是UserAction的话,配置应该是:

<action name="userAction" class="com.yjl.web.action.UserAction" >
            
</action>

ClassAction的话,配置应该是

<action name="classAction" class="com.yjl.web.action.ClassAction" >
            
</action>

二.三.四 action子节点result的配置

result表示结果,是对方法的返回值进行相应的分析。有两个属性,name和type

    <result name="success" type="dispatcher">/index.jsp</result>

其中name的值要与方法的返回值保持一致。

如 list方法返回值是return SUCCESS,那么这个list方法的返回值对应的result的值就是 <result name="success"> ,

如果返回是"hello", 那么这个name的返回值就是 <result name="hello">

如果在action中配置通配符, name=Hello3_*形式,method="{1}", 那么为了简化result的配置,可以将result配置成 name={1},

相应的.jsp,可以变成 /{1}.jsp。

但这样必须保证Action中方法的名称与返回值的名称相同,并且与跳转到的jsp的名称也要相同, 这样不太好。

result中type五种常见的形式, dispatcher(转发到jsp),redirect(重定向到jsp), chain(转发到另外一个方法),redirectAction(重定向到另外一个方法),stream(上传和下载流)

其中dispathcer和redirect是跳转到jsp,如果想要传递数据,用dispather,

如果不想传递数据,用redirect (dispathcer是转发,redirect是重定向)

chain,redirectAction是跳转到action的操作,一般用于这同一个类中两个方法之间的跳转,

如add()添加成功之后,需要跳转到list()方法进行显示结果,这时就可以配置成:

    <result name="add" type="redirectAction">Hello3_list</result>

地址url也会相应的改变,如果是chain的话,地址栏是不会改变的。 chain是转发到action, redirectAction是重定向到action.

也可以在不同包之间的action进行的跳转 。

如 add 方法 想到跳转到 /class 命名空间下的 Hello2Action 的 list 方法。

<result name="add" type="redirectAction">
    <!-- 要跳转到哪一个命名空间,即哪一个包 -->
    <param name="namespace">/class</param>
    <!-- 要跳转到哪一个Action 不加后缀 -->
    <param name="actionName">Hello2Action</param>
    <!-- 跳转到哪一个方法 -->
    <param name="method">list</param>
    <!-- 可能要传递的参数. 用ognl表达式,根据情况添加 -->
    <param name="id">${id}</param>
</result>

通过 param 标签来配置带参还是不带参。

二.四 全局结果页面与局部结果页面。

这个全局是相对于package来说的,是package中的全局,并不是所有的struts.xml中的全局,所以全局结果的节点位置应该放在package节点里面,与action节点平行。 用<global-results> </global-result> 节点。

常用的全局结果页面有两种:

error错误页面,页面出错了都显示这个页面,

login 登录页面, 如果没有登录,输入任何url都会跳转到login页面(认证时用)

noprivilege 没有权限页面,如果用户没有权限访问了某一个页面,会给出相应的提示(授权时用)

<global-results>
            <result name="error">/error/error.jsp</result>
            <result name="login">/login.jsp</result>
            <result name="noprivilege">/noprivilege.jsp</result>
</global-results>

当全局结果页面与局部结果页面发生冲突时,以局部结果页面为准。

全局配置时:

<global-results>
            <result name="success">/successGlobal.jsp</result>
</global-results>

在该包下的某个action 的方法result 也返回了 success

    <result name='success'>success.jsp</result>

那么,当逻辑视图为 success时,最终将返回 success.jsp

二.五 配置跳转页面

在开发中,常常有这么一种情况,

请求login.jsp 时,为 /login, 那么就跳转到 login.jsp 页面,

语法为 register.jsp 时,为 /register, 那么就跳转到 register 页面。

这个时候,配置 为:

    <action name="*">
            <result>/WEB-INF/content/{1}.jsp</result>
    </action>

将页面放置在 content 文件夹下面,避免用户直接访问 jsp页面。

注意,要将此 action 放置在最后, 当所有上面的action都不匹配时,才匹配这一个action.

三 Struts2的执行流程

当用户在客户端发送一个请求后,如常用的标准的http://localhost:8080/Struts_Hello/user/User_add.action时,

会经过前端控制器(StrutsPrepareAndExecuteFilter) 过滤器,执行一连串的过滤器链,然后根据user 找到了对应的package的namespape,进入到具体的package包下。 利用通配符的方式进行访问,User_add会进行匹配相应的action,根据class和method找到是哪一个类的哪一个方法,在实例化类Action之前,会先执行拦截器。通过反射实例化类,运行方法, 方法运行成功之后,有一个返回值,这个返回值会与刚才action下的<result> 中的name进行相应的匹配,匹配到哪一个,就执行哪一个result。 如果是diapatcher或者redirect,就显示到相应的.jsp页面(带有数据), 如果是chain或者redirectAction,那么就去执行那一个方法,之后进行返回具体的视图。

执行过程图如下:

在这里插入图片描述
在这里插入图片描述

谢谢您的观看!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342