struts2从认识到细化了解

时间:2023-03-09 08:59:33
struts2从认识到细化了解

目录

首发日期:2018-08-15

修改日期:

  1. 2018-09-08:发现序号与我使用的markdown文本编辑器不同,所以把markdown序号改成了纯文字序号。
  2. 2018-10-26:重新整理了一下文字。

Struts2的介绍与执行流程

介绍:

  • Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet。如果你了解过servlet,那么你应该知道servlet的开发步骤:1.新建一个servlet;2.在web.xml中配置servlet,使得请求路径url-pattern能够与servlet对应。事实上,struts2的步骤与servlet步骤差不多。
  • 如果将struts2按mvc来拆分,那么模型对应struts2中的action,控制器对应struts2中的StrutsPrepareAndExecuteFilter(旧版的是filterdispatcher),视图对应struts2中的result

执行流程:

  • 如果你了解过mvc,那么从上面的“将struts2按mvc”拆分,你应该可以得出一个粗略的执行流程:视图负责与用户进行交互->控制器接收到请求->请求交给模型来处理->请求处理完毕,模型返回结果->控制器根据结果,让视图进行跳转。
  • 这里我就懒得写了,你可以参考一下这个:https://www.cnblogs.com/sunTin/p/6682041.html
  • 从流经的核心组件来看:
    • struts2从认识到细化了解

运行环境搭建

1.在官网下载struts2:https://struts.apache.org/

我使用的版本是2.3.34







2.解压下载到的压缩文件,可以看到几个比较重要的文件夹和其他的文件

  • 重要文件夹:
    • apps:存放着struts2的实例程序,都是一些war文件,war文件直接放入到tomcat中就可以运行。
    • docs:存放着struts2的文档
    • lib:存放着struts2的核心类库、第三方插件类库
    • src:存放着struts2的全部源代码







      3.struts2基本运行所需要的依赖包(13个):
  • asm-3.3.jar
  • asm-commons-3.3.jar
  • asm-tree-3.3.jar
  • commons-fileupload-1.3.1.jar:Struts2文件上传组件依赖包
  • commons-io-2.2.jar:包含处理IO的一些工具类
  • commons-lang3-3.2.jar
  • freemarker-2.3.22.jar:Struts2标签模板使用的类库
  • javassist-3.11.0.GA.jar:javassist的类库,javassist可以用于反射和代理对象。
  • log4j-api-2.2.jar:包含了Struts2的日志管理组件依赖包的API
  • log4j-core-2.2.jar:Struts2的日志管理组件依赖包
  • ognl-3.0.6.jar:OGNL表达式语言的依赖包
  • struts2-core-2.3.24.jar:struts2的核心包
  • xwork-core-2.3.24.jar:WebWork核心库 ,struts2的运行依赖于webwork







    上面的13个依赖包你可以慢慢地一个个找出来,你也可以利用struts2提供的基础示例工程来获取(一个基础的工程肯定已经搭建好了环境),在apps文件夹中,有一个struts2-blank.war,它是一个已经搭建好了环境的空白工程,我们可以右键用压缩文件打开,在struts2-blank.war\WEB-INF\lib下就有我们所需要的13个依赖包。

    【如果你会maven,那么也可以使用maven来搭建struts2的环境】

【这里提醒一下,log4j的两个包在struts-2.3.34\lib中是找不到的,你可以在struts2-blank.war找到它,或者自己下载】

通常来说,这13个依赖包就足够我们的struts2的基本使用了。如果功能不足够,我们可以后续再从lib中导入需要的依赖包。


基础示例

下面的示例基于eclipse。

1.新建web工程

2.导入依赖包

struts2从认识到细化了解





3.创建一个Action类。(这是用来处理请求的,是MVC中的Model。这里遵循了一种规范,使得它能够处理struts转发过来的请求,这些规范将在后面讨论。)

package work.action1;

public class HelloAction {
public String execute() {
System.out.println("我接收到你的请求了");
return "success";//这里返回的结果影响视图的显示
}
}

4.在src目录下创建struts.xml,在struts.xml中对Action进行配置,这里配置的是请求转发信息和请求结果视图。action标签配置了请求转发信息,这样struts2就知道哪个请求交给哪个程序处理了;result标签配置了请求结果视图,struts2就知道请求结束后跳转到哪个视图了。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="demo1" extends="struts-default" namespace="/">
<!--下面指明了,当发起的请求为“hello.action”的时候,给HelloAction来处理。 -->
<action name="hello" class="work.action1.HelloAction">
<!--下面指明了HelloAction中返回值为success时,跳转到哪个视图 -->
<result name="success">/index.html</result>
</action>
</package>
</struts>

5.在web.xml中配置struts2的核心过滤器,struts2的运行依赖于过滤器,核心过滤器相当于它的前端控制器,有了核心过滤器struts2才能够将请求转发给Action。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>MystrutsDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

6.部署工程到服务器上


7.访问`http://localhost:8080/MystrutsDemo/hello.action`,结果是控制台打出了“我接收到你的请求了”,页面也跳转到index.html中了。【注意这里访问的是hello.action】


Action类的编写

介绍:

  • action在struts2中是mvc中的model,负责处理业务逻辑。
  • XXX.action处理请求实际上是交给action中的方法来处理的。【请求交给哪个action是可以配置的,需要在struts.xml中配置,下一节会讲到。】
  • 在struts.xml中还可以可以配置结果视图页面,action类中的方法返回的结果如果与struts.xml中action下的result匹配,那么处理完请求后会跳转到result指定的结果视图。

action的编写方式:

  • 方式1:POJO类的方式:创建一个简单类(懵懂初学的你可以先认作是只有目标方法的类),创建自定义方法,方法的格式要求是public String 方法名(),这里方法名是什么不重要,请求过来调用什么方法是可以配置的(这个在struts.xml中配置,后面会讲)。如果你不想配置调用什么方法的话,那么要求方法名要求是execute。请求的处理默认就是调用这个方法处理的
package work.action1;

public class HelloAction {
public String execute() {
System.out.println("我接收到你的请求了");
return "success";//这里返回的结果影响视图的显示
//如果你不想进行页面跳转,你可以return null;
}
}
  • 方式2:定义一个类,实现Action接口;实现Action接口需要实现execute方法【因为它是默认的请求处理方法所以需要进行实现,如果你不需要它,那么你可以空实现,然后定义其它你想要的方法,但格式要是public String 方法名(),然后在struts.xml中配置哪些请求交给哪个action处理】
    • 【Action接口给我们提供了五个常量:SUCCESS成功("success"),ERROR错误("error"),LOGIN登录出错的跳转("login"),INPUT表单校验出错的跳转("input"),NONE不跳转("null")。我们可以“考虑”用这些常量作为我们的返回值,使用常量能帮助我们指明某个视图的意义,但也可以不使用。】
package work.action1;
import com.opensymphony.xwork2.Action;
public class ActionDemo2 implements Action {
@Override
public String execute() throws Exception {
System.out.println("你的请求交给了ActionDemo2来处理了。");
return null;
}
}
  • 方式3:继承ActionSupport,execute由于是默认的请求方法,所以你可能需要进行重写。但如果你每个请求都有指定的方法来处理,那么你只需要定义那些需要的方法即可。【继承ActionSupport是最常用的,ActionSupport实现了Action接口和继承了一些常用类,它能够提供数据校验、错误消息处理等功能】【ActionSupport实现了Action接口,所以它也有上面所说的五个变量】
