• Struts2 MVC基础介绍


    流程介绍


    我们模拟一个请求/响应的情景,来介绍Struts的工作流程。注意,下面的序号和图中的序号没有严格的对应关系。

    1. 浏览器向系统发出请求,请求的地址是ac.action
    2. 请求被StrutsPreparedExecuteFilter拦截,去掉.action后缀,所得结果ac作为action的name。
      在Struts框架中,负责处理用户请求的称为action,这里的name用于获取action,找到他,让他干活。
    3. StrutsPreparedExecuteFilter在struts.xml中查找action映射相关的配置,根据ac查到action的类pkg.AcAction。
    4. StrutsPreparedExecuteFilter实例化pkg.AcAction,并调用其execute方法。
    5. pkg.AcAction工作完成之后,汇报工作结果:返回一个字符串success,称之为result。
    6. StrutsPreparedExecuteFilter使用success去查struts.xml中的结果映射部分,获取到对应的物理视图资源是ac-success.jsp。
    7. StrutsPreparedExecuteFilter使用forward的方式,将ac-success.jsp展示给用户。

    仔细看一下上面的图和内容,在脑海中回忆一下整个流程,最好能够闭眼将整个流程复述一遍,然后再继续。

    我们的工作

    在上述的流程描述中,几乎都是框架要做的事,但是为了能让框架顺利的工作,我们要提供支持性的工作。使用框架基本都是这样,我们按照框架的模式提供支持,框架自行工作。
    接下来看一下我们需要做的事:

    1. 配置StrutsPreparedExecuteFilter。
      StrutsPreparedExecuteFilter是整个流程中的核心,这个指挥中心不是自行启动的,我们需要在web.xml中启动它。这件事只需要做一次。
    2. 创建Action类
      action负责处理用户的请求,具体怎么处理,需要我们创建一个Action类来实现。
    3. 创建视图
      Action实现之后,我们要考虑返回怎样的视图给用户。在这里,我们需要创建1到多个jsp文件,担任视图的角色。
    4. 配置action映射
      配置name和class的对应关系,让Struts知道该把哪些请求分派给哪个action。
    5. 配置result映射
      Action返回的是一个普通的字符串,我们称之为处理结果或者逻辑视图,不管叫什么,总之它不是物理视图,不指定任何视图文件。Struts为了将Action类和视图文件解耦,将返回结果和物理视图的对应关系,我们称之为result映射,在配置文件中配置。

    上面五个工作,第一个只需要做一次,相对的,后面四个每创建一个action都需要做一次。

    在上面五个工作中,视图文件是jsp,我们将之视为基本知识,并不打算介绍。result映射一般是在action映射内部配置的,所以配置result将包含在配置actioin中。所以,我计划分下面三个主题,介绍上面的工作:

    1. 配置核心过滤器
    2. 创建Action
    3. 配置action

    1.配置核心过滤器


    这里使用Maven管理项目,如果要使用Struts框架,你需要引入依赖。你可以在http://mvnrepository.com/中输入struts2-core来查找可用的版本,从中选择一个并获取其依赖配置:

    <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-core</artifactId>
        <version>2.3.28</version>
    </dependency>

    在web.xml中配置下面的内容:

    1 <filter>
    2   <filter-name>struts2</filter-name>
    3   <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    4 </filter>
    5 <filter-mapping>
    6   <filter-name>struts2</filter-name>
    7   <url-pattern>/*</url-pattern>
    8 </filter-mapping>

    2.创建Action


    2.1.参数获取

    既然要在Action中处理用户请求,那么Action就需要获取用户的请求参数。我们需要为此在Action中添加对应的属性,属性的name和请求参数的name一致,类型是基本类型;另外,我们要为这个属性添加setter方法。
    如果为Action的属性提供getter方法,在jsp可以从request的属性中获取该属性。

    2.2.创建Action

    有三种方式可以创建Action。
    1. POJO
      Action不需要实现任何接口,不需要继承任何类,但是需要包含一个方法:public String execute() throws Exception。这个方法是处理请求的入口,由Struts框架调用。这个方法的名字可以自行定义,但是execute是默认的名字,使用这个可以省去一项属性配置。
    2. 实现Action接口
      全称com.opensymphony.xwork2.Action,这个接口定义了几个字符串常量,作为result;还定义了execute方法。
    3. 继承ActionSupport
      全称com.opensymphony.xwork2.ActionSupport,实现了Action。

    2.3.demo设计

    为了介绍上面3种方式,这里设计一个demo。

    以登录为例,用户填写账号和密码,提交登录请求。Action对账号密码进行权限验证,通过则跳转到首页,否则依旧跳转到登录页面。
    所以对于Action来说,就是获取请求参数:账号、密码。判断账号是否存在,密码是否正确。如果通过返回一个"success"的逻辑结果,否则返回"login"的逻辑结果。
    为了添加一点人性化的元素,我们将区分“账号不存在”和“密码错误”两种情况,并分别进行提示。
    好了,我们开始吧。

    2.3.1.POJO

     1 package cn.ljl.note.struts2.login.actions;
     2  
     3 public class LoginPOJO {
     4   private static final String VALID_USER = "admin";
     5   private static final String VALID_PWD = "admin";
     6   
     7   private static final String SUCCESS = "success";
     8   private static final String LOGIN = "login";
     9   
    10   private String username;
    11   private String password;
    12   private String tip;
    13   
    14   public String getUsername() {
    15      return username;
    16   }
    17   public void setUsername(String username) {
    18      this.username = username;
    19   }
    20   public String getPassword() {
    21      return password;
    22   }
    23   public void setPassword(String password) {
    24      this.password = password;
    25   }
    26   public String getTip() {
    27      return tip;
    28   }
    29   public void setTip(String tip) {
    30      this.tip = tip;
    31   }
    32   
    33   public String execute() throws Exception {
    34      boolean validUser = VALID_USER.equals(getUsername());
    35      boolean validPwd = VALID_PWD.equals(getPassword());
    36      
    37      if (!validUser) {
    38        setTip("用户不存在!");
    39        return LOGIN;
    40      }
    41      
    42      if (!validPwd) {
    43        setTip("密码不正确!");
    44        return LOGIN;
    45      }
    46      
    47      setTip(null);
    48      return SUCCESS;
    49   }
    50 }

    2.3.2.实现Action接口

    Action接口的类图如下:

    源代码:

     1 package cn.ljl.note.struts2.login.actions;
     2  
     3 import com.opensymphony.xwork2.Action;
     4  
     5 public class LoginAction implements Action{
     6   private static final String VALID_USER = "admin";
     7   private static final String VALID_PWD = "admin";
     8   
     9   private String username;
    10   private String password;
    11   private String tip;
    12   
    13   public String getUsername() {
    14      return username;
    15   }
    16   public void setUsername(String username) {
    17      this.username = username;
    18   }
    19   public String getPassword() {
    20      return password;
    21   }
    22   public void setPassword(String password) {
    23      this.password = password;
    24   }
    25   public String getTip() {
    26      return tip;
    27   }
    28   public void setTip(String tip) {
    29      this.tip = tip;
    30   }
    31   
    32   @Override
    33   public String execute() throws Exception {
    34      boolean validUser = VALID_USER.equals(getUsername());
    35      boolean validPwd = VALID_PWD.equals(getPassword());
    36      
    37      if (!validUser) {
    38        setTip("用户不存在!");
    39        return LOGIN;
    40      }
    41      
    42      if (!validPwd) {
    43        setTip("密码不正确!");
    44        return LOGIN;
    45      }
    46      
    47      setTip(null);
    48      return SUCCESS;
    49   }
    50  
    51 }
    LoginAction

    2.3.3.继承ActionSupport类

    com.opensymphony.xwork2.ActionSupport是一个复杂的类,它提供了很多其他的功能,而这些我们目前还不需要关注。所以这里只要把它当成Action接口的默认实现类就好了,这里也不再贴出其类图。
    demo的源代码:
     1 package cn.ljl.note.struts2.login.actions;
     2  
     3 import com.opensymphony.xwork2.ActionSupport;
     4  
     5 public class LoginActionSupport extends ActionSupport {
     6  
     7   private static final long serialVersionUID = 8451980703294866793L;
     8   
     9   private static final String VALID_USER = "admin";
    10   private static final String VALID_PWD = "admin";
    11   
    12   private String username;
    13   private String password;
    14   private String tip;
    15   
    16   public String getUsername() {
    17      return username;
    18   }
    19   public void setUsername(String username) {
    20      this.username = username;
    21   }
    22   public String getPassword() {
    23      return password;
    24   }
    25   public void setPassword(String password) {
    26      this.password = password;
    27   }
    28   public String getTip() {
    29      return tip;
    30   }
    31   public void setTip(String tip) {
    32      this.tip = tip;
    33   }
    34   
    35   @Override
    36   public String execute() throws Exception {
    37      boolean validUser = VALID_USER.equals(getUsername());
    38      boolean validPwd = VALID_PWD.equals(getPassword());
    39      
    40      if (!validUser) {
    41        setTip("用户不存在!");
    42        return LOGIN;
    43      }
    44      
    45      if (!validPwd) {
    46        setTip("密码不正确!");
    47        return LOGIN;
    48      }
    49      
    50      setTip(null);
    51      return SUCCESS;
    52   }
    53   
    54 }
    LoginActionSupport

    2.4.三种方式的比较

    比较上述三种方式的源代码,大部分代码都是重复的。实现Action接口或者继承ActionSupport类,可以直接使用已经定义好的逻辑结果,而这些一般是比较常用的。
    通过继承ActionSupport来开发Action,这是建议的方式。

    3.配置action


    3.1.配置文件

    Struts2的常规配置文件是struts.xml,这个文件放在源文件夹的根目录。比如使用maven,应该把它放在src/main/resources下。
    配置文件的结构像下面这样:
    1 <?xml version="1.0" encoding="GBK"?>
    2 <!DOCTYPE struts PUBLIC
    3     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    4     "http://struts.apache.org/dtds/struts-2.3.dtd">
    5 <struts>
    6     
    7 </struts>
    其中,文档声明中的版本号与当前使用的struts2的版本有关。也可以从struts2-core相关jar包中拷贝。
    下面将要介绍的内容,基本是在这里配置的,否则会特别说明。另外,如果你修改了这里的配置,编译部署之后,需要重启服务器。

    3.2.命名空间

    一次请求的地址,像这个样子:

    http://localhost:8080/note-struts2/login/check.action
    在Struts2看来,/login称为命名空间;check称为action-name;.action是后缀,在解析的时候会自动去掉。
    命名空间可以多级,就像目录结构一样,一层层;action-name就像是文件名。
    在struts.xml中,使用<package>的namespace属性来指定命名空间,所以action的定义都是在<package>下定义的。比如,我们可以这样定义:
    <package name="login" extends="struts-default" namespace="/login">
      ......
    </package>
    上述代码片段,涉及到3个package属性:
    • name
      name唯一标识一个package
    • extends
      package有继承的特性,使用extends指定另一个package的name,就会继承彼package下所定义的内容。
      这里继承的struts-default是在struts2-core的struts-default.xml中定义的。通常,建议继承struts-default。
    • namespace
      配置命名空间。

    默认的命名空间

    namespace不是必需的属性,如果没有配置,那就是默认的命名空间。默认的命名空间有特殊的作用:如果在请求的URL中解析出来的命名空间里找不到对应的action,就到默认的命名空间里找。默认命名空间是一个抽象的概念,不是默认值,你不能说“我可以通过配置namespace等于默认值,来指定默认命名空间”。比如"/"被称为根命名空间,但是它不是默认命名空间,它也没有任何特殊的性质,就和其他命名空间一样。

    3.3.action

    即配置action映射,struts框架需要查找这个映射,才能根据URL找到实际的处理Action。action是在<package>元素内配置的,下面是一个demo:

    <action name="check" class="cn.ljl.note.struts2.login.actions.LoginActionSupport" method="execute">
        ......
    </action>
    <action>元素有3个基本的属性:
    • name
      它同时也是action的url请求地址的一部分,同一个命名空间下,action的name要唯一
    • class
      它是Action类,负责处理用户的请求。这是一个非必需的属性,默认为com.opensymphony.xwork2.ActionSupport;你可以看下这个类的execute方法,只是直接返回SUCCESS。
    • method
      它是Action的方法名,对于框架来说,相当于回调方法。框架会调用这个方法,以达到通知请求到达的效果。这是一个非必需的属性,默认值为execute,所以上面的demo完全不用配置这个属性。

    3.4.result

    即配置result映射,根据这个映射,struts框架才能根据Action返回的逻辑结果(字符串)找到对应的视图资源。result是在<action>元素内配置的,下面是一个demo:

    <result name="success">/index.jsp</result>
    <result name="login">/login/login.jsp</result>

    <result>元素的属性name代表Action返回的逻辑结果;<result>体的内容,代表物理视图的路径。其中name的默认值是"success",所以第1行不用配置name属性。

    3.5.异常

    在Action中出现异常,可能希望根据不同的异常类型跳转到不同的物理视图。
    结合Struts2框架的工作流程,我们可以Action中捕获异常,根据不同的类型返回不同的字符串,并在struts.xml中根据这些返回的结果配置不同的物理视图。比如我们可能会这样写Action的方法:
    1 public String execute() {
    2     try {
    3         // ...
    4     } catch(异常1 e1) {
    5         return 结果1;
    6     } catch(异常2 e2) {
    7         return 结果2;
    8     }
    9 }

    然后我们会在struts.xml中这样配置result映射:

    <result name="结果1">视图1.jsp</result>
    <result name="结果2">视图2.jsp</result>

    这样是可以的,实际上在我们已知的知识上,想到这种方式来解决新的问题,能体现我们是会灵活变通的。不过Struts也提供了正统的配置方法,让我们只需要配置异常和返回结果的映射关系,而不需要捕获Action处理方法中抛出的异常。

    3.5.1.异常映射

    在<action>元素下,使用<exception-mapping>元素来配置,下面上一个demo:

    <result name="exception">/exception/exception.jsp</result>
    <exception-mapping result="exception" exception="java.lang.Exception" />
    这个demo的意思是,如果处理方法抛出了java.lang.Exception异常,就返回"exception"的逻辑结果,它对应的物理视图是/exception/exception.jsp。
    <exception>有两个属性:
    • result
      对应的异常类型发生时,要返回什么逻辑结果
    • exception
      配置什么类型的异常
    留两个有趣的问题:
    1. 配置的异常类型,对其子类型异常是否有效?
    2. 两种异常类型(继承关系)配置的先后顺序,对异常抛出的返回结果是否有影响?
      抛出的异常可以是父类型、子类型、两者的子类型。
    这两个是比较细节、相对啰嗦的问题,我不喜欢这样的问题,实际遇到的时候,我会顺手测一下,但是目前,我还不关心它们的答案。

    3.5.2.拦截器

    我们虽然提供了异常到逻辑结果的映射,但是还需要一个拦截器来做这样的工作:拦截抛出的异常,查映射关系,改为返回对应的逻辑结果。这样的拦截器已经在struts-default中使用了,所以只需要保证定义的<package>,直接或间接的继承了struts-default就好了

    3.5.3.输出异常

    我们可以在jsp中使用el来输出异常,像这样${exception }。不过,Struts2也提供了相关的标签,下面是一个demo:

    1 <%@ page language="java" contentType="text/html; charset=GBK"
    2     pageEncoding="GBK"%>
    3 <%@ taglib uri="/struts-tags" prefix="s" %>
    4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    5 <html>
    6 <body>
    7     <s:property value="exceptionStack"/>
    8 </body>
    9 </html>
    line 3,引入了struts的标签库,定义了前缀为s。
    line 7,使用<property>标签,输出了异常的堆栈信息。
    <property>标签在用于输出异常信息是,value属性有下面几个选择:
    • exception
      输出异常本身,相当于${exception }
    • exception.message
      输出异常的信息,相当于${exception.message }
    • exceptionStack
      输出异常的堆栈信息,正是demo中所用到的。

    3.6.总结

    好了,让之前出现过的几位也上来吧,我们来张合照:struts.xml

     1 <?xml version="1.0" encoding="GBK"?>
     2 <!DOCTYPE struts PUBLIC
     3     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
     4     "http://struts.apache.org/dtds/struts-2.3.dtd">
     5 <struts>
     6     <package name="login" extends="struts-default" namespace="/login">
     7         <action name="check" class="cn.ljl.note.struts2.login.actions.LoginActionSupport">
     8             <result name="success">/index.jsp</result>
     9             <result name="login">/login/login.jsp</result>
    10             <result name="exception">/exception/exception.jsp</result>
    11             <exception-mapping result="exception" exception="java.lang.Exception" />
    12         </action>
    13     </package>
    14 </struts>

    4.使用config-browser查看配置


    你可能想浏览配置的情况,当然对于一个简单的项目,直接看struts.xml可能更快、更专业。接下来要出场的是一个插件,config-browser,它可以提供通过前台查看配置情况的功能。这不是它唯一的优点,但是我们不需要为其优点列一个清单,暂时知道这一个吧,剩下的自己体会。

    4.1.添加依赖

    我们使用的struts2-core是2.3.28版本,我们也使用相同版本的config-browser:

    <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-config-browser-plugin</artifactId>
        <version>2.3.28</version>
    </dependency>

    4.2.使用

    重新打包、部署,重启服务,输入访问地址:http://localhost:8080/note-struts2/config-browser/actionNames.action,可以看到下面的内容:

    注意左侧导航栏,Namespaces下是所有的命名空间,其中/config-browser是插件定义的,/login是我们之前定义的。右侧默认显示默认命名空间下的action。

    点击左侧Namespaces/login链接,可以看到:

    右侧列出了这个命名空间下的action,点进去会看到:

    注意:这里的内容是按照标签页组织的,默认显示的是Results标签页,切换到Eception Mappings,可以看到:

    我们关于config-browser入门的介绍就到这里了,其他的靠大家自己了。

    5.扩展


    前面已经对框架的基本流程中涉及到的工作,做了基本的介绍,下面这些内容会深入一下。

    5.1.action

    5.1.1.多个处理方法

    一个Action类中可以有多个处理方法,只需要在配置<action>的时候使用method指定不同的方法名,就可以定义多个action。比如,我们假想一个Action,它的类图是这样的:

    我们可以在struts.xml中这样配置:

     1 <package name="imagination" extends="struts-default" namespace="/imagination">
     2     <action name="addUser" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="add">
     3         ...
     4     </action>
     5     <action name="saveUser" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="save">
     6         ...
     7     </action>
     8     <action name="deleteUser" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="delete">
     9         ...
    10     </action>
    11 </package>

    5.1.2.使用通配符

    在满足一定的模式的情况下,使用通配符可以使用最少的配置量,配置多个action。比如对于上面的情况,也可以使用下面的配置:

    1 <package name="imagination" extends="struts-default" namespace="/imagination">
    2     <action name="*User" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="{1}">
    3         ...
    4     </action>
    5 </package>

    line 2,name属性值中使用了*,method属性值中的{1}表示使用第1个*所匹配的字符串。这个配置和上文的配置效果是一样的,但是配置工作更少。

    class也可以和name满足一定的模式,比如下面的配置:

    1 <package name="imagination" extends="struts-default" namespace="/imagination">
    2     <action name="*User" class="cn.ljl.note.struts.imaginary.actions.{1}UserAction">
    3         ...
    4     </action>
    5 </package>
    此时,AddUser将映射到cn.ljl.note.struts.imaginary.actions.AddUserAction。
    其实,上述两种配置,体现了两种思想:将不同的业务交给Action的不同的处理方法,或者干脆使用不同的Action。
     
    优先级
    使用通配符会存在这样的问题:请求的地址为addUser.action,struts.xml中同时有addUser、*User、*可以匹配这个请求,会选择哪一个来处理请求呢?
    如果我们称不使用通配符的为完全匹配,称使用通配符的为模式匹配,那么有下面两个规则:
    • 完全匹配优先于模式匹配
    • 模式匹配中,前面的优先
    所以,会选择addUser。如果没有这个配置,则在*User、*中,哪一个出现在struts.xml的前面,哪一个被选择。
    所以,建议把范围更广的配置放在后面。

    5.1.3.默认的action

    *可以匹配一切,所以我们可以使用*来配置默认的action。另外,我们还可以像下面这样配置:

    1 <package name="imagination" extends="struts-default" namespace="/imagination">
    2     <default-action-ref name="default" />
    3     <action name="default" class="...">
    4         ...
    5     </action>
    6     ...
    7 </package>

    在特定命名空间下配置,只能作为当前命名空间的默认action;在默认的命名空间下配置,可以作为全局的默认action。

    5.1.4.默认的class

    配置<action>时,默认的class是com.opensymphony.xwork2.ActionSupport,你可以修改这项配置:在<package>下添加<default-class-ref class="" />。

    5.1.5.动态方法调用

    在form标签的action属性,可以同时指定action的name和方法,比如:

    <form action="user!add">
        ...
    </form>

    就指定name为user的action的add方法,来处理请求。

    这种方式,前台依赖于服务端的API,这样是不好的。

    5.2.result

    Struts2支持多种result-type,基本的result-type都是在struts2-core的struts-default.xml中配置的。

    5.2.1.多种结果类型

    1.dispatcher

    disparcher是默认的result-type,以指定的jsp作为视图。最原始的配置方式是这样的:

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

    因为result的name默认就是success,type默认就是dispatcher,而视图的location可以直接在<result>的体配置。所以可以简化成这个样子:

    <result>/success.jsp</result>

    2.plainText

    plainText将指定的视图以文本的形式显示给浏览器。使用这种方式,需要指定视图的location;如果视图文件中包含非西欧字符,还要指定charSet。

    <result name="success" type="plainText">
        <param name="location">/success.jsp</param>
        <param name="charSet">GBK</param>
    </result>

    3.redirect

    重定向。这个类型可以指定一个location,浏览器重定向到指定的视图。

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

    4.redirectAction

    重定向到Action。这个类型专门重定向到Action,与redirect算是被包含关系。这个类型可以指定namespace和actionName:

    <result type="redirectAction">
        <param name="namespace">/login</param>
        <param name="actionName">check</param>
    </result>

    暂时,就介绍这几种吧。

    5.2.2.使用通配符

    在配置result映射的时候,也可以使用通配符,比如:

    <package name="imagination" extends="struts-default" namespace="/imagination">
        <action name="*User" class="cn.ljl.note.struts.imaginary.actions.UserAction" method="{1}">
            <result>/imagination/result-{1}.jsp</result>
        </action>
    </package>

    按照上面的配置,addUser对应cn.ljl.note.struts.imaginary.actions.UserAction的add方法,返回的视图是/imagination/result-add.jsp。

    5.2.3.使用OGNL表达式

    OGNL表达式的具体内容,计划放在后面讲,这里只介绍几个简单的用法。配置result映射时,可以使用action的属性值(要提供getter方法),比如:

    <result>/imagination/result-add.jsp?username=${username}</result>

    在计算物理视图时,就会使用action的username属性,替换其中的${username}。

    如果属性是复杂属性,比如bean,而在result中需要的是属性bean的属性,也可以按照这样的方式获取:${user.name}。

    5.2.4.全局配置

    在之前的配置里,都是针对action的一个逻辑结果进行配置;全局配置是在<package>范围,提供一个配置,对所有action都有效。

    全局配置是在<package>下,使用<global-results> - <result>来配置,比如:

    1 <package ...>
    2     <global-results>
    3         <result name="exception">/exception.jsp</result>
    4     </global-results>
    5     ...
    6 </package>

    这样一来,这个package下所有的action,如果没有指定"exception"的映射,就使用全局的映射;如果指定了,就覆盖全局的配置。

    5.3.异常的全局配置

    在<package>范围,异常也可以使用全局配置。使用<global-exception-mappings> - <exception-mapping>。异常映射是把异常的类型映射到result,所以它依赖于result,全局的异常映射应该只使用全局的result,像下面这样:

    1 <package ...>
    2     <global-results>
    3         <result name="exception">/exception.jsp</result>
    4     </global-results>
    5     <global-exception-mappings>
    6         <exception-mapping exception="java.lang.Exception" result="exception" />
    7     </global-exception-mappings>
    8     ...
    9 </package>
  • 相关阅读:
    python 结巴分词简介以及操作
    JWT(Json web token)简介
    为什么推荐前端使用Vue.js
    Vue 加载外部js文件
    Docker简介以及操作
    'QueryDict' object is not callable 错误解析
    django- Vue.js 操作
    django —— KindEditor
    websocket ----简介,以及demo
    python --商品评价---- 数据表结构以及理解
  • 原文地址:https://www.cnblogs.com/ywjy/p/5635635.html
Copyright © 2020-2023  润新知