Struts2框架的基本使用(三)

     上篇 Struts2框架的基本使用(二)介绍了Action和result的相关配置操作,本篇接着介绍剩下的异常处理机制和Convention插件的使用。下篇文章介绍的是Struts2框架中标签库的使用情况。

一、Struts的异常处理机制      每一个优秀的MVC框架都有一套完善的异常处理机制。我们不希望在Action中try..catch捕获异常,这样整个Action中耦合了大量的异常处理代码,一旦以后需要修改异常处理的代码就需要在Action中做大量变动,这是我们不愿意看到的。我们可以看到Struts框架的默认执行方法execute:

public String execute() throws Exception

该方法抛出所有异常,也就是我们不需要在Action中处理任何异常,但是抛出的异常又在哪被捕获了呢?在Struts框架中默认是定义了一个异常拦截器,用于捕获Action抛出的异常,它有点像我们的核心拦截器。一旦捕获到异常信息,会去struts.xml配置文件中查找该异常信息所对应的物理视图的位置,然后请求该视图页。所以我们只需要在struts.xml文件中指定映射关系即可。下面看一个例子:

//我们定义一个Action
public class LoginAction extends ActionSupport {
    public String execute() throws Exception{
        //抛出java.lang.ArithmeticException异常
        int a = 3/0;
        return SUCCESS;
    }
}
//struts.xml
<struts>
    <package name="walker" namespace="/" extends="struts-default">
        <action name="login" class="MyPackage.LoginAction">
            <result name="success">/index.jsp</result>

    //这块代码等会介绍
            <exception-mapping exception="java.lang.ArithmeticException" result="exception" />
            <result name="exception">/exception.jsp</result>
        </action>
    </package>
</struts>

当我们请求login的时候,LoginAction 在响应我们请求的时候抛出java.lang.ArithmeticException异常,于是异常拦截器捕获到异常信息之后从struts.xml中找到该异常对应的视图,dispather该页面。结果如下:

我们在Action中使用exception-mapping元素配置了异常的映射关系,该元素主要有以下两个属性:

  • exception:该属性指定了具体的异常类型
  • result:该属性用于指定一个result元素

其中的result属性的值只是指定了一个抽象的result,具体的result元素还需要自行实现,正如我们上述代码展示的一样,exception-mapping 元素的result属性指定了一个名为exception的result元素,紧接着自定义了一个具体的result元素映射到exception.jsp视图页面。

     这样就完成了对一个异常的简单映射,在实际开发中,我们需要根据自己的代码判断可能发生的异常,显式的在struts.xml中指定映射关系,避免让用户直接看到这样的异常信息。

虽然这样使得用户无法直接看到抛出的异常信息,但是对于我们程序员来说,如果想要查看异常信息调试程序该怎么办呢?这一点将在下篇文章介绍标签库的使用时说明。

二、Convention插件的约定      自Struts2.1以来,引入了Convention插件来实现开发"零配置",但是正真做到零配置还是很难的,Convention插件已经在尽力的减少配置操作了。在之前介绍的内容中,我们做了很多的配置,而使用该插件的目的就是减少这些配置的次数甚至达到零配置,但是我们需要记住这些“约定”,因为该插件就是根据这些约定实现自动配置。      Struts包下lib中有个jar包,struts2-convention-plugin-2.3.32.jar。这就是Convention插件的jar包,我们将它引入到项目中。我们首先看第一个约定,Action的搜索和映射约定。之前我们自定义一个Action是需要在Struts.xml中通过action元素的name和class实现请求和响应映射,现在我们可以使用插件省去在struts.xml中配置的过程。我们的Convention插件会自动搜索所有位于action,actions,struts,struts2包下的Java类,遇到以下两种情况,就会把该Java类当做Action处理。

  • 实现了com.opensymphony.xwork2.Action的Java类
  • 类名类似于:xxxAction的Java类