package work.action1;

import com.opensymphony.xwork2.ActionSupport;

public class ActionDemo3 extends ActionSupport {
@Override
public String execute() {
System.out.println("你的请求交给了ActionDemo2来处理了");
return null;
}
public String findAll() {
System.out.println("给findAll.action指定调用findAll来处理");
return null;
}
//findAll需要这样配置<action name="findAll" class="work.action1.ActionDemo3" method="findAll"></action>
}

上面谈完action的编写之后,那么就应该谈到业务逻辑的处理了,业务逻辑通常有几个核心:接受数据、处理数据、返回数据。其中处理数据由于大部分是数据库内容或者可自定义内容,与struts没有强烈的关联,所以这里不讲。而接受数据与返回数据,在以前的servlet学习中都是使用数据域来存储和获取数据的。而struts2虽然高级于servlet,但也脱不开servlet,下面将讲述struts2中servlet API的使用。也正是由于struts2高级于servlet,所以数据接收和返回数据的手段也比servlet多,而这一部分将留到数据封装和数据回显中讲述。

访问servlet API

  • 间接访问servlet API:通过ActionContext来获取对象中的数据,这样只能获取数据,不能获取实际的对象。
package work.action1;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport; public class ActionDemo4 extends ActionSupport {
@Override
public String execute() throws Exception {
ActionContext context = ActionContext.getContext();
//读
Map<String, Object> requestMap = context.getParameters();//获取request中的参数
Map<String, Object> contextMap = context.getContextMap();//获取context中的参数
Map<String, Object> applicationMap = context.getApplication();//获取application中的参数
Map<String, Object> sessionMap = context.getSession();//获取session中的参数
//在上面的几个map中,key为属性名,value为属性值
//写
context.put("reqKey", "reqValue");//向request中写入数据
context.getSession().put("sessionKey", "sessionValue");//向session中写入数据
context.getContextMap().put("contextKey", "contextValue");//向context中写入数据
context.getApplication().put("appKey", "appValue");//向application中写入数据
//还有context.setXXX()方法可以向几个数据域写数据,不过参数是Map
return NONE;//不跳转
}
}
  • 直接访问servlet API:

    • 利用ServletActionContext来直接获取request、session、context等对象
      package work.action1;

      import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport; public class ActionDemo5 extends ActionSupport {
@Override
public String execute() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ServletContext servletContext = ServletActionContext.getServletContext();
//获取到对象之后,你就可以使用在servlet中学到的操作来操作了
return NONE;//不跳转
}
}
  • 实现对应接口(比如获取request需要实现ServletRequestAware,基本都是ServletXXXXXAware)。实现对应接口需要实现对应的setXXX方法,方法有我们需要的对象的形参,可以直接把形参赋值给成员变量。
      package work.action1;

      import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.interceptor.ServletRequestAware;
import com.opensymphony.xwork2.ActionSupport; public class ActionDemo6 extends ActionSupport implements ServletRequestAware {
private HttpServletRequest request;
@Override
public void setServletRequest(HttpServletRequest request) {
this.request=request;//把形参赋值给成员变量
}
@Override
public String execute() throws Exception {
request.setAttribute("name", "neo");
return NONE;
}
}

补充:

  • action是多例的,每一次请求都会创建一次action。
  • action编写完毕后,需要对action与请求路径进行对应,这样才能把,这需要在struts.xml中配置,这将在下面的配置文件中讲解。

配置文件

从基础示例中,我们可以看到两个配置文件的出现:web.xml和struts.xml,struts2的运行需要配置文件的帮助,下面我将介绍struts2中的配置文件。

