JavaWeb开发知识总结(五)-(struts2_概述)

JavaWeb开发知识总结(struts2-概述)

1. struts2概述

框架学习重点:

  1. 框架封装了什么?
  2. 我们开发时能在框架的基础上做什么?

框架:是指定义的一组规范,解决特定领域内问题,并且框架具备重用性。

struts2框架

​ Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互,struts2框架实际上是struts1和webwork的综合。它将不同的逻辑功能通过方法的形式封装到一个类中,提高代码的通用性,然后通过配置文件的形式来访问指定类中指定的方法实现逻辑功能。

struts2框架的核心内容:

  1. 拦截器:interceptor ,用来拦截action;
  2. action:一个包含了execute方法的普通的Java类,是完成类似Servlet中逻辑处理的功能;
  3. ognl和valueStack:ognl表达式和值栈。

struts2的工作流程图及说明:
Struts2流程图

# 一个HttpServletRequest请求在Struts2框架中的处理步骤:
1. 客户端向服务器发送一个Http请求;
2. 这个请求经过一系列的过滤器(Filter)(其中有一个过滤器中是ActionContextCleanUp的可选过滤器,这个过滤器一般用于Struts2和其他框架的集成); 
3. 接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action;FilterDispatcher是控制器的核心,就是mvc中c控制层的核心。
4. 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy;
5. ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类 ,一般是从struts.xml配置中读取配置信息;
6. ActionProxy创建一个ActionInvocation的实例;
7. ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用:
        ActionInvocation是工作流程的:
        ActionInvocation是Xworks中Action调度的核心。而对Interceptor的调度,也由ActionInvocation负责。ActionInvocation是一个接口,DefaultActionInvocation则是Webwork对ActionInvocation的默认实现。
        Interceptor的调度流程大致如下:
        1.ActionInvocation初始化时,根据配置,加载Action相关的所有Interceptor;
        2.通过ActionInvocation.invoke方法调用Action实现时,执行Interceptor;
        Interceptor将很多功能从我们的Action中独立出来,大量减少了我们Action的代码,独立出来的行为具有很好的重用性。XWork、WebWork的许多功能都是有Interceptor实现,可以在配置文件中组装Action用到的Interceptor,它会按照你指定的顺序,在Action执行前后运行。
8. 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2框架中继承的标签。在这个过程中需要涉及到ActionMapper。
# 在上述过程中所有的对象(Action,Results,Interceptors等)都是通过ObjectFactory工厂类来创建的。

2. struts2框架原理

​ struts2框架是MVC(web层/表现层,service层/业务层,dao层/持久化层)中web层的框架,根据JavaWeb传统模式的开发中,web层是使用Servlet作为控制器,功能是获取参数、封装数据和跳转页面,struts2代替Servlet,则需要清楚struts2框架的配置、参数数据的封装处理和页面跳转三个方面的使用。

2.1 struts2入门案例

struts2使用步骤:

  1. 导入struts2相关的基本功能的jar包(基本功能需要13个jar包,可以在struts2提供的示例项目下找到);
  2. 需要在web项目的web.xml文件中配置能够使struts2工作的一个过滤器StrutsPrepareAndExecuteFilter,拦截所有的请求(将所有的请求托管给struts2进行处理);
  3. 在项目的src目录下创建struts.xml配置文件,配置自定义的action类的访问路径及访问的方法及跳转路径;
  4. 创建自定义的action类,实现自定义的逻辑代码。

struts2框架源码分析:以struts-2.3.24为例

struts2目录下有apps(示例代码)、docs(框架API文档)、lib(框架的jar包)和src(框架源码)。

  1. struts2框架的核心源码:以org.apache.struts2开头的包中,位置:src\core\src\main\java
  2. struts2的xwork核心源码:位置:src\xwork-core\src\main\java\com\opensymphony\xwork2
  3. struts2插件的源码:位置:src\plugins

入门案例:

在jsp页面创建请求的链接:

<a href="${pageContext.request.contextPath}/struts">struts2入门案例</a>

在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>

创建自定义的action类:实现在控制台输出语句

public class Struts2Demo1 {
    public void show() {
        System.out.println("hello somnus....");
    }
}

在项目的src目录在的struts2.xml配置文件中配置action访问路径:

<?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="default" namespace="/" extends="struts-default">
        <action name="struts" class="com.itheima.web.struts.Struts2Demo1" method="show">
        </action>
    </package>
</struts>

struts2执行流程分析:

# 执行流程分析:
1. 当通过浏览器发送一个请求
2. 会被StrutsPrepareAndExecuteFilter拦截
3. 会调用strtus2框架默认的拦截器(interceptor)完成部分功能
4. 再执行Action中操作
5. 根据Action中方法的执行结果来选择来跳转页面Result视图
# 一般将StrutsPrepareAndExecuteFilter 叫做前端控制器(核心控制器),只有配置了这个filter我们的strtus2框架才能使用。
# Strtus2的默认拦截器(interceptor)它们是在struts-default.xml文件中配置的
# 注意: struts-default.xml文件是在strtus-core.jar包中。默认的拦截器是struts-default.xml的defaultStack中定义的。

2.2 struts2配置文件

2.2.1 web.xml的配置:

​ struts2的框架的执行是基于前端控制器,struts2的前端控制器是一个过滤器,需要在项目工程的web.xml配置文件中配置过滤器,这个过滤器是为了拦截所有的请求,并将请求的处理交给struts2框架执行,这样才能使得struts2框架生效,这个过滤器的配置如下:

<!--在项目的web.xml文件中配置-->
<filter>
    <filter-name>struts2filter</filter-name>
    <!--过滤器的class的完全限定名-->
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>struts2filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
2.2.2 struts.xml配置文件
struts2配置文件加载顺序:

​ struts2的过滤器本质是Filter,在服务器启动时创建,创建时会执行过滤器中的init方法,在该方法中会进行struts2框架配置文件的加载并初始化,在加载时初始化配置文件的顺序及类型如下:

// 会执行的初始化方法   实际上是调用struts2框架中的Dispatcher类中的init()方法进行
// 配置文件按照下列的顺序进行加载
init_DefaultProperties(); // [1]
init_TraditionalXmlConfigurations(); // [2]
init_LegacyStrutsProperties(); // [3]
init_CustomConfigurationProviders(); // [5]
init_FilterInitParameters() ; // [6]
init_AliasStandardObjects() ; // [7]
// 说明:
第一个加载:default.properties文件
    位置:strtus2-core.jar/org.apache.struts2包下
    作用:主要是声明了struts2框架的常量
第二个加载一批配置文件:
    strtus-default.xml文件
    位置:struts2-corl.jar
    作用:声明了interceptor  result  bean
    strtus-plugin.xml文件
    位置:在strtus2的插件包中
    作用:主要用于插件的配置声明
    strtus.xml文件
    位置:在我们自己的工程中src下
    作用:用于我们自己工程使用struts2框架的配置,action和interceptor等配置
第三个加载的是自定义的strtus.properties
    位置:都是在自己工程的src下
    作用:定制自定义常量
第四自定义配置提供
第五加载的是web.xml配置文件
    web.xml中的系统的配置在服务器启动时加载配置信息,而struts2再去加载在web.xml文件中的相关配置.
第六加载bean相关配置
// 注意事项:
1.在开发中一般只关心使用struts.xml、strtus.properties和web.xml三个配置文件,这三个配置文件按照顺序进行加载,在这三个配置文件中均可以定制自定义常量,但是后加载的文件中定义的常量会将先加载文件中同名的自定义常量覆盖。
struts.xml中package配置:

​ package是包,用来管理struts2中的action和拦截器,每个package中可以有多个action、拦截器及拦截器引用的集合。package标签具有4个属性:

# 1.name属性(必选):定义一个包的名称,必须唯一,一般不能出现中文。
# 2.namespace属性(可选):主要是与action标签的name属性联合使用来确定一个action的访问路径。
# 3.extends属性(可选):指定该package继承自package包。一般值是struts-default(struts-default包是在struts-default.xml文件中声明的),struts-default是struts默认配置的,将自定义的package继承至struts-default使得自定义的package中配置的类具有action功能;
# 4.abstract属性(可选):它代表当前包是一个抽象的,主要是指示该package可以被继承。

# 注意事项:
1. 在namespace配置时:有三种配置方式
    * namespace="":默认名称空间
    * namespace="/":项目根名称空间
    * namespace="/名称":带名称的名称空间   访问时需要加上名称
案例:
    struts.xml中配置:
    <package name="default" namespace="/user" extends="struts-default">
        <action name="login" class="com.web.struts.Struts2Demo1"
            method="show">
        </action>
    </package>
    Jsp页面中访问路径:可以执行Struts2Demo1中的show方法
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        <input type="submit" value="login"/>
    </form>
2. action访问时会向从绝对匹配路径访问,如果绝对路径匹配不到要访问的action,则会向上一级目录进行搜索要访问的action,直到搜索到namespace配置命名空间,如果在namespace空间中没有搜索到要访问的action,则报错。
案例:
    namespace中配置的是/,action标签配置name属性,当在jsp页面请求的是name配置的虚路径时,也可以访问,如:访问 项目名/user/login,也可以执行action中的show方法
    struts.xml中配置:
    <package name="default" namespace="/" extends="struts-default">
        <action name="login" class="com.web.struts.Struts2Demo1"
            method="show">
        </action>
    </package>
    Jsp页面中访问路径:可以执行Struts2Demo1中的show方法
    <form action="${pageContext.request.contextPath}/somnus/user/login" method="post">
        <input type="submit" value="login"/>
    </form>
struts.xml中action配置:

​ action中配置是将请求的URL映射到一个action类,当请求匹配到action的name属性时,就会使用action中配置的类来处理该请求。action标签的配置是将要请求访问的action和自定义的action类进行关联。action有3个属性:

# 1.name属性(必选):主要是与package的namespace联合使用来确定一个action的访问路径,配置action的访问路径;
# 2.class属性(可选):用于指示当前的action类,使用的是action类的完全限定名;
# 3.method属性(可选):用于指示当前的action类中的哪个方法执行,方法名.

# 注意事项:
1. action标签必须写在package标签里面;
2. 在一个package里面可以写多个action标签;
3. 在一个action类里面可能有很多的方法,默认执行execute方法,执行其他的方法,使用action标签里面的属性 method配置,method没有配置时执行默认方法,当配置method属性,但action中没有对应的方法时,会报错。

struts.xml中result配置:

​ result标签是来配置逻辑视图(视图字符串)和物理视图之间的映射,result是配置在action中的,result有2个属性:

# 1.name属性(可选):是与action类的method方法的返回值进行匹配,来确定跳转路径,默认值是success;
# 2.type属性(可选):是用于指定跳转方式,默认是dispatcher(请求转发);

# result的type属性:有11个,常用的有4个:
1. dispatcher:默认值,代表的是请求转发,针对于jsp页面;
2. redirect:代表的是重定向,针对于jsp页面;
3. chain:类似于请示转发,只不过它是针对于action跳转;
4. redirectAction:类似于重定向,针对于action。

# 注意事项:
1. action的方法有返回值,配置返回值跳转到不同的页面中;
2. result标签里面有name属性,action里面的方法的返回值 /代表的是项目的根路径
    如:<result name="success">/success.jsp</result>
3. 在一个action标签里面写多个result标签,但result标签的name属性值不能相同。
4. 结果视图的跳转:重定向的方式在跳转的到的页面无法获取request域中存储的值。

​ 当有较多的action标签中都配置的result的跳转路径相同,则可以在struts.xml配置文件中配置全局的跳转路径:

<!-- 配置全局的结果页面,在struts.xml中package标签下配置 -->
<global-results>
    <!--name:逻辑视图的名称,type:跳转的方式,success.jsp:物理视图路径-->
    <result name="success" type="dispatcher">/success.jsp</result>
</global-results>
struts.xml配置文件中注意事项:
# package标签中可以配置:action interceptors global-results
三者是由顺序的:interceptors配置必须在package的最开始位置,否则报错。
2.2.3 struts2中常量配置

​ struts2中的有些功能是通过配置常量的形式实现的(如统一字符集编码),则根据项目需要,可能需要自定义常量或修改struts2中默认的常量。默认常量均是在default.properties中声明,实现自定义或修改默认常量有3中方式:

# 第一种:推荐使用  在项目的struts.xml中进行配置
<struts>
    <!--常量名称在默认常量中查找,值可以改变,常用的有两个-->
    <constant name="struts.devMode" value="true" /><!--配置当前是开发模式,可以显示更详细的错误信息,默认值false-->
    <constant name="struts.i18n.encoding" value="UTF-8" /><!--配置字符集编码,解决表单post提交时中文乱码问题,默认值就是UTF-8,默认就将post提交中文乱码问题解决-->
    <package name="default" namespace="/" extends="struts-default">
    ...
    </package>
</struts>
# 第二种:在src下面创建文件 struts.properties,可以模仿默认配置文件default.properties中定义常量
# 第三种:在web.xml进行配置,在StrutsPrepareAndExecuteFilter过滤器中配置初始化参数
<filter>
    <filter-name>struts2Filter</filter-name>
    <filter-class>
        org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    </filter-class>
    <!-- 可以声明struts2框架常量值-->
    <init-param>
        <param-name>struts.i18n.encoding</param-name>
        <param-value>GBK</param-value>
    </init-param>
</filter>
2.2.4 分模块开发配置

​ 在开发中按照模块来划分配置文件,可以将各模块的配置文件引入到struts.xml核心配置文件中,来实现各模块配置的分离。

# 实现步骤:
1. 创建配置文件,把配置写到这个文件中(约束不能省略的),就是一个完整的类似struts.xml配置文件;
2. 在struts.xml中把创建配置文件引入到里面,和package同级结构。
    <include file="文件的路径名"></include> 
    如:com.web.login包下的user.xml配置文件,引入方式是:<include file="com/web/login/user.xml" />

2.3 struts2中action

​ 每次访问action时,会默认执行action中的execute方法(Servlet中每次访问时都会执行service方法),当在action配置中配置了action中的具体方法,则针对的请求会执行对应的方法。

2.3.1 action的创建:
第一种方式:创建pojo类

Pojo(plain Ordinary java object):指的是简单的java对象,就是没有实现任何接口没有继承任何类。

// 案例:
// 自定义pojo类
public class Struts2Demo1 {
    public void show() {
        System.out.println("hello somnus....");
    }
}
// struts.xml配置
<action name="struts" class="com.web.struts.Struts2Demo1" method="show"></action> 
第二种方式:实现Action接口

Action接口中定义了5个常量和一个默认执行的方法。

# 在Action接口中定义了五个常量和一个execute方法:
五个常量:它们是默认的五个结果视图<result name=””>:
ERROR : 跳转到错误视图;
INPUT: 是struts2框架中interceptor中发现问题后会访问的一个视图,数据校验时会跳转的视图;
LOGIN:跳转到登录视图,可以在权限操作中使用;
NONE:代表的是null,不跳转页面;
SUCCESS:跳转到成功视图;

# 注意事项:
1. 在action的方法里面,如果返回值,必须在action标签写result标签进行配置,如果没有配置返回404页面。
2. action的方法不进行返回,不需要进行配置,但返回值要是none,否则显示404错误。
// 案例:
// 实现action接口
public class Struts2Demo1 implements Action {
    @Override
    public String execute() throws Exception {
        System.out.println("hello somnus....");
        return NONE; // 返回none代表不跳转
    }
}
// struts.xml中配置,不配置method属性,默认执行action中的execute方法
<action name="struts" class="com.web.struts.Struts2Demo1"></action> 
第三种方式:继承ActionSupport类–推荐方式