对于以上两种情况,插件才能够自动识别这是一个Action,还有一点需要注意的是,该插件一般只会在上述提到的四个包中搜索Java类,如果想要使用这种约定,一般需要将Java类的包命名为以上四种之一。当然是可以通过配置修改这些默认搜索的包的,后文介绍。

以上介绍了如何利用约定让核心拦截器可以通过插件搜索到具体的Action,下面介绍如何让这些Action能够对应于具体的URL请求。我们知道在Java中,对于一个类的命名是依照驼峰式命名规范来的(每个单词的首字母大写,其余字符小写),所以插件在解析的时候会利用这个规范,例如下面的一些例子:

Action:MyInfo------------URL:my-info
Action:UserMessage-------URL:user-message

上述的这种方式定义的Action在解析成URL的时候,会拆分成一个一个的单词,单词之间使用中划线连接。连接之后的结果作为该Action对应的URL。

还有一种方式定义的Action,在定义Java类的时候命名以XXXAction的形式,也会被解析为一个Action。插件在解析它的时候就简单了,去掉后面的Action,剩下的就是该Action对应的URL。例如:

Action:MyAction------------URL:my
Action:UserAction----------URL:user

有关第一个约定还有一点需要注意下,以前我们配置Action的时候都是在package元素中配置的,也就是每个Action都会有一个namespace(没有显式指明的为默认命名空间下),那么我们这种约定式的配置如何区分具有相同名称的Action了呢?Convention插件通过该Action的包进行区分,例如:

my.action.LoginAction------URL:/login
my.actions.user.LoginAction----URL:/user/login
him.struts.name.LoginAction----URL:/name/login
her.struts2.age.LoginAction----URL:/age/login

如上所示,Convention通过该Action的包来指定namespace的值。

接下来我们看看第二个约定,按约定映射结果。默认情况下,Convention总会到WEB-INF/content路径下查找视图资源,也就是根据Action返回的字符串组装视图资源的逻辑 名称。例如:LoginAction返回的SUCCESS,插件会到content目录下找login-success.jsp或者login-success.html。这种组装的格式为:actionName-resultCode.suffix。当然啊,这个目录是默认的,后面我们会介绍通过配置指定成我们想用的目录。下面我们通过具体的例子完整的感受下:

//首先创建一个Action
public class MyInfo implements Action {

    public String execute() throws Exception{

        return "index";
    }
}

找到WEB-INF/content路径,创建视图文件:

请求my-info:

接下来我们看看另外一种方式的实现:

//创建Action,xxxAction
public class MyAction  {
    public String execute() throws Exception{
        return "index";
    }
}

创建对应返回的视图文件:

请求/my:

我们可以看到利用Convention插件,几乎零配置,只需要记住几个约定即可。

下面看Convention的第三个约定,Action链式约定。我们之前介绍过,可以通过指定result元素的type属性值为redirectAction来使得Action的返回结果继续指向另外一个Action,从而形成链式Action。那么我们如何约定链式Action呢?我们只要将第二个Action的Java类放在和第一个Action的Java类同目录,并依照约定命名第二个Action即可。第二个Action的命名规则如下:firstActionName+resultCode。例如:

public class MyAction  {
    public String execute() throws Exception{
        return "index";
    }
}
//第二个Action命名为:MyIndexAction

下面看看完整演示:

public class MyAction  {
    public String execute() throws Exception{
        System.out.println("myAction is running");
        return "index";
    }
}
//和MyAction相同包下
public class MyIndexAction extends ActionSupport {
    public String execute() throws Exception{
        System.out.println("myIndexAction is running");
        return SUCCESS;
    }
}

创建MyIndexAction 的视图页面:

请求/my:

从控制台的输出我们也是能够看出来,这种Action的链式调用过程的。其中需要注意的是:只有在MyAction找不到指定的物理视图文件的时候才会到自己同包下查找是否存在匹配的下一个Action,也就是框架优先查找视图资源,在没有找到的情况下才会搜索是否有匹配的Action,所以我们如果想要实现链式Action处理的话,前面的Action返回的逻辑名一定不能匹配到任何物理视图。