常见配置文件:

  • web.xml:这个web.xml是我们之前的servlet中认识的web.xml,它在struts2中的主要作用是配置核心过滤器,因为struts2的运行依赖于核心过滤器。

    • <filter>
      <filter-name>struts2</filter-name>
      <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
      </filter>
      <filter-mapping>
      <filter-name>struts2</filter-name>
      <url-pattern>/*</url-pattern>
      </filter-mapping>
      </web-app>
  • struts.xml:struts.xml是我们自行定义的配置文件(struts2会自动读取,struts.xml可以存放到src目录下,必须是classpath包含的路径),struts.xml主要用来配置action和result,用来使业务请求跟处理业务请求的业务逻辑处理建立上关系。

    <struts>
<package name="demo2" extends="struts-default" namespace="/">
<action name="actionDemo21" class="work.action2.ActionDemo21" >
<!-- 局部视图结果视图 -->
<result name="saveUI">/add.jsp</result>
</action>
</package>
</struts>
  • struts-default.xml:struts-default.xml是struts2自带的配置文件,它里面有很多预定义的常量、预定义的组件配置和拦截器等内容。(自带的意味不能修改)在struts2的使用中,struts-default.xml中有一个struts-default包,我们通常用它来作为struts.xml的包中的父包,使子包继承了父包中的常量、拦截器等配置,省去了很多的人工配置。

    • <struts>
      <package name="demo2" extends="struts-default" namespace="/">
      <!-- 在struts.xml中,上面的extends中的struts-default实际上就是struts-default.xml中的struts-default包 -->
      </package>
      </struts>
  • default.properties:default.properties是struts2自带的配置文件,里面有很多预定义的常量,这些常量定义了很多struts2的配置信息,比如“请求以什么字符编码(默认是utf-8)”、“默认请求扩展名(默认是.action或者空)”。

  • struts.properties:struts.properties是我们自行定义的配置文件(struts2会自动读取,struts.properties可以存放到src目录下,必须是classpath包含的路径),主要用来配置属性,由于default.properties中定义的常量不能修改,我们可以在struts.properties中对已定义的常量的值进行覆盖。struts.properties中常量生效的优先级要高于default.properties中的。

配置文件的加载顺序:

  1. default.properties
  2. struts-default.xml
  3. struts-plugin.xml
  4. struts.xml
  5. struts.properties
  6. web.xml

常量的配置:

  • 在上面的default.properties和struts.properties提到了常量的配置,这些常量影响struts2就好比线程池的配置文件影响线程池。struts2在default.properties中默认定义了很多常量,不然让程序员一个个变量配置过去就不知道猴年马月了。

  • default.properties是struts2是所有的常量配置信息的根源,它给所有struts2中定义的常量都赋予了值。所以我们需要配置常量的时候,都可以参考default.properties。

  • 在default.properties中有默认的常量,如果我们想要修改常量,我们需要利用struts.xml 、web.xml和struts.properties

    • struts.xml中的写法:<constant name="配置名" value="配置值"></constant>
   <struts>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<constant name="struts.action.extension" value="action"></constant>
<package name="demo1" extends="struts-default" namespace="/">
<!--内容省去,请关注上面的常量定义的配置方法-->
</package>
</struts>
  • struts.properties中的写法

    • struts.enable.DynamicMethodInvocation=true
      struts.action.extension=action
  • web.xml中的写法:使常量作为核心过滤器的初始化参数。

    • <filter>
      <filter-name>struts2</filter-name>
      <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
      <init-param>
      <param-name>struts.action.extension</param-name>
      <param-value>action</param-value>
      </init-param>
      </filter>
      <!-- 省去下面的内容 -->

  • 常见常量配置:

    • struts.action.extension :指定struts2处理的请求的后缀,即符合后缀才会交给struts2处理,否则404。默认是“action,,”即要求请求为xxx.action或xxx
    • struts.enable.DynamicMethodInvocation :指定是否开启动态方法访问
  • 配置生效优先级:后配置的常量会覆盖前面定义过的常量,下面的常量配置优先级下面的高于上面的。

    • default.properties
    • struts-default.xml
    • struts-plugin.xml
    • struts.xml
    • struts.properties
    • web.xml

struts.xml配置:

struts.xml需要xml约束,这里给一下:

<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
  • package标签:主要用来方便管理多个action,没有特殊意义,但要求在一个工程中package标签的name属性不能重复

    • 属性:
      • name:package的名称,没有特殊意义,只是方便更好的管理,就好像通常我们会给实体类的包命名成domain。
      • extends:指明这个package继承哪个包,通常值为struts-default,继承的意义是继承了别的包中定义好了的配置。struts-default(对应struts2-core-2.3.34.jar/struts-default.xml中的struts-default pageckage)为我们定义了很多配置,这方便了我们的使用。
      • namespace:名称空间,用来跟action中的name共同决定请求路径(namespace + name-->最终请求路径)
      • abstract :是否是抽象的包,是抽象的包的话就可以被别的包继承。
  • action标签:用来配置请求跟action的对应信息,配置哪个请求交给哪个action来处理(这个步骤英文名为workflow path)

    • 属性:
      • name:与namespace共同决定访问路径 (比如namespace="/",而name="hello"时,那么请求路径是localhost:8080/工程名/hello.action,这里有个后缀.action是struts默认的常量,是可以更改的。)
      • class:指定请求交给哪个action来处理,值为action类的全路径
      • method:指定业务请求处理执行action中的哪个方法,默认为execute。【这里有其他的配置方法,留到下面的Action的访问配置中讲】
      • converter:类型转换器。【这里不讲这个】
  • include标签:一个struct.xml中可以包含其他的struct.xml(这里名字一样只是代表功能一样,名字可以更改,比如下面的例子中,但最终的使用include的要是struct.xml),在struts标签下使用include标签就可以引入其他的struct.xml

    • include标签方便于模块化设计。(就好像java中一个包用来设计实体类,另一个包负责其他功能,它只需要导入实体类所在的包就能使用到实体类)
    • <include file="work/action2/struts_demo2.xml"></include>

给一个参考示例:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.action.extension" value="action"/>
<package name="demo1" extends="struts-default" namespace="/">
<action name="customer_*" class="customerAction" method="{1}">
<result name="saveUI">/jsp/customer/add.jsp</result>
</action>
</package>
</struts>

Action的访问配置:

  • 默认情况:直接配置name和class,默认请求交给class中的类的execute方法来处理

    <action name="actionDemo2" class="work.action1.ActionDemo2"></action>
  • 通过method来直接指定调用哪个方法来处理,下面的findAll.action会交给ActionDemo3中的findAll方法来处理

    <action name="findAll" class="work.action1.ActionDemo3" method="findAll"></action>
  • 利用通配符来指定调用哪个方法来处理,在name中使用通配符"*"来代表匹配任意字符串,在method中获取通配符匹配到的结果{1}代表获取第一个匹配的字符串,后面的获取也是{2}、{3}。下面中如果请求为customer_findAll.action,那么会调用ActionDemo7中的findAll方法。【通配符也可以在class中使用,所以你甚至可以根据请求来决定处理的类】

    <action name="customer_*" class="work.action1.ActionDemo7" method="{1}"></action>
  • 动态方法访问:动态方法访问有点类似上面的通配符的方式,但它主要在

    • 首先需要在struts.xml的struts标签下开启动态方法访问:

      <constant name="struts.enable.DynamicMethodInvocation"  value="true"></constant>
    • 然后在struts.xml配置,class中填用于处理的类的路径,name中用于填写”标识“,这个标识的作用可以说成用来标识某个Action,当请求为”标识“+"!"+"方法名".action时,就会调用Action的对应方法。(不理解的话看下面页面中的请求编写就理解了)

      <action name="user" class="work.action1.ActionDemo8"></action>
    • 最后,请求的编写是重点,请求要:”标识“+"!"+"方法名".action,发起什么样的请求就执行什么样的方法。

      • 比如http://localhost:8080/MystrutsDemo/user!list.action就会调用ActionDemo8的list方法

结果视图result的配置:

  • result标签:用于配置action处理完业务后跳转到哪个视图。

    • 属性:
      • name:逻辑视图的名称,与action中的返回值匹配。(不设置时,默认值为“success")
      • type:配置页面跳转的类型
        • dispatcher:转发(默认值)
        • redirect:重定向
        • chain:Action转发到Action【action之间的转发需要使用这个】
        • redirectAction:Action重定向到Action【action之间的重定向需要使用这个】
        • stream:用于配置下载上传的功能。
  • 局部结果视图配置:配置只针对result所在的action

    <struts>
    <package name="demo2" extends="struts-default" namespace="/">
    <action name="actionDemo21" class="work.action2.ActionDemo21" >
    <!-- 局部视图结果视图 -->
    <result name="saveUI">/add.jsp</result>
    </action>
    </package>
    </struts>
  • 全局结果视图配置:当想要某个结果视图在包下所有的action中都生效,那么可以使用全局结果视图,只要action返回值是这个,那么就会触发这个结果视图。【局部的结果视图优先级高于全局结果视图,如果在action中定义了同名的,那么局部的生效】

    <struts>
    <package name="demo2" extends="struts-default" namespace="/">
    <!-- 全局结果视图定义开始 -->
    <global-results>
    <result name="success">/success.jsp</result>
    <!-- 可以有多个result -->
    </global-results>
    <!-- 全局结果视图定义结束 -->
    <action name="actionDemo21" class="work.action2.ActionDemo21" >
    <result name="saveUI">/add.jsp</result>
    </action>
    </package>
    </struts>

分隔线:上面讲了action,result和struts.xml,一个大致的请求处理流程就已经结束了,下面的内容会更深一点。


数据封装:

在上面的action的讲解中,我们讲了使用原始的servlet来获取数据,但struts2比servlet高级得多,它可以自动封装表单提交过来的数据。

普通属性的封装

  • 属性驱动:Action中提供带有setter方法的属性,页面中提交与属性同名的表单项。当调用Action来处理的时候,同名的数据会对应封装到Action中的属性中。

    • Action的编写:

      • package work.action2;
        import com.opensymphony.xwork2.ActionSupport;
        public class ActionDemo22 extends ActionSupport {
        //有属性
        private String username;
        private String password;
        //属性有setter
        public void setUsername(String username) {
        this.username = username;
        }
        public void setPassword(String password) {
        this.password = password;
        }
        @Override
        public String execute() throws Exception {
        System.out.println(username+".."+password);//查看封装结果
        return NONE;
        }
        }
    • 页面的表单项的写法:要求表单项的name与Action中的属性同名

      • <form action="${pageContext.request.contextPath }/actionDemo22.action" method="post">
        <h1>属性驱动:属性有setter的方式</h1>
        <!-- 表单项的name与Action中的属性同名 -->
        <input type="text" name="username" />
        <input type="password" name="password" />
        <input type="submit" />
        </form>
  • 属性驱动:Action中提供一个带有getter、setter方法的类对象,页面中提交以类对象变量名.类对象属性*为命名的表单项。当调用Action来处理的时候,提交的数据会封装到Action中的类对象中。

    • Action的写法:

      • package work.action2;
        import com.opensymphony.xwork2.ActionSupport;
        import domain.User;
        public class ActionDemo23 extends ActionSupport {
        //有类对象
        private User user;
        //有setter,getter
        public User getUser() {
        return user;
        }
        public void setUser(User user) {
        this.user = user;
        }
        @Override
        public String execute() throws Exception {
        System.out.println(user.getUsername()+".."+user.getPassword());//查看封装结果
        return NONE;
        }
        }
    • 页面表单项的写法:要求表单项的name为Action中的“类对象.类的属性”

      • <form action="${pageContext.request.contextPath }/actionDemo23.action" method="post">
        <h1>属性驱动:类对象有setter、getter的方式</h1>
        <!-- 表单项的name为Action中的类对象.类的属性 -->
        <input type="text" name="user.username" />
        <input type="password" name="user.password" />
        <input type="submit" />
        </form>
  • 模型驱动:Action实现ModelDriven<T>接口,T为目标类,实现里面的getModel方法。首先,要求Action中手动初始化一个类对象,然后,将getModel方法中的返回值改成这个类对象。当页面中提供与类对象的属性同名的表单项时,提交的数据会通过ModelDriven自动封装到类对象中,并经过getModel返回,返回后类对象中就已经封装好了提交上来的数据。

    • action的写法

      • package work.action2;
        import com.opensymphony.xwork2.ActionSupport;
        import com.opensymphony.xwork2.ModelDriven;
        import domain.User;
        public class ActionDemo24 extends ActionSupport implements ModelDriven<User> {
        //有类对象
        private User user=new User();
        //在getModel中将返回值改成类对象的变量名
        @Override
        public User getModel() {
        return user;//此处将数据封装到user中
        }
        @Override
        public String execute() throws Exception {
        System.out.println(user.getUsername()+".."+user.getPassword());//查看封装结果
        return NONE;
        }
        }
    • 页面的写法:要求表单项的name为Action中的类对象的属性

      • <form action="${pageContext.request.contextPath }/actionDemo24.action" method="post">
        <h1>模型驱动:实现ModelDriven的方式</h1>
        <!-- 表单项的name为Action中的类对象的属性 -->
        <input type="text" name="username" />
        <input type="password" name="password" />
        <input type="submit" />
        </form>
  • 补充:

    • 第二种跟第三种比较常用,第二种好处是可以封装多个对象,第三种就只能封装一个对象,但它相对方便快捷。

复杂类型的数据封装

  • List类型的数据封装:在Action中提供List集合对象(不需要实例化),并提供getter和setter。页面中提交的表单项要求格式为"List变量名[下标]"。提交的数据按照下标封装到List集合中,页面中有多少就封装多少。如果集合中的对象为类,那么还可以使用List变量名[下标].属性来给集合中的对象封装属性

    • action的写法:

      • package work.action2;
        import java.util.Iterator;
        import java.util.List;
        import com.opensymphony.xwork2.ActionSupport;
        import domain.User;
        //测试list类型数据封装
        public class ActionDemo25 extends ActionSupport {
        //有list集合
        private List<User> users;
        //提供getter,setter
        public List<User> getUsers() {
        return users;
        }
        public void setUsers(List<User> users) {
        this.users = users;
        }
        @Override
        public String execute() throws Exception {
        for (User user : users) {
        System.out.println(user);
        }
        return NONE;
        }
        }
    • 页面表单项的写法:要求表单项的name为Action中的list集合对象[需要].对象的属性

      • <form action="${pageContext.request.contextPath }/actionDemo25.action" method="post">
        <h1>list类型的数据封装</h1>
        <!-- 表单项的name为Action中的list集合对象[需要].对象的属性 -->
        <input type="text" name="users[0].username" />
        <input type="password" name="users[0].password" />
        <input type="text" name="users[1].username" />
        <input type="password" name="users[1].password" />
        <input type="submit" />
        </form>
  • Map类型的数据封装:在Action中提供Map集合对象(不需要实例化),并提供getter和setter。页面中提交的表单项要求格式为"Map变量名[自定义的key]"。提及的数据会按照键值对存储到Map集合对象中。取出来的时候需要根据存储的时候用的key来获取。如果集合中的对象为类,那么还可以使用Map变量名[自定义的key].属性来给集合中的对象封装属性

    • action的编写方法:

      • package work.action2;
        import java.util.Iterator;
        import java.util.Map;
        import com.opensymphony.xwork2.ActionSupport;
        import domain.User;
        public class ActionDemo26 extends ActionSupport {
        //有map
        private Map<String,User> users;
        //提供getter,setter
        public Map<String, User> getUsers() {
        return users;
        }
        public void setUsers(Map<String, User> users) {
        this.users = users;
        }
        @Override
        public String execute() throws Exception {
        for (String key : users.keySet()) {
        User user=users.get(key);
        System.out.println("key:"+key+","+user);
        }
        return NONE;
        }
        }
    • 页面表单项的编写方法:

      • <form action="${pageContext.request.contextPath }/actionDemo26.action" method="post">
        <h1>map类型的数据封装</h1>
        <!-- 表单项的name为Action中的map集合对象[key].对象的属性 -->
        <input type="text" name="users['A'].username" />
        <input type="password" name="users['A'].password" />
        <input type="text" name="users['B'].username" />
        <input type="password" name="users['B'].password" />
        <input type="submit" />
        </form>

数据封装中的报错:Input逻辑视图:

  • 在数据封装中,如果你给一个int类型的数据封装一个字符串数据,那么会报错。但这是封装过程中报的错,所以这个要求在封装过程中处理。struts2的数据封装依赖于拦截器(下面有讲),拦截器处理过程中如果发生错误,它会将错误存储起来。struts2的拦截器错误处理方法是,如果workflow拦截器发现错误区域有错误信息,那么就会返回input,input是一个视图结果。所以input视图是很重要的。
  • 由于input视图很重要,那么input逻辑视图可以说是必须的。我们设置result的时候,如果考虑到数据错误的情况,那么应该设置一个input结果视图。
  • 在input视图中,可以利用struts标签来获取错误信息。【留到错误回显那里讲。】
  • 结果视图的配置上面已经讲了,所以这里就不再重复了。input结果跳转到哪个视图是可以由你自己指定的。

补充:

  • 数据回显是数据自动回显到表单项中,与从数据域中获取不同。struts2的数据回显依赖于它的struts2标签,由于这个内容需要struts2标签的理解,所以这个内容留到后面的数据回显再讲。

OGNL

介绍:

  • OGNL全称Object-Graph Navigation Language 对象图导航语言,是一种强大的表达式语言。
  • 在struts2中可以方便地使用OGNL来获取对象的属性、调用对象的方法等等操作。
  • 在struts2中,OGNL经常用来与值栈(struts2中一个用来存储数据的地方)配合使用,后面我们将介绍到值栈。

OGNL的使用认识:

  • ognl主要是用来获取数据的,但ognl也可以用来存储一些数据,这里不具体谈如何向ognl中存储数据,只略谈ognl数据存储结构,ognl的root根对象只能存储一个对象,context能存储多个对象。当向ognl中存储了数据之后,获取root中的数据,不需要#;访问context中的数据,需要加#(这里的加#是指获取的时候使用的key需不需要加#,ognl中存储的数据是键值对形式)。
  • 这里暂时不谈ognl表达式怎么使用,暂时先谈谈ognl表达式的语法格式,因为struts2的ognl重点也就是语法格式而已。
  • ognl表达式调用对象的方法格式是:对象.方法名(),例如:"abcdefg".length()
  • ognl表达式调用对象的静态方法格式是:@类@方法名(),其中要求类包含路径,例如@java.lang.Math@random()
  • ognl表达式获取List集合对象的数据格式是:集合名[下标] ,例如:list[0]
  • ognl表达式获取Map集合对象的数据格式是:集合名[key]

使用:

由于ognl主要是用来获取数据的,在struts中,它一般使用在视图上.

  • 获取属性:<s:property value="ognl表达式" />这样就可以把ognl的值取出来,并显示在视图上了.

  • 调用对象的方法:(ognl表达式调用对象的方法格式是:对象.方法名())

    • <s:property value="ognl表达式" />
    • 例如:<s:property value="user.getName()" />【假设存储了一个user对象】
  • 调用对象的静态方法:(ognl表达式调用对象的静态方法格式是:@类@方法名(),其中要求类包含路径)

    • 首先需要配置常量允许ognl调用对象的静态方法,常量可以在struts.xml中配置,也可以在struts.properties中配置:

      • <constant name="struts.ognl.allowStaticMethodAccess" value="true" />
    • 然后调用方法:

      • 例如:<s:property value="@java.lang.Math@random()" />

在struts2中,ognl表达式默认是从值栈中获取数据的,并且由于值栈是struts2的很多数据的中转站,所以要讲一下值栈,然后再讲述ognl的使用。

值栈

  • struts2在创建Action的同时会创建一个OgnlValueStack值栈(一个action使用一个值栈)。OgnlValueStack与Ognl的结构有点类似,不过这时候root能够存储多个对象。OgnlValueStack与Ognl关系密切,我们可以使用ognl表达式来获取OgnlValueStack里面的数据。
  • Struts2的框架当中的数据就都保存到了ValueStack中,实例是OgnlValueStack值栈。
  • 值栈的内部结构:
    • root:其实就是一个ArrayList。里面一般放置对象。【与ognl的root相类似,但可以存储多个对象,获取root的数据不需要加#】。
    • context:其实就是一个Map。里面放置是web开发的常用的对象数据的引用。获取context数据需要加#。【由于context只包含几个对象的引用,所以其他不是这几个对象中的数据的都可以不使用#
      • 包含的对象:
        • request
        • session
        • application
        • parameters
        • attr

获取值栈:

  • 在Action中通过ActionContext获取:

    • ValueStack valueStack = ActionContext.getContext().getValueStack();
  • 在Action中通过request获取:

    • ValueStack valueStack = (ValueStack)ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

操作值栈:

  • 向值栈中存储数据:【下面讲的都是存放数据到root区域中的方式,如果想存储到context区域,需要理由request\session这些数据域来存储值,request这些数据域就存储在context区域中。】

    • 由于action也会被存储到值栈中,所以action中定义的属性也会存储到值栈中,但如果你想要获取这种方式存储的数据,那么Action的属性要提供getter。

    • 调用push方法来存储,存储格式是对象存储,获取时根据对象中的属性名来获取,不是使用对象.属性来获取。

      • public class ActionDemo32 extends ActionSupport {
        @Override
        public String execute() throws Exception {
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        User user= new User();
        valueStack.push(user);
        return NONE;
        }
        }
    • 调用set方法来存储,存储格式是键值对,获取的时候根据key来获取。

      • public class ActionDemo32 extends ActionSupport {
        @Override
        public String execute() throws Exception {
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        User user= new User();
        valueStack.set("user", user);
        return NONE;
        }
        }
  • 获取值栈数据:

    • 值栈的数据通常在视图中使用,通常我们使用ognl表达式来获取值栈中的数据,并且通常在视图中使用ognl表达式来获取数据,比如你可以使用<s:property value="ognl表达式" />来获取值栈中的数据
    • 当值栈有同名数据时,接近栈顶的生效,所以如果某个属性与对象中的属性同名时,要小心使用。
    • 比如:
      • 获取user对象的属性<s:property value="user.username" />【假设值栈中存储了一个对象叫user】
      • 调用user对象的方法<s:property value="user.getUsername" />【假设值栈中存储了一个对象叫user】
      • 获取存储到request域中的属性:<s:property value="#request.username" />【假设request中存储了一个属性叫username】

小演示:

  • 在action中使用push来存储一个数据,在action中向request中存储一个数据.

    • package work.action3;
      
      import org.apache.struts2.ServletActionContext;
      
      import com.opensymphony.xwork2.ActionContext;
      import com.opensymphony.xwork2.ActionSupport; import domain.User; public class ActionDemo33 extends ActionSupport {
      private String phone; public String getPhone() {
      return phone;
      } @Override
      public String execute() throws Exception {
      phone="10086";//测试action方式存储数据
      ActionContext.getContext().getValueStack().set("user", "lilei");//set方式方式存储数据
      User user= new User();
      user.setUsername("炸弹人");
      user.setPassword("123456");
      ActionContext.getContext().getValueStack().push(user);//push方式方式存储数据
      ServletActionContext.getRequest().setAttribute("address", "1314");//向request中存储
      return "show";
      }
      }
  • 在页面中使用OGNL获取出来。

    • <%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8"%>
      <%@ taglib uri="/struts-tags" prefix="s" %>
      <!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>
      action中的phone:<s:property value="phone" /><br>
      set方式存储的user:<s:property value="user" /><br>
      push方式存储的对象的username:<s:property value="username" /><br>
      push方式存储的对象的password:<s:property value="password" /><br>
      request中的password:<s:property value="#request.password"/>
      </body>
      </html>

OGNL中的特殊符号

  • #

    • 获取context数据,例如:<s:property value="#request.username" />
    • 在视图中也可以可以定义数据的,特别是使用struts标签时,#可以构建一个map集合【这个是标签的内容,这里不讲述,留到下面讲】
  • %号:

    • 强制解析OGNL,在struts2表单标签里面使用ognl表达式是不解析,只有%之后才会强制解析。

      • <!-- 下面是不解析的 -->
        <s:textfield name="name" value="#request.name" />
        <!-- 强制解析 -->
        <s:textfield name="name" value="%{#request.name}" />
    • 强制不解析OGNL(少用,有兴趣自查)

  • $号:

    • 在配置文件中使用OGNL,在配置文件中使用${ognl表达式}就能在配置文件中使用ognl。

EL的强化:

  • 介绍完值栈之后,要提一下的是,在struts2中,EL表达式可以获取值栈中的数据。
  • 能获取值栈的数据的原因是struts2对EL进行了强化,EL表达式本身也需要依靠类,而在struts2中,这个类被封装成了一个新的代理对象,使得EL表达式的功能被强化了。原本它只能在几个数据域中获取数据,增强之后,如果从数据域中找不到对应数据时,会到值栈中找。
  • 【假设值栈中存储了一个叫username的属性,EL表达式可以使用{ username }来获取到值栈中username的值】

补充:

  • 上面的<s:textfield name="name" value="#request.name" />其实就是从值栈中获取数据来回显到视图,所以其实也可以使用ognl表达式来实现数据回显。
  • OGNL是一个第三方表达式语言,它其实还可以在普通的javase工程中使用,这里不讲述,有兴趣可以自查。
  • 在struts2的标签中,经常会需要一些变量,就好比标签循环中的i,在struts2标签中的变量会存储到值栈中(标签那里会讲),那么这个变量该不该用#?。

数据校验

  • ActionSupport提供了数据校验。我们这里讲的数据校验中的Action必须继承ActionSupport。
  • 这个数据校验依赖validation拦截器
  • 当数据校验错误时,我们可以向fielderror中存储错误信息。然后在页面中使用fielderror标签显示出来。

手动定义校验规则:

  • 重写ActionSupport的validate方法,在到达目标业务处理方法之前,validate先执行,并且它是属于拦截器的内容,它可以向错误区域中存储错误信息,使得workflow拦截器拦截请求到方法。【获取错误信息可以使用actionerror和fielderror标签获取,什么类型的错误使用什么标签获取,fielderror一般都是表单错误,使用可以参考下面的页面编写】

    • Action的编写:

      • 要校验数据,必须先获取数据,所以属性要提供setter;其次,检验中如果发生错误,必须向错误域中写入错误信息,这样才能拦截请求。
      package work.action3;
      
      import com.opensymphony.xwork2.ActionSupport;
      
      public class LoginAction extends ActionSupport {
      //要校验 ,先获取
      private String username;
      private String password; public void setUsername(String username) {
      this.username = username;
      }
      public void setPassword(String password) {
      this.password = password;
      }
      @Override
      public String execute() throws Exception {
      System.out.println("登录成功!");
      return NONE;
      }
      @Override
      public void validate() {
      if(username==null ||username.trim().length()==0) {
      //向错误区域写错误
      this.addFieldError("username", "用户名不能为空");//往错误区存入错误使workflow拦截器拦截运行
      }
      if(password==null ||password.trim().length()==0) {
      //向错误区域写错误
      this.addFieldError("passoword", "密码不能为空");//往错误区存入错误使workflow拦截器拦截运行
      }
      }
      }
    • 页面的编写:

      <%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8"%>
      <%@ taglib uri="/struts-tags" prefix="s" %>
      <!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>
      <form action="${pageContext.request.contextPath }/login.action" method="post" />
      <h3>
      <s:fielderror /><!-- 用来显示错误的 -->
      </h3>
      用户名:<input type="text" name="username" />
      密码:<input type="password" name="password" />
      <input type="submit">
      </form>
      </body>
      </html>
  • 上面的重写validate会对所有的方法进行校验,如果只想校验某个方法,可以使用validateXXX,XXX为方法名(首字母大写)。由于跟上面的差不多,只给个小例子。

    • //只校验login方法的数据
      public void validateLogin() {
      if(username==null ||username.trim().length()==0) {
      //向错误区域写错误
      this.addFieldError("username", "用户名不能为空");//往错误区存入错误使workflow拦截器拦截运行
      }
      if(password==null ||password.trim().length()==0) {
      //向错误区域写错误
      this.addFieldError("passoword", "密码不能为空");//往错误区存入错误使workflow拦截器拦截运行
      }
      }

使用自带的数据校验器:

  • struts2提供了不少数据校验器给我们,我们要使用他提供的数据校验器,那么我们需要进行xml配置。

    • 1.在Action的包下创建一个xml:xxx-validation.xml 【xxx是Action的名字】,其中dtd文件在xwork依赖包的根目录下,根据现在的版本,这里使用xwork-validator-1.0.3.dtd,在注释里面拷贝一下里面的dtd约束

      -struts2从认识到细化了解

    • 2.在xxx-validation.xml 中配置

      • <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE validators PUBLIC
        "-//Apache Struts//XWork Validator 1.0.3//EN"
        "http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
        <validators>
        <!-- 每个field就是一个表单项,name是要检验的表单项名称 -->
        <field name="username">
        <!-- field-validator是校验器,type的值是我们要选择的校验器 -->
        <field-validator type="requiredstring">
        <!-- message是违反校验时的报错信息 -->
        <message>用户名不能为空!</message>
        </field-validator>
        </field>
        <field name="password">
        <field-validator type="requiredstring">
        <message>密码不能为空!</message>
        </field-validator>
        </field>
        </validators>
    • 3.在action中配置:要给属性提供getter和setter,setter是封装从表单提交过来的数据,getter是用于给自带的校验体提供数据。

      • package work.action3;
        
        import com.opensymphony.xwork2.ActionSupport;
        //测试数据校验
        public class LoginAction extends ActionSupport {
        //要校验 ,先获取
        private String username;
        private String password; public void setUsername(String username) {
        this.username = username;
        }
        public void setPassword(String password) {
        this.password = password;
        } public String getUsername() {
        return username;
        }
        public String getPassword() {
        return password;
        }
        @Override
        public String execute() throws Exception {
        System.out.println("登录成功!");
        return NONE;
        }
        }
    • 上面的方法也是给action进行校验,如果想要仅仅想要给action中的某个方法使用校验器,那么xml的文件名应该为**Action类名-方法对应的访问路径-validation.xml **(这里的方法对应的访问路径是指struct.xml中action中的name,假如校验一个叫LoginAction中的execute方法,然而访问路径是login,那么文件名是LoginAction-login-validation.xml),然后xml里面的配置与上面的一样。

校验器的参数如何配置

对于一些校验器要提供参数来帮助校验,这里讲一下怎么配置校验器的参数

  • stringlength校验器是用来校验输入的字符串的长度的,所以它需要提供范围参数。我们利用这个来查看
    • 参数:
      • dotrim
      • minLength
      • maxLength
    • 设置参数值的方法:

      -struts2从认识到细化了解
  • 想要查看校验器有哪些参数,可以看一下xwork依赖包中的com/opensymphony/xwork2/validator/validators/下的类,这些类就是对应的校验器,它们里面有什么属性,就说明可以赋予哪些参数。这些参数大部分都是有默认值的。

常见自带数据校验器:

想知道有什么默认的数据校验器,可以参考xwork依赖包中的com/opensymphony/xwork2/validator/validators/default.xml

  • required:要求必须填写,但可以是空格。
  • requiredstring:要求必须填写,但不可以是空格。
  • int:要求必须是整数,且数值要在校验器参数的min和max之间。
  • date:要求必须是日期,且日期要在校验器参数的min和max之间。
  • email:必须是合法的email
  • url:必须是合法的url
  • regex:根据校验器的参数来进行正则校验。
  • stringlength:要求输入的字符串长度必须符合规则,要求长度要在校验器参数的minLength和maxLength之间。
  • expression:校验与某个表单项的值是否相同(重复密码校验?)。

补充:

  • 这里没有讲如何自定义校验器,有兴趣的可以自查。

拦截器

拦截器的介绍:

  • 拦截器本身还是过滤器,不过它对过滤器进行了强化,使得它能做更精细的事,比如过滤某个方法。
  • 当发起请求的时候,请求先经过核心过滤器,然后还要经过一系列的拦截器才能到达action.

struts2从认识到细化了解

struts2自带的拦截器

(只介绍几个,只为帮助了解拦截器的作用。)

  • params:这个可以帮助我们把请求提交过来的数据封装到javabean中(上面提到的数据封装)。
  • servlet-config:这个可以帮助我们把servlet API传给action。
  • fileupload:这个可以帮助我们封装上传的文件数据,所以说,struts2是可以实现上传文件的功能的。
  • validation:帮助我们进行数据校验,比如email表单项没有填正确的话,那么它会把错误信息存储起来。
  • modelDriven:帮助封装数据,这个就是ModelDrivern注入法所依赖的拦截器.
  • workflow:这个负责最终工作流向,在最终到action之前,workflow会检测前面的拦截器是否发生了错误(拦截器发生错误不会立即停止,会把错误信息存储起来,然后workflow检查有没有错误信息),如果发生了错误,跳转到INPUT视图,正常则将请求交给action。

自定义拦截器:

  • 编写一个类实现Interceptor接口或者继承AbstractInterceptor类(Interceptor接口的空实现类)。

    • 实现Interceptor接口需要实现三个方法,但主要实现intercept方法,其他两个可以空实现,Intercept方法的实现可以参考下面继承AbstractInterceptor类版本的。

    • package work.interceptor;
      
      import com.opensymphony.xwork2.ActionInvocation;
      import com.opensymphony.xwork2.ActionSupport;
      import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class MyInterceptor2 extends AbstractInterceptor {
      @Override
      public String intercept(ActionInvocation invocation) throws Exception {
      boolean flag=true;
      if(flag) {//这里做演示,所以不给明显条件,假设flag为用户是否已登录。
      //处理。。。
      System.out.println("你经过了我的拦截器");
      return invocation.invoke();//登录了,放行
      }else {
      //处理。。。
      //不放行的时候要考虑给视图进行返回所以这里要获取Action
      ActionSupport action = (ActionSupport) invocation.getAction();
      return action.LOGIN;//不放行,返回登录页面
      }
      }
      }
  • 创建了拦截器之后,要告诉struts2新增了拦截器,要在struts.xml中对拦截器进行配置,有两种配置方法【但记住struts默认当作是覆盖了原来的拦截器,如果你还需要原有的拦截器,需要加上,默认的拦截器栈为defaultStack;拦截器加载是有顺序的,依据配置的顺序,所以如果想要在默认拦截器栈之前使用自定义的拦截器,可以先引入自定义的,再引入默认的。】:

    struts2从认识到细化了解

    • 配置方法1:先在interceptors中用interceptor声明新建了一个拦截器,name是自定义的拦截器的名字,class是拦截器的类路径;再使用interceptor-ref把拦截器配置到action中,name是拦截器的名字。
    • 由于默认栈很重要,所以我们经常要加回。当需要给多个action配置拦截器的时候,上面那种方法会导致需要很多重复操作,所以可以把新定义的拦截器放到一个拦截器栈(拦截器栈在interceptor中使用interceptor-stack声明)中,然后再在action中配置拦截器栈(这样重复次数少很多)

      struts2从认识到细化了解

struts2的标签:

  • struts2的标签可以帮助视图的显示,与jsp的标签功能有点类似,但功能要比jsp的强大。
  • 要使用struts2的标签,先要在页面中导入struts2的标签的标签库:
<%@ taglib uri="/struts-tags"  prefix="s"%>

通用标签:

struts2从认识到细化了解

控制标签:

  • 判断标签:<s:if><s:elseif><s:else> 。与jsp中JSTL标签 的if功能类似。它使用test来判断。由于太过简单,不解释了。
    • 属性:
      • test:使用test来判断
<s:if test="%{false}">
<div>不显示的</div>
</s:if>
<s:elseif test="%{true}">
<div>显示到页面内容的</div>
</s:elseif>
<s:else>
<div>不显示的</div>
</s:else>
  • 迭代标签:<s:iterator>

    • 属性:

      • begin:如果指定,则迭代将在该索引上开始
      • end:如果指定迭代将在该索引上结束(包括)
      • status:迭代元素的索引对象,可以使用getIndex() 获取当前值
      • step:迭代的步数。
      • value:用来迭代的对象【可选的,如果没有给定迭代的对象,那么应该给begin和end,用来指定迭代一个列表】value属性的值无论来自对象栈还是map栈都可以不加#。
      • var:迭代过程中,存放到值栈中的指向迭代元素的对象【可选的】【会存放到root区域】
    • <s:iterator>中可以使用<s:property />来输出变量的值

      • <s:iterator value="{'1','2','3','4','5','6'}" >
        <s:property />
        </s:iterator>
        <!-- 结果: 1 2 3 4 5 6 -->
        <br>
        <s:iterator var="i" status="s" value="{'1','2','3','4','5','6'}" >
        <s:property value="i" />--
        <s:property value="#s.getIndex()" />
        </s:iterator>
        <!-- 结果:1-- 0 2-- 1 3-- 2 4-- 3 5-- 4 6-- 5 -->

数据标签:

  • 显示数据标:<s:property />

    • 核心属性:

      • value:要获取的值,可以使用ognl表达式
      • default :如果value属性为null,则显示的值
    • <s:property value="username"/><!-- 假设值栈中存储着username -->
      <s:property value="user.name"/><!-- 假设值栈中存储着user对象 -->
      <s:property value="#request.flag"/><!-- 假设request中存储着flag属性 -->
    • 提一下的是:<s:property value="model.属性名"/>可以获取使用模型驱动封装的属性。

      • 下面的代码是使用模型驱动封装User对象的属性,然后使用<s:property value="model.属性名"/>获取属性。

        <s:property value="model.username"/>
        <s:property value="model.password"/>
  • 设置数据的标签,把数据存储到值栈中:<s:set var=”i” value=”5” scope=”request” />
    • 属性:
      • var :相当于key,用于标识存储到值栈中的值。
      • value :要存储的值。
      • scope :数据存储到哪里,可以是application, session, request, page或action(默认值,).
  • 在页面上显示格式化的日期:<s:date />

    • 核心属性:

      • format :定义如何格式化日期
      • name :用于被格式化的日期数据
    • <!-- 假设存储了一个person对象,里面有birthday -->
      <s:date name="person.birthday" nice="true" />
      <s:date name="person.birthday" />
      <s:date name="person.birthday" format="dd/MM/yyyy" />

表单标签:用来取代html标签

  • <s:textfield /> :代替text输入框

    • 通用属性:cssClass 【相当于class】,cssStyle 【相当于style】,id ,label 【给输入框加上label】,name 【相当于name】,value 【相当于value】,
  • <s:password />:代替password输入框

    • 通用属性:cssClass 【相当于class】,cssStyle 【相当于style】,id ,label 【给输入框加上label】,name 【相当于name】,value 【相当于value】,
  • <s:radio /> :代替单选框

    • 通用属性:cssClass 【相当于class】,cssStyle 【相当于style】,id ,label 【给输入框加上label】,name 【相当于name】,value 【相当于value】,

    • 在radio标签中,它是“一组”单选框,所以我们要给它提供能够形成一组单选框的值。

      • <!-- 提供一个genders属性在值栈中,通过list获取,那么会把list的值作为多个单选框的值,key是name -->
        <s:radio label="性别" name="gender" list="genders"/>
        <!-- 下面的方式把gender作为name,把list中的元素作为单选框的值和提示文字 -->
        <s:radio label="性别" name="gender" list="{'男','女'}"/>
        <!-- 下面的方式把gender作为name,把list中的元素中的key作为单选框的值,value作为提示文字 -->
        <s:radio label="性别" name="gender" list="#{'1':'男','2':'女'}"/>
  • <s:file /> :代替文件上传框

    • 通用属性:cssClass 【相当于class】,cssStyle 【相当于style】,id ,label 【给输入框加上label】,name 【相当于name】,value 【相当于value】,
  • <s:form /> :代表form表单

    • 核心属性:
      • action:表单中的action,要求是访问路径,比如说访问hello.action,那么直接写hello.action即可。
      • method:提交的方法
    • 通用属性:cssClass 【相当于class】,cssStyle 【相当于style】,id ,label 【给输入框加上label】,name 【相当于name】,value 【相当于value】,
  • <s:hidden />

    • 核心属性:
      • name:隐藏域的名字
      • value:隐藏域的值

错误显示标签:

  • <s:actionerror /> :当action中处理发生错误时,会把错误封装到错误域,可以使用<s:actionerror />来获取此次请求发生的错误。【我们向actionerror 中写错误的时候一般都是业务逻辑处理错误】
    • 在action中使用this.addActionError(错误信息)就可以向Action错误域中增加一个错误。
    • 在错误域中有了错误,那么在页面中直接使用<s:actionerror /> 就可以把错误输出到页面中。
  • <s:fielderror />:当表单项发生提交错误时,会把错误封装到错误域,可以使用<s:fielderror />来获取此次请求发生的错误。【我们向fielderror中写错误的时候一般都是表单项提交错误】
    • 在action中使用this.addFieldError(错误信息)就可以向Field错误域中增加一个错误。

补充:

  • 想了解更多,可以参考struts-2.3.34\docs\docs\tag-reference.html

数据回显:

  • 使用了struts2的表单标签后,struts2会在跳转视图的时候,根据struts2的表单标签的name从值栈中获取数据。
  • 注意一下的是,这里的数据回显不使用<s:property />标签,因为它能够利用ognl表达式从值栈中取数据,所以它本质上是取数据,而不是回显(虽然其实数据回显本质也是取数据,但没有那么直接)。所以这里使用输入框来回显。【password默认是不回显的,可以配置】
  • 与属性驱动方式的数据封装对应的数据回显:在数据封装时,要求action中的属性提供setter,回显的时候要求提供getter

    • action中的编写【测试的流程是,页面提交数据到action中,action返回刚才的页面,查看页面时候回显了刚才提交的数据。】

      package work.action3;
      
      import com.opensymphony.xwork2.ActionSupport;
      
      public class ActionDemo35 extends ActionSupport {
      //有属性
      private String username;
      private String password;
      //属性有setter,getter
      public void setUsername(String username) {
      this.username = username;
      }
      public void setPassword(String password) {
      this.password = password;
      } public String getUsername() {
      return username;
      }
      public String getPassword() {
      return password;
      }
      @Override
      public String execute() throws Exception {
      System.out.println(username+".."+password);//查看封装结果
      return "reShow";
      } }
    • 页面的编写

      • <s:form action="actionDemo35.action"  method="post"   >
        <s:textfield label="用户名" name="username"></s:textfield>
        <s:password label="密码" name="password"></s:password>
        <s:submit></s:submit>
        </s:form>
  • 与属性驱动方式的数据封装对应的数据回显:在数据封装时,要求action中的对象提供setter,getter,回显的时候不再需要额外提供getter

    • action的编写:【测试的流程是,页面提交数据到action中,action返回刚才的页面,查看页面时候回显了刚才提交的数据。】

      • package work.action3;
        
        import com.opensymphony.xwork2.ActionSupport;
        
        import domain.User;
        
        public class ActionDemo35 extends ActionSupport {
        //有类对象
        private User user;
        //有setter,getter
        public User getUser() {
        return user;
        }
        public void setUser(User user) {
        this.user = user;
        }
        @Override
        public String execute() throws Exception {
        System.out.println(user.getUsername()+".."+user.getPassword());//查看封装结果
        return "reShow";
        } }
    • 页面的编写:

      • <s:form action="actionDemo35.action"  method="post"   >
        <s:textfield label="用户名" name="user.username"></s:textfield>
        <s:password label="密码" name="user.password"></s:password>
        <s:submit></s:submit>
        </s:form>
  • 与模型驱动方式的数据封装对应的数据回显与第一个数据回显的方式页面编写方式相同,不同只是action,action不需要额外提供setter和getter.

    • action的编写:

      • package work.action3;
        
        import com.opensymphony.xwork2.ActionSupport;
        import com.opensymphony.xwork2.ModelDriven; import domain.User; public class ActionDemo35 extends ActionSupport implements ModelDriven<User> {
        private User user=new User();
        @Override
        public User getModel() {
        return user;
        }
        @Override
        public String execute() throws Exception {
        System.out.println(user.getUsername()+".."+user.getPassword());//查看封装结果
        return "reShow";
        }
        }
    • 页面的编写:

      •  <s:form action="actionDemo35.action"  method="post"   >
        <s:textfield label="用户名" name="username"></s:textfield>
        <s:password label="密码" name="password"></s:password>
        <s:submit></s:submit>
        </s:form>

写在最后

​ 这篇博文的出发点是启蒙和认识使用,所以这篇博文中并没有讲述struts2的所有内容,也没有对所有知识点讲清楚它的运行原理。上面的内容学习完毕后,你就可以大致的开发出一个项目了。如果你想要了解更多,你可以参考专业的书籍。但如果你仅仅想要学习如何使用,你可以根据我这篇博文的内容,通过百度或者学习论坛来进行扩展学习即可,因为专业书籍通常都会涉及到一些不常用的内容,但这所谓的不常用就仁者见仁智者见智了。

附上一些想写但没写上的内容:【出于篇幅以及系统化讲述(一个系列的内容只讲一个小东西很不习惯,所以我也需要去查一下)的考虑所以没写】

  • 类型转换问题:当进行封装的时候,可能会有封装类型不符合的问题,所以有时候需要自定义类型转换。

  • jsp的取代品:freemarker、velocity

  • 上传和下载

  • 数据校验:自定义校验器