ActionSupport类本质是实现了Acting接口的类,继承该类不需要再实现Action中方法,并且还能访问到Action中定义的5个常量。并且该类还具有表单校验,错误信息设置等额外的功能。

// 案例:
// 实现action接口
public class Struts2Demo1 extends ActionSupport {
    @Override
    public String execute() throws Exception {
        System.out.println("hello somnus....");
        return NONE; // 返回none代表不跳转
    }
}
// struts.xml中配置,不配置method属性,默认执行action中的execute方法
<action name="struts" class="com.web.struts.Struts2Demo1"></action> 
三种方式实现方式比较:
# 方式1:创建pojo类
    优点:程序间无耦合;
    缺点:需要自身完成较多的功能;
# 方式2:实现Action接口
    优点:耦合度低;
    缺点:需要实现接口中所有的方法;
# 方式3:继承ActionSupport类  推荐使用方式
    优点:耦合度高;
    缺点:具有丰富的功能,例如:表单校验,错误信息设置,国际化等;
2.3.2 action中方法的访问

定义action中的方法是用来处理不同的请求,则访问action的方法有三种方式:

方式1:action配置中指定访问方法名:

​ 在action标签,使用method属性进行配置,在method属性值写action里面要执行的方法名称,,通过method来指定访问的方法,如果method没有没配置,默认访问的是execute方法。如果method属性配置了,但在action类中没有对应的方法,则会报错。缺点是:每一个方法访问,都需要配置一个action标签,当action中方法较多时,配置的action较多。

<package name="default" namespace="/" extends="struts-default">
  <action name="show1" class="com.web.struts.Struts2Demo1" method="show1">
    <result name="success" type="dispatcher">/success.jsp</result>
    <result name="failer" type="redirect">/failer.jsp</result>
  </action>
  <action name="show2" class="com.web.struts.Struts2Demo1" method="show2">
    <result name="success" type="dispatcher">/success.jsp</result>
    <result name="failer" type="redirect">/failer.jsp</result>
  </action>
</package>

方式2:使用通配符进行配置访问:使用通配符* 方法的通配 –推荐方式

​ 在action标签的name属性使用通配符,可以匹配多个方法,要求是命名规范要统一。不建议使用较多的*进行通配,否则会降低程序的阅读性。

实现通配注意事项:
1. 在action标签name属性,name属性值写 符号 * 星号
1. * 星号匹配任意内容
案例:
*代表的是通配方法名,{1}代表的是第一个*,可以配置多个*
<action name="book_*" class="com.web.struts.BookAction" method="{1}">
    <result name="success" type="dispatcher">/success.jsp</result>
    <result name="failer" type="redirect">/failer.jsp</result>
</action>
当请求访问:localhost:8080/struts2/book_add   -->会通配到book_*,相当于是*代表add,则要执行的方法就是add
当请求访问:localhost:8080/struts2/book_del   -->会通配到book_*,相当于是*代表del,则要执行的方法就是del

方式3:使用配置动态访问:

​ 需要开启动态访问的常量<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>,默认是false关闭的。在struts.xml中配置该action,action只能配置name和class属性,不能配置method属性。

<!--struts.xml文件配置-->
<!--开启动态访问-->
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<package>
<!--配置action的访问-->
<action name="book" class="com.web.struts.BookAction">
    <result name="success" type="dispatcher">/success.jsp</result>
    <result name="failer" type="redirect">/failer.jsp</result>
</action>
</package>
当请求访问:localhost:8080/struts2/book!add  --会执行action中的add方法
当请求访问:localhost:8080/struts2/book!del  --会执行action中的del方法

注意事项:动态方法调用,可能会和action标签中通过*配置的方式相冲突。

2.4 action获取表单中的数据