三、Convention插件约定的一些细节      在Convention插件中,提供了大量的配置常量用于配置Convention的一些默认值。

Convention中定义的所有的常量都在Convention的jar包的struts-plugin.xml文件中定义了,我们关注几个重点的,如果日后项目中需要用到一些配置可以返回来查找。

//指定Convention插件默认从哪些包中搜索Action
<constant name="struts.convention.exclude.packages" value="org.apache.struts.*,org.apache.struts2.*,org.springframework.web.struts.*,org.springframework.web.struts2.*,org.hibernate.*"/>

我们看到该常量指定了四个包,正是我们之前强调的action ,actions ,struts,struts2。当然我们可以修改或者添加我们自己的包以便在项目中直接使用。

 <constant name="struts.convention.result.path" value="/WEB-INF/content/"/>

该常量指定了插件默认寻找资源文件的起点,我们也可以自己定义想要它从哪开始搜索资源文件。

<constant name="struts.convention.action.suffix" value="Action"/>

我们之前可以为Java类命名为xxxAction表示这是一个Action,其实我们也可以通过修改该常量的值来指定什么后缀的Java类可以被视作Action。

等等还有很多常量的配置在我们日常的项目中具有重大作用,大家在使用的时候可以返回来查阅。

到目前为止,有关Struts2的基本使用就简单的介绍完了,下篇介绍struts2的标签库技术。总结的不好,望大家不吝赐教!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏各种机器学习基础算法

原 crontab使用

2163
来自专栏吴伟祥

Linux下的shell简介(三) 原

        shell的本意是“壳”的意思,其实已经很形象地说明了shell在Linux系统中的作用。shell就是围绕在Linux内核之外的一个“壳”程序...

993
来自专栏好好学java的技术栈

学多线程的看过来,带你学习多线程中断机制

当我们点击某个杀毒软件的取消按钮来停止查杀病毒时,当我们在控制台敲入quit命令以结束某个后台服务时……都需要通过一个线程去取消另一个线程正在执行的任务。Jav...

1553
来自专栏积累沉淀

干货--Redis 30分钟快速入门

一、 redis环境搭建 1.简介        redis是一个开源的key-value数据库。它又经常被认为是一个数据结构服务器。因为它的value不仅...

32510
来自专栏C/C++基础

C/C++头文件的作用和用法

示例代码编译运行环境:Windows 64bits+VS2017+Debug+Win32。

1141
来自专栏Python

python flask里 post请求,JSON数据获取方式总结

#!flask/bin/python #encodig=utf-8 # _*_ coding:utf-8 _*_ # Writer : byz # dateTi...

5895
来自专栏iOS 开发杂谈

iOS 编译过程

iOS 编译采用 Clang 作为编译器前端,LLVM 作为编译器后端,编译器前端负责语法分析,语义分析,生成生成中间码 (LLVM IR),在这个过程中,会进...

3212
来自专栏专注 Java 基础分享

初识Hibernate之理解持久化类

     上一篇文章我们简单介绍了Hibernate相关的一些最基本的文件及其作用,并在最后完整的搭建了Hibernate的运行环境,成功的完成了与数据库的映射...

1978
来自专栏未闻Code

Tenacity——Exception Retry 从此无比简单

Python 装饰器装饰类中的方法这篇文章,使用了装饰器来捕获代码异常。这种方式可以让代码变得更加简洁和Pythonic。

1741
来自专栏cloudskyme

gsoap开发webservice

gSOAP编译工具提供了一个SOAP/XML 关于C/C++ 语言的实现,从而让C/C++语言开发web服务或客户端程序的工作变得轻松了很多。绝大多数的C++w...

3406

扫码关注云+社区

领取腾讯云代金券