Y · 2019年08月26日

SSH开发模式——Struts2(第三小节)

struts2框架的知识点,虽然分了几个小节,感觉内容还是挺多的,但是你仅仅是入门了而已,想要进一步地提升自己,你得有一颗持之以恒的学习的心,最后的内容我都将在这篇博客中讲到,所以篇幅可能会有点长,希望大家能够耐心阅读。

首先介绍一下struts2的配置。
还记得我们创建的test.jsp文件吗?直接看到这篇博客的同学们也不用去翻阅我之前的博客,因为我在之前的博客中只是简单地介绍了使用,而并没有深入,我并没有讲解每一步的作用,和为什么要这样写。所以,直接从该篇博客开始阅读学习也是可以的,我将从最基础的配置开始重新进行讲解,并讲述每一步的作用、原因。

那么test.jsp文件里的代码很简单,就是一个超链接。

<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="/struts-tags"   prefix="s"%>
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
    </head>
  <body>
       入门的路径:<br>  
      <a href="${pageContext.request.contextPath}/primer/helloWorldAction.action">helloWorld</a><br>
  </body>
</html>

那就介绍一下访问helloWorld应用的路径的设置。
在struts2中,访问struts2中action的URL路径由两部份组成:
包的命名空间+action的名称
例如: 访问本例子HelloWorldAction的URL路径为: /primer/helloWorldAction.action (注意:完整路径为:http://localhost:端口/内容路径/primer/helloWorldAction.action)。另外我们也可以加上.action后缀访问此Action。

 <package name="primer" namespace=“/primer“   extends="struts-default">
      <action name="helloWorldAction" class="cn.itcast.primer.HelloWorldAction">
        <result name="success" type="dispatcher">/success.jsp</result>
      </action>
 </package>

路径中的action名称必须和你在package标签下配置的action标签的name属性一致。

接下来我们在test.jsp文件中加入如下代码。

       测试Action名称的搜索顺序:<br>
        <a href="${pageContext.request.contextPath}/primer/primer/primer/helloWorldAction.action">helloWorld</a><br>
        <a href="${pageContext.request.contextPath}/primer/primer/helloWorldAction.action">helloWorld</a><br>
        <a href="${pageContext.request.contextPath}/primer/helloWorldAction.action">helloWorld</a><br>
 
     没有为action指定class<br>
        <a href="${pageContext.request.contextPath}/primer/actionNoClass.action">helloWorld</a><br>
       
      测试struts2 输出没有命名空间helloworld:<br>
        <a href="${pageContext.request.contextPath}/primer/userAction.action">helloWorld</a><br>

如果我们去点击这三个请求链接会发生什么呢?
你们试着点击一下就会发现,这三个请求链接都能被正确地处理运行。那我们就想,为什么没有配置却能够正确运行呢?这是因为在struts2中,当你点击请求链接时,它会对Action进行一个搜索。以第一个链接为例, <a href="${pageContext.request.contextPath}/primer/primer/primer/helloWorldAction.action">helloWorld</a>,当你点击链接时,会去搜索/primer/primer/primer/helloWorldAction.action,当查询不到时,会搜索/primer/primer/helloWorldAction.action,查询不到时,会搜索/primer/helloWorldAction.action,以此类推,直到Action被找到。所以如果我这么写, <a href="${pageContext.request.contextPath}/primer/primer/aaa/helloWorldAction.action">helloWorld</a>,程序就会报错。

那如果没有为action标签配置class属性,程序会有问题吗?
在package标签中添加一个action标签。

<action name="actionNoClass">
        <result name="success">/primer/success.jsp</result>
</action>

此时我们运行并点击,会发现程序仍然正确运行了,那问题来了,我连class都没配置,它是如何运行得到结果success,并跳转页面的呢?
我们查阅struts-default.xml文件,看到最后几行的配置

 <default-class-ref class="com.opensymphony.xwork2.ActionSupport" />

你明白了吗?在没有为action指定class的时候,struts2框架默认会执行该Action。而我们查阅ActionSupport类的源码时,找到了一个方法。

public static final String SUCCESS = "success";

public String execute() throws Exception {
        return SUCCESS;
}

这个方法再熟悉不过了吧,所以为什么没有配置class属性也能正常运行,相信你已经明白了。

如果请求的路径查找不到action的情况下,程序运行会抛出异常 ,可以通过配置当找不到action的情况下,会执行默认的action

<package name="primer"  namespace="/"  extends="struts-default">
     <!--指定默认的action引用,如果该包下没有对应action配置,则启用该配置-->
     <default-action-ref name="helloWorldAction"></default-action-ref>

     <action name="helloWorldAction" class="cn.itcast.primer.HelloWorldAction">
            <result name="success" type="dispatcher">/success.jsp</result>
      </action>
     <action name="actionNoClass">
          <result>/success.jsp</result>
     </action>
 </package>

接下来我们来了解一下ActionSupport类,它是我们没有指定class执行的默认Action。
它远比Action接口强大,所以我们在编写Action类时可以改为继承ActionSupport类。

相信很多同学在开始的时候对这个.action的后缀表示无法理解,为什么写.action?我们来了解一下。
StrutsPrepareAndExecuteFilter是Struts 2框架的核心控制器,它负责拦截由<url-pattern>/*</url-pattern>指定的所有用户请求,当用户请求到达时,该Filter会过滤用户的请求。默认情况下,如果用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入Struts 2框架处理,否则Struts 2框架将略过该请求的处理。

根据配置文件:struts2-core-2.1.8.1.jar包下的 org.apache.struts2/default.properties文件定义的常量决定

  struts.action.extension=action,,

默认处理的后缀是可以通过常量”struts.action.extension“进行修改的,如下面配置Struts 2只处理以.do为后缀的请求路径:

<struts>
    <constant name="struts.action.extension" value="do"/>
</struts>

如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。如:

 <constant name="struts.action.extension" value="do,go"/>

我们还有第二种方式来修改后缀。
在struts.properties中配置常量, (struts.properties文件放置在src下)
struts.action.extension=do.go
既然有两种方式,那么如果我两种方式都实现了,它会启用哪个配置呢?我就不卖关子了,如果你两种方式都实现了,struts2框架会启用struts.properties的配置。那这是为什么呢?我可以给你解释一下。
因为常量可以在多个配置文件中进行定义,所以我们需要了解下struts2加载常量的搜索顺序:
1 struts-default.xml
2 struts-plugin.xml
3 struts.xml
4 struts.properties(自己创建)
5 web.xml
如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值。
通俗点说,越后执行的文件优先级越高。
虽然实现方式有两种,但是不建议大家使用第二种自己创建配置文件的方式。

然后是常用的常量介绍。
指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出

<constant name="struts.i18n.encoding" value="UTF-8"/>

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

<constant name="struts.action.extension" value="do"/>

设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭

<constant name="struts.serve.static.browserCache" value="false"/>

配置当国际化文件修改时,重新加载该国际化资源文件,默认值是false(不重新加载),true为重新加载

<constant name="struts.i18n.reload" value="true"/>

当struts的配置文件修改后,系统是否自动重新加载该文件,默认值是false(不重新加载),true为重新加载

<constant name="struts.configuration.xml.reload" value="true"/>

开发模式下使用,这样可以打印出更详细的错误信息,默认值为false(生产环境下使用),开发阶段最好打开

<constant name="struts.devMode" value="true" />

默认的视图主题

<constant name="struts.ui.theme" value="simple" />

与spring集成时,指定由spring负责action对象的创建

<constant name="struts.objectFactory" value="spring" />

该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性
为 false

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

上传文件的大小限制

 <constant name="struts.multipart.maxSize" value=“10701096"/>

接下来是第二个部分,struts2的结果类型。
我们新建文件夹resulttype,然后创建form.jsp文件

<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="/struts-tags"   prefix="s"%>
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
    </head>
  <body>
    <form action="${pageContext.request.contextPath}/resulttype/resulttypeAction.action" 
          name="form1"  method="post">
      <input type="submit" value="提交">
    </form>
  </body>
</html>

然后我们创建出对应的Action,这次我们不实现Action接口了,而是继承ActionSupport类。

public class ResultTypeAction extends ActionSupport {
    @Override
    public String execute() throws Exception {
        System.out.println("ResultTypeAction ...");
        
        HttpServletRequest request = ServletActionContext.getRequest();
        request.setAttribute("username", "username_request");
        
        return "success";
    }
}

对其进行配置,在struts.xml文件中添加

<package name="resulttype" namespace="/resulttype" extends="struts-default">
        <action name="resulttypeAction" class="cn.itcast.action.ResultTypeAction">
            <result name="success">/resulttype/success.jsp</result>
        </action>
</package>

转向的success.jsp页面如下

<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="/struts-tags"   prefix="s"%>
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
    </head>
  <body>
            resulttype :   ${requestScope.username}
  </body>
</html>

点击提交按钮,页面成功显示保存在request范围的数据,这说明了什么?说明我的转向方式一定是转发,如果是重定向的话数据就取不到了。其实,在result标签里有一个type属性,它可以用来设置转向方式,只不过它默认是"dispatcher"。
这是第一种设置转向的方式,我们也有第二种。

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

这样配置result标签也能实现转向。这个location指的是要转发到的页面。这种方式的实现原理在struts-default.xml文件中也能被找到,我就不带大家看了,感兴趣的同学可以自己研究一下。了解了转发,那重定向如何实现?这太简单了,将type值改为"redirect"即可。
注意了,重定向中,转向到jsp才能这样写,而如果想转向到action,你需要这样配置:

<result name="success" type="redirectAction">
    <param name="actionName">helloWorldAction</param>
    <param name="namespace">/primer</param>
</result>

最后一个部分就是struts2通配符与动态方法的调用。
在说这个之前先来拓展一个知识点。
我们先新建一个test.jsp文件。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <a href="${pageContext.request.contextPath }/bookAction.action">提交</a>
</body>
</html>

里面只有一个超链接,然后配置struts.xml文件,相信配置已经难不倒各位了,那我就对action作如下配置。

<action name="bookAction" class="cn.itcast.action.BookAction">
    <result name="success">/success.jsp</result>
    <result name="add">/bookaction.jsp</result>
</action>

然后创建BookAction.java文件。

public class BookAction extends ActionSupport{
    @Override
    public String execute() throws Exception {
        System.out.println("BookAction ...");
        return "success";
    }
    
    public String add(){
        System.out.println("add ...");
        return "add";
    }
}

我们运行项目,然后点击提交,页面会转向success.jsp文件,那么我如果想点击页面从而跳转到我们配置的bookaction.jsp文件呢?其实很简单,在action标签中有一个method属性,只需将它的值设置为方法名即可。那这个自定义的方法是不是可以随便编写呢?当然是不行的。它必须由public关键字修饰,必须有返回值,该方法不能传参,总结来说,就是除了方法名和execute方法一样,其它的都必须和它一样。到这里有人就会说了,你这句话有问题,execute方法抛出了异常,而自定义的方法却没有抛出,我解释一下,这是因为我们写的测试代码过于简单,以至于没有异常可供处理,所以你可以不抛异常,但是当代码很复杂的时候,程序或多或少都会有异常,这时候就必须抛出异常了。

这部分就先这样,回到我们的重点,通配符。
我们在test.jsp文件中添加三个超链接。

    通配符映射示例(1):<br>
    <a href="${pageContext.request.contextPath}/a_add.action"> 通配符映射示例(1)</a><br>
    <a href="${pageContext.request.contextPath}/b_add.action"> 通配符映射示例(1)</a><br>
    <a href="${pageContext.request.contextPath}/c_add.action"> 通配符映射示例(1)</a><br>
    <br> 
    <br>
    <br>

然后配置struts.xml。

<action name="a_add" class="cn.itcast.action.BookAction" method="add">
    <result name="success">/success.jsp</result>
    <result name="add">/bookaction.jsp</result>
</action>
<action name="b_add" class="cn.itcast.action.BookAction" method="add">
    <result name="success">/success.jsp</result>
    <result name="add">/bookaction.jsp</result>
</action>
<action name="c_add" class="cn.itcast.action.BookAction" method="add">
    <result name="success">/success.jsp</result>
    <result name="add">/bookaction.jsp</result>
</action>

运行程序,程序正常运行,但是这样的配置显然很麻烦,有没有什么简单一点的写法呢?这时候通配符就派上用场了,我们把之前的配置都删掉,然后编写如下配置。

<action name="*_add" class="cn.itcast.action.BookAction" method="add">
    <result name="success">/success.jsp</result>
    <result name="add">/bookaction.jsp</result>
</action>

运行项目是没有问题的。这个*号就是通配符,有些语言基础的同学应该都了解,我就不多说了。
下面给出struts2通配符的知识点。
一个 Web 应用可能有成百上千个 action 声明. 可以利用 struts 提供的通配符映射机制把多个彼此相似的映射关系简化为一个映射关系
通配符映射规则
若找到多个匹配, 没有通配符的那个将胜出
若指定的动作不存在, Struts 将会尝试把这个 URI 与任何一个包含着通配符 * 的动作名及进行匹配
若 Struts 找到的带有通配符的匹配不止一个, 最后一个匹配将胜出
被通配符匹配到的 URI 字符串的子串可以用 {1}, {2} 来引用. {1} 匹配第一个子串, {2} 匹配第二个子串…{0} 匹配整个 URI

  • 可以匹配零个或多个字符, 但不包括 / 字符. 如果想把 / 字符包括在内, 需要使用 **. 如果需要对某个字符进行转义, 需要使用 \。

最后就是动态方法调用了。
动态方法调用: 通过 url 动态调用 Action 中的方法
如果Action中存在多个方法时,我们可以使用!+方法名调用指定方法
默认情况下, Struts 的动态方法调用处于激活状态, 若想禁用该功能, 则可以在 struts.xml 文件中添加如下 constant 元素:
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
以上面的项目为例,我可以不再action中配置method,而在超链接的url地址中作如下修改:

<a href="${pageContext.request.contextPath }/bookAction!add.action">提交</a>

这样我也能在点击提交按钮后调用配置action中的add方法。这就是动态方法调用。

到这里,struts2框架的学习就结束了,当然,struts2框架的知识远不止如此,感兴趣的同学可以继续深入学习,我这里就先告一段落了。

推荐阅读
关注数
0
文章数
55
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息