​ struts2中,Action没有直接和Servlet API进行耦合,则struts2中action不能直接访问Servlet API中的方法,但是在实际开发中要获取前台页面传递的传输数据,必须通过Servlet的相关的API(request,session和appliance域)才能获取数据,struts2框架将这些与Servlet相关的API调用进行了封装,在struts2中调用Servlet的API有如下的三种方式:

2.4.1 通过ActionContext类获取

​ struts2框架中的ActionContext类是Action执行的上下文对象(其生命周期和Action相同,就是为Action保存数据的),在ActionContext中保存了Acting执行逻辑所需要的所有对象,包括parameters,request,session,application等,其实ActionContext对象中的方法时模拟了Servlet中域对象方法的特点,能够获取到相应的数据。常用API有:

方法名称方法说明
void put(String key,Object value)将键值对数据存储在ActionContext中,代替Servlet中的HttpServletRequest的setAttribute方法
Object get(String key)获取ActionContext中指定名称的数据,代替Servlet中的HttpServletRequest的getAttribute方法
Map< String,Object> getApplication()获取应用级的Map对象,类似于Servlet中的ServletContext
Map< String,Object> getParameters()获取包含所有请求参数数据的Map集合,代替Servlet中的HttpServletRequest的getParameterMap方法
Map< String,Object> getSession()获取Map集合类型的HttpSession对象
void getApplication(Map< String,Object> application)设置application的上下文
void getSession(Map< String,Object> session)设置Map类型的session值
static ActionContext getContext()返回当前线程中的ActionContext对象(在action中获取ActionContext的方式)

案例:获取请求的参数

// 自定义action
public class LoginAction extends ActionSupport {
    public String execute() {
        // 获取ActionContext对象
        ActionContext context = ActionContext.getContext();
        // 获取所有的参数
        Map<String, Object> parameters = context.getParameters();
        // 变量参数数据
        Set<String> keys = parameters.keySet();
        for (String key : keys) {
            // 获取具体的参数
            String[] value = (String[]) parameters.get(key);
            System.out.println(key+"--"+Arrays.toString(value));
        }
        return SUCCESS;
    }
}
2.4.2 通过ServletActionContext类获取

​ struts2框架还提供了ServletActionContext用于访问Servlet的API,该类中均是静态的方法,用于获取原始的Servlet中的对象。常用API有:

方法名称方法说明
static HttpServletRequest getRequest()获取本次请求的HttpServletRequest对象
static HttpServletResponse getResponse()获取本次请求的HttpServletResponse对象
static HttpServletContext getServletContext()获取应用级的ServletContext对象
static PageContext getPageContext()获取页面的PageContext对象

案例:获取请求参数

// 自定义action
public class LoginAction extends ActionSupport {
    public String execute() {
        // 通过ServletActionContext获取HttpServletRequest对象
        HttpServletRequest request = ServletActionContext.getRequest();
        // 获取指定名称的参数
        String username = request.getParameter("username");
        // 获取所有参数的Map集合形式
        Map<String, String[]> parameterMap = request.getParameterMap();

        // 向域对象中存储数据
        request.setAttribute("username", "somnus");
        request.getSession().setAttribute("username","somnus");
        ServletActionContext.getServletContext().setAttribute("username", "somnus");
        return SUCCESS;
    }
}

注意事项:struts2中通过配置常量的方式<constant name="struts.i18n.encoding" value="UTF-8" /> 解决了表单提交中文数据乱码问题,则在使用获取的request对象获取post方式提交的参数时,不用手动设置编码来解决中文参数乱码问题。

2.4.3 通过特定接口访问–注入方式

​ struts2中在action执行过程中,会执行一个系统默认的拦截器servletConfig,该拦截器会判断该action是否实现了指定的接口,如果实现了指定接口就返回对应的Servlet API对象。常用的有如下几个:

接口名称说明
ServletRequestAware实现该接口的action可以拥有HttpServletRequest对象
ServletResponseAware实现该接口的action可以拥有HttpServletResponse对象
SessionAware实现该接口的action可以拥有HttpSession对象
ServletContextAware实现该接口的action可以拥有ServletContext对象

案例:获取请求的参数

public class LoginAction extends ActionSupport implements ServletRequestAware {
    // 定义HttpServletRequest变量
    private HttpServletRequest request;
    @Override
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }
    public String execute() {
        // 获取指定名称的参数
        String username = request.getParameter("username");
        // 获取所有参数的Map集合形式
        Map<String, String[]> parameterMap = request.getParameterMap();

        // 向域对象中存储数据
        request.setAttribute("username", "somnus");
        return SUCCESS;
    }
}

2.5 struts2封装数据

​ 在前台页面提交请求到服务器时,需要对请求中提交的参数进行封装,以便于数据传递,在struts中既可以使用原始Servlet的方式封装数据,也可以使用struts2提供的封装数据的方式封装数据。在struts2中提供两类三种数据的封装形式:属性驱动封装数据(两种方式)和模型驱动封装数据。

2.5.1 传统方式封装数据

​ 通过在action中获取Servlet中的request对象,然后进行数据的封装。案例如下:

// 自定义action并封装数据
public class LoginAction extends ActionSupport {
    public String execute() {
        // 通过ServletActionContext获取HttpServletRequest对象
        HttpServletRequest request = ServletActionContext.getRequest();
        // 获取指定名称的参数并将数据封装javabean中
        User user = new User();
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        user.setUsername(username);
        user.setPassword(password);
        // 其他逻辑操作
        return SUCCESS;
    }
}
2.5.2 属性驱动方式封装数据

​ 属性驱动方式封装数据方式1:在action的成员位置声明要封装参数的变量,声明的字符串类型,要求是表单字段的name属性值和声明的变量名称相同,然后提供成员变量的get/set方法,则struts2框架会自动将提交的参数数据封装到名称相同的变量中(其原理是使用Java的内省机制进行数据的设置)。案列如下:

//  action类
public class LoginAction extends ActionSupport {
    // 声明属性变量
    private String username;
    private String password;
    // 提供get/set方法
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String login() {
        // 打印参数
        System.out.println(username+"---"+password);
        // 其他逻辑操作
        return SUCCESS;
    }
}
// 前端页面:
<form action="${pageContext.request.contextPath}/login" method="post">
    username:<input type="text" name="username" />
    password:<input type="text" name="password" />
    <input type="submit" value="login"/>
</form>

​ 属性驱动方式封装数据方式1:在action的成员位置声明要封装javabean对象变量,要求是表单字段的name属性值和声明的javabean中属性名称对应相同,然后提供javabean变量的get/set方法,并且在在前端页面中javabean.属性名方式什么字段的name属性值,则struts2框架会自动将提交的参数数据封装到名称相同的变量中(其原理是使用Java的内省机制进行数据的设置)。案例如下:

// action类
public class LoginAction extends ActionSupport {
    // 声明javabean类型属性变量
    private User user;
    // 提供get/set方法
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public String login() {
        // 打印参数
        System.out.println(user.getUsername()+"---"+user.getPassword());
        // 其他逻辑操作
        return SUCCESS;
    }
}
// 前端页面
<form action="${pageContext.request.contextPath}/login" method="post">
    username:<input type="text" name="user.username" />
    password:<input type="text" name="user.password" />
    <input type="submit" value="login"/>
</form>

以上两种方式封装数据的优缺点:

​ 第一种:操作比较简单,但在将web层数据向Service层传递时需要将action的属性在赋值给模型(javaBean)去操作后再进行传递参数。

​ 第二种:不需要在直接将值给javaBean过程,因为直接将数据封装到了javaBean中。但是它要求在页面上必须使用特殊的表达式才能进行数据的封装,则造成前端页面不通用(当封装数据模式发生变化,需要更改前端的页面字段的name属性值)。

2.5.3 模型驱动方式封装数据

​ 模型驱动封装数据:是将数据直接封装到javabean对象中,此时需要action实现ModelDriven接口,并在action成员位置定义封装数据的javabean对象(必须创建对象,否则会报错),并在重写ModelDriven的getModel方法中将定义的javabean对象直接返回(封装数据的本质还是通过内省机制调用javabean对象属性的get/set方法进行封装数据,类似于BeanUtils工具类封装数据)。要求:实体javabean类中的属性必须和表单中对应字段的namg属性一致。案例如下:

// action类
public class LoginAction extends ActionSupport implements ModelDriven<User> {
    // 声明javabean类型属性变量
    private User user = new User();
    @Override
    public User getModel() {
        return user;
    }
    public String login() {
        // 打印参数
        System.out.println(user.getUsername()+"---"+user.getPassword());
        // 其他逻辑操作
        return SUCCESS;
    }
}
// 前端页面
<form action="${pageContext.request.contextPath}/login" method="post">
    username:<input type="text" name="username" />
    password:<input type="text" name="password" />
    <input type="submit" value="login"/>
</form>

属性驱动和模型驱动封装数据对比:对于模型驱动它与属性驱动对比,对jsp页面中表单的name属性没有特殊要求,在实际开发中使用比较多,模型驱动缺点,它只能对一个模型数据进行封装,当接收的参数中涉及到多个模型时,无法直接对属于各个模型的数据进行封装。此时可以考虑使用传统的方式进行多个javabean对象数据的封装。

注意事项:在一个action中对表单是数据进行封装时,可以使用属性驱动或模型驱动,但是两者不能同时使用来封装相同的数据,如果同时使用来封装相同的数据,会优先使用模型驱动封装数据,属性驱动中是获取不到数据的。

2.5.4 封装数据到集合中

封装数据到List集合中:使用属性驱动封装:使用模型驱动无法获取值(还没解决)

public class LoginAction extends ActionSupport {
    // 声明javabean类型属性变量
    private List<User> list = new ArrayList<User>();
    public List<User> getList() {
        return list;
    }
    public void setList(List<User> list) {
        this.list = list;
    }
    public String login() {
        // 打印参数
        for (User user : list) {
            System.out.println(user.getUsername()+"---"+user.getPassword());
        }
        // 其他逻辑操作
        return SUCCESS;
    }
}
// 前端页面:
<form action="${pageContext.request.contextPath}/login" method="post">
    <!-- 
        list集合:
        list[0]代表的是List集合中第一个元素,list中存储的user对象
        list[0].username代表的是List集合中第一个元素user对象的username属性
     -->
    username:<input type="text" name="list[0].username" />
    password:<input type="text" name="list[0].password" />
    <br>
    username:<input type="text" name="list[1].username" />
    password:<input type="text" name="list[1].password" />
    <input type="submit" value="login"/>
</form>

封装数据到Map集合中:使用属性驱动方式

// action类
public class LoginAction extends ActionSupport{
    // 声明javabean类型属性变量
    private Map<String, User> map = new HashMap<String, User>();
    // 提供get/set方法
    public Map<String, User> getMap() {
        return map;
    }
    public void setMap(Map<String, User> map) {
        this.map = map;
    }
    public String login() {
        // 打印参数
        for (String key : map.keySet()) {
            System.out.println(map.get(key).getUsername()+"---"+map.get(key).getPassword());
        }
        // 其他逻辑操作
        return SUCCESS;
    }
}
// 前端页面:
<form action="${pageContext.request.contextPath}/login" method="post">
    <!-- 
        map集合:
        map['abx']代表的是abx是key,整体代表的是Map集合中第一javabean对象
        Map集合中的key可以随意指定
     -->
    username:<input type="text" name="map['abx'].username" />
    password:<input type="text" name="map['abx'].password" />
    <br>
    username:<input type="text" name="map['somnus'].username" />
    password:<input type="text" name="map['somnus'].password" />
    <input type="submit" value="login"/>
</form>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值