Struts2框架入门

目录

Struts2

一、百度百科定义

Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。其全新的Struts2的体系结构与Struts 1的体系结构差别巨大。Struts2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts2可以理解为WebWork的更新产品。虽然从Struts 1到Struts 2有着太大的变化,但是相对于WebWork,Struts2的变化很小。

Struts2是一个基于MVC设计模式的WEB层框架
Struts2的内核相对于Struts1来讲已经发生巨大变化


首先了解一个概念,前端控制器模型(用一个过滤器Filter来实现):从客户端向服务器发送的所有请求,都需要经过前端控制器,然后前端控制器再根据具体的请求所要实现的功能分发到不同的Action去处理

二、Struts2 的入门

下载Struts2的开发环境:Struts2的官网
Struts2开发包:在这里插入图片描述

  1. apps:Struts2提供的应用,war文件:web项目打包成war包,可以直接放入tomcat,tomcat会自动识别
  2. docs:Struts的开发文档和API
  3. lib:Struts2框架开发的jar包
  4. src:Struts2的源码

所需jar包:apps下的struts2_blank下寻找

三、Struts2的执行流程

  1. 客户端向服务端发起请求
  2. 先经过核心过滤器StrutsPrepareAndExecuteFilter,Prepare对应的是过滤器内的init方法,init方法执行一些初始化操作,Execute对应过滤器内的doFilter方法,doFilter方法用于操作,其中在核心过滤器中有个ActionMapper,服务器初始化的时候会加载配置文件,加载配置文件的时候ActionMapper会创建一个类似map结构的对象,将配置文件中的action的name属性跟class进行绑定并存入这个对象中,当访问Action的时候会执行过滤器,过滤器经过很多步骤之后创建了一个Action代理,执行Action代理中的方法,这个方法会执行一系列invoke方法和拦截器,当拦截执行完毕后,过滤器再反射寻找到这个Action,执行Action中对应的方法,再经过一系列拦截器后,执行页面的跳转。在核心过滤器的内部会执行一组拦截器,Struts2的功能都是由拦截器来实现的
  3. 根据struts.xml配置文件去寻找action,跟package标签下的action下的name属性相对应,在找到action下的class属性,根据这个属性寻找到类,反射执行类里面的execute方法
  4. 返回一个String类型的值,这个值跟action下的result标签下的name属性相对应,然后进行一个页面的跳转,跳转到result标签内的值

客户端向服务器发送一个Action的请求,执行核心过滤器(doFilter)方法。在这个方法中,调用executeAction方法,在这个方法中调用dispatcher.serviceAction();在这个方法内部创建了一个Action代理,最终执行的是Action代理中的execute方法,在代理中执行execute方法中,调用了ActionInvocation的invoke方法,在invoke方法内部递归执行一组拦截器(完成部分功能),如果没有下一个拦截器,就会执行目标Action,根据Action的返回的结果进行页面跳转。

四、Struts2配置文件的加载顺序

服务器一启动,过滤器就会被创建,过滤器中的init方法就会执行

   init_DefaultProperties(); // [1]加载属性配置
   init_TraditionalXmlConfigurations(); // [2]加载struts-default.xml、struts-plugin.xml、struts.xml
   nit_LegacyStrutsProperties(); // [3]加载struts.properties(自定义的配置文件)
   init_CustomConfigurationProviders(); // [4]加载配置提供类
   init_FilterInitParameters() ; // [5]加载web.xml中过滤器初始化参数
   init_AliasStandardObjects() ; // [6]加载Bean对象

tips:后配置的常量的值会覆盖先配置的常量的值

五、配置核心过滤器

要使用struts2的话,都需要到项目的web.xml中配置一个核心过滤器。到web.xml中,添加如下代码即可。

<!-- 配置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>

六、struts.xml的配置

(一)package元素的配置

package标签称为包,这个包与java中的包不一致。这个包是为了更好地管理action的配置
package标签的属性:

  • name:包的名称,只要在一个项目中不重名就好了。
  • extends:继承哪个包,通常值为struts-default
  • namespace:名称空间,与action标签中name属性共同决定访问路径
    名称空间有三种写法:带名称的名称空间:namespace=“/a”
    跟名称空间:namespace="/"
    默认名称空间:namespace=""
  • abstract: 抽象的,用于其他包的继承
<?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="" extends="" namespace="">
		<!-- 配置Action -->
		<action name="" class="">
			<!-- 配置页面的跳转 -->
			<result name=""></result>
		</action>
	</package>
</struts>

(二)action标签的配置

action标签用于配置Action类
action标签中常用的属性

  • name :与namespace共同决定访问路径
  • class:Action类的全路径
  • method:执行Action中的哪个方法的方法名,默认值execute
  • converter:自定义一个类型转换器,一般不用这个属性,struts2内部提供的类型转换器一般够用

(三)Struts2常量的配置(struts2-core-2.3.24包下的org.apache.struts2下的default.properties)

在这里插入图片描述
设置字符集编码,Sturts2中所有的post请求的中文乱码都不用手动解决:

struts.i18n.encoding=UTF-8

Struts2请求的默认的扩展名。默认扩展名是.action或者无

struts.action.extension=action,,

(四)Struts2中修改常量的值

修改常量值,可通过三个地方进行修改

  1. 在struts.xml进行修改
    通过配置constant标签,来修改常量的值
  • name属性:常量的key
  • value属性: 常量的值
<?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="" value=""></constant>
   <package name="" extends="" namespace="">
		<!-- 配置Action -->
		<action name="" class="">
			<!-- 配置页面的跳转 -->
			<result name=""></result>
		</action>
	</package>
</struts>
  1. 在struts.properties中进行修改
  • 在src下新建一个struts.properties文件,专门用来修改常量,将键值对输入即可
  1. 在web.xml中进行修改
  • 通过过滤器的初始化参数来修改
 <filter>
  	<filter-name>struts2</filter-name>
  	<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  <init-param>
  	<param-name></param-name>
  	<param-value></param-value>
  </init-param>
  </filter>

如果三种方法同时修改了同一个常量,最终会生效的是web.xml,这跟配置文件的加载顺序有关,习惯上修改struts.xml

(五)分模块开发的配置

  • include配置,在一个配置文件中引入其他配置文件
<include file="">

七、Action的写法(3种)

  • Action类是POJO的类,没有继承任何类,没有实现任何接口

POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。
使用POJO名称是为了避免和EJB混淆起来, 而且简称比较直接. 其中有一些属性及其getter setter方法的类,没有业务逻辑,有时可以作为VO(value -object)或dto(Data Transform Object)来使用.当然,如果你有一个简单的运算属性也是可以的,但不允许有业务方法,也不能携带有connection之类的方法。

企业级JavaBean(Enterprise JavaBean, EJB)是一个用来构筑企业级应用的服务器端可被管理组件。
Java企业版API(Java Enterprise Edition)中提供了对EJB的规范。EJB是一个封装有某个应用程序之业务逻辑服务器端组件。EJB最早于1997年由IBM提出,旋即被太阳微系统采用并形成标准(EJB 1.0 和EJB 1.1)。其后在Java社区进程(Java Community Process)支持下陆续有一些JSR被制订来定义新的EJB标准,分别是JSR 19 (EJB 2.0), JSR 153 (EJB 2.1) 以及最新的JSR 220 (EJB 3.0)。

这种写法只需要提供一个返回String类型的值(用于跳转页面)的execute方法

  • Action类实现一个Action的接口

这种写法需重写execute方法,接口中提供了五个常量(五个逻辑视图的名称)

  1. SUCCESS:成功(值可修改)
  2. ERROR:失败(值可修改)
  3. LOGIN:登录出错页面跳转(值可修改)
  4. INPUT:表单校验时出错(值不可修改)
  5. NONE:不跳转
  • Action类继承ActionSupport类(推荐使用)

这种方法也得重写execute方法 。ActionSupport中提供了数据校验,国际化等一系列操作的方法。

八、Action的访问

  • 通过method配置

在配置struts.xml文件中,配置action标签的时候,配置多一个method,可以访问到相应的类里面的方法

<action name="" class="" method="">
  • 通过通配符配置

访问连接时指定路径为一定格式,例如
<a href="${pageContext.request.contextPath }/action_find.action">
然后在struts.xml配置action标签的时候name属性写为action_*,method属性写第通配符的位置对应的数字,代表第几个*号的位置对应的值,例如

<action name="action_*" class="struts.demo1.HelloAction" method="{1}">

通用写法:

<action name="*_*" class="包名.{1}"  method="{2}"></action>
  • 动态方法访问

在struts.xml 中配置常量struts.enable.DynamicMethodInvocation = true

访问连接的格式为:action的name属性 + ! + .action(后缀名)
<a href="${pageContext.request.contextPath }/action!find.action">

九、Struts2访问Servlet的API方式

  • 完全解耦合的方式(完全看不见request,response,application,session这些对象的出现)

利用Struts2中的对象ActionContext对象

ActionContext context = ActionContext.getContext();

ActionContext里面有许多方法,比如:

getParameters,类似request.getParameterMap
put(String key,Object value),把值存到域对象里,相当于request.setAttribute
getSession,获得一个map集合,集合内存放的数据是存放在session内的数据,context.getSession().put()相当于session.setAttribute()
getApplication,获得一个map集合,集合内存放的数据是存放在application内的数据,context.getApplication.put(),相当于application.setAttribute()
缺点:不能操作request,session,application这类对象的一些方法,只能获得存储在这些对象内的数据(map集合)

  • 使用Svlet的API的原生方式(直接获取reqeust,session,response这类对象,然后直接调用这些对象里面的方法)

通过ServletActionContext对象,获得request等对象

HttpServletRequest request = ServletActionContext.getRequest();

通过这种方法就可以使用request等对象内的方法

  • 接口注入的方式

Action类要实现一个ServletRequestAware接口,并实现setServletRequest方法

public void setServletRequest(HttpServletRequest request) {
		this.request = request;
	}

实现这个方法后可以向request跟session内存值

request.setAttribute("","");
request.getSession().setAttribute("","");

实现ServletContextAware接口,可以向application存放数据

public void setServletContext(ServletContext context) {
		this.context = context;
	}
context.setAttribute("","");

还有一些其他的接口,就不一一赘述了,毕竟这种方法比较麻烦


Servlet是单例的,多个程序访问同一个Servlet只会创建一个Servlet的实例。Action是多例的,一次请求就会创建一个Action的实例,不会出现线程安全的问题。

十、Struts2的结果页面的配置

  1. 全局结果页面

在包中配置一次,其他的在这个包中的action只要返回了这个值,都可以跳转到这个页面,针对这个包下的所有action的配置都有效。
在package标签下配置

<global-results>
		<result name=""></result>
</global-results>

如果既配置了全局结果页面又配置了局部结果页面,那么会跳转到局部结果页面

  1. 局部结果页面
    只能在当前action中的配置有效

十一、result标签的配置

  • name属性:逻辑视图的名称,通过这个name就可以找到要跳转的页面。默认值是success,如果execute方法返回success的话可以不写那么属性
  • type属性:页面跳转的类型(可在核心包中的struts-default.xml查找)
    常用的几个:
    1.dispatcher:默认值,请求转发(Action转发到JSP)
    2.redirect:重定向(从一个Action重定向到JSP)
    3.chain:转发(从一个Action转发到另一个Action)
    4.redirectAction:重定向(从一个Action定向到另一个Action)如果在同一个名称空间下的话可以直接写xxx.action,如果不在同一个名称空间下的话,还需要在result标签下配置两个标签
<result name="" type="redirectAction">
			    <param name="namespace">/xxx</param>
			    <param name="actionName">xxx.action</param>
</result>

5.stream:Struts2中提供文件下载的功能

十二、Struts2数据的封装

(一)属性驱动:提供属性set方法的方式

  • 新建一个javaBean,在Action类中提供这个javaBean的属性变量以及它们的set方法,在Action类中的execute方法中可以获取到jsp页面传递过来的这些变量值,再新建一个此javaBean实例进行手动封装
  • 缺点:手动封装属性到实例中比较麻烦
  • 优点:Struts2自动帮我们进行了数据的类型转换

(二)属性驱动:页面中提供表达式的方式(核心包中struts-default.xml中拦截器params、conversionError完成类型转换以及数据封装)

  • 新建一个javaBean,在Action类中提供这个javaBean(声明一个javaBean)以及它的的get、set方法
  • 注意:一定要提供get方法,如果没有提供get方法,拦截器就会帮我们把创建很多个javaBean实例,并把每个属性分别放入这个javaBean中,并返回一个javaBean对象,简而言之,拿到的数据不完整

例如:private User user ; public User getUser() { return user; } public void setUser(User user) { this.user = user; }

在jsp页面中input标签的name属性中值的写法为javaBean的变量名.javaBean对应的属性名(Struts2内部提供的OGNL表达式)即可

  • 可以向多个对象同时封装数据

(三)模型驱动:采用模型驱动方式(核心包中struts-default.xml中拦截器modelDriven完成的)

  • Action类实现一个ModelDriven类,手动实例化一个javaBean对象,实现一个getModel()方法,返回上面实例化的javaBean对象
  • 缺点:只能同时向一个对象中封装数据

十三、INPUT逻辑视图的配置

先看一个东西,struts-default.xml里的一段代码

 <interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="datetime"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="deprecation"/>
            </interceptor-stack>

这一段代码表示的时默认栈内的拦截器,这些拦截器在使用Struts2框架的时候都会依次执行。
Strut2框架提供了数据存储的区域,这块区域中有一块存储错误信息的区域,以上的拦截器在执行过程中如果产生了错误,会向这块区域中存储错误信息。

 <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
 </interceptor-ref>

这个拦截器会检查错误区域是否有错误信息,如果没有,直接跳转到目标 Action,如果有,则会跳转到INPUT逻辑视图

因此需要在action中配置增加一个name属性为input的result标签,可跳转到指定页面,否则到报错界面

如果想要把错误信息打印到跳转到页面上,可以引入以下代码在jsp页面的顶部,并在想要显示错误信息的地方加上标签,如:

<%@ taglib uri="/struts-tags"  prefix="s"%>

在某一处加上此类标签:

<s:fielderror></s:fielderror>

十四、Struts2的复杂类型的数据封装

(一)同时把多个数据存储到List集合中

  • 在Action类中声明一个List变量并提供它的set方法和get方法
  • 在jsp页面配置input的name属性的时候,值的书写格式为List变量名[数字].存入List的数据项的属性名

(二)同时把多个数据存储到map集合中

  • 在Action类中声明一个map变量并提供它的set方法和get方法
  • 在jsp页面配置input的name属性的时候,值的书写格式为Map变量名[key(自定义)].存入Map的数据项的属性名

十五、OGNL

(一)什么是OGNL

对象导航图语言(Object Graph Navigation Language),简称OGNL,是应用于Java中的一个开源的表达式语言(Expression Language),它被集成在Struts2等框架中,作用是对数据进行访问,它拥有类型转换、访问对象方法、操作集合对象等功能。

OGNL是第三方的表达式语言,不在Struts2里也能使用

(二)OGNL使用的要素

  • 表达式
  • 根对象
  • Context对象

(三)OGNL在java环境下的使用

导入ognl.jar包之后

(1)访问对象的方法
//获得context对象
Ognl context = new OgnlContext();
//获得根对象
Object root = context.getRoot();
//执行表达式,访问对象的方法,输出hello字符串的长度
Object obj = Ognl.getValue("'hello'.length()",context,root);
System.out.println(obj);
(2)访问对象的静态方法
//获得context对象
Ognl context = new OgnlContext();
//获得根对象
Object root = context.getRoot();
//执行表达式,@类名@方法名,输出一个随机数
Object obj = Ognl.getValue("@java.lang.Math@random()",context,root);
System.out.println(obj);
(3)获得Root中的数据,不需要加#
//向root里边存值
context.setRoot(new User("aaa","123"));//提前创建一个User类,属性为username和password
//获得context对象
Ognl context = new OgnlContext();
//获得根对象
Object root = context.getRoot();
//执行表达式
Object username = Ognl.getValue("username",context,root);
(4)获得OgnlContext中的数据
//获得context对象
Ognl context = new OgnlContext();
//获得根对象
Object root = context.getRoot();
//向context内存数据
context.put("name","张三");
//执行表达式
Object username = Ognl.getValue("#name",context,root);

(四)OGNL在Struts2环境的使用

导入ognl.jar包,在jsp页面顶部引入

<%@ taglib uri="/struts-tags" prefix="s" %>
(1)调用对象的方法

使用property标签

<s:property value="'hello'.length()"/>
(2)调用对象的静态方法

静态方法访问在Struts2中默认是关闭的,需要我们手动去开启一个常量
在struts.xml中struts标签下加入一段代码

<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
<s:property value="@java.lang.Math@random()"/>

(五)OGNL表达式中的特殊字符

(1)#
①获取context的数据

后面有提到

②使用#号构建map集合
//构建一个list集合
<s:property value="{'aa','bb','cc'}"/>
//遍历list集合
 <s:iterator  var="i" value="{'aa','bb','cc'}">
 	<s:property value="i"/>//或者<s:property value="#i">
 </s:iterator>
//构建一个map集合
<s:property value="#{'aa':'11','bb':'22','cc':'33'}"/>
 //遍历map集合第一种方式
 <s:iterator  value="#{'aa':'11','bb':'22','cc':'33'}">
 	<s:property value="key"/>
 	<s:property value="value"/>
 </s:iterator>
 //遍历map集合第二种方式,只要定义了var,该变量在context就会有一份,则可以通过#取出值
 <s:iterator var="i" value="#{'aa':'11','bb':'22','cc':'33'}">
	<s:property value="#i.value"/><br>
	<s:property value="#i.key"/><br>
</s:iterator>
③使用#号可以简化一些表单标签的写法

例如:

性别:<input type="radio" name="sex1" value="男">男
	<input type="radio" name="sex1" value="女">女<br>
性别:<input type="radio" name="sex1" value="1">男
	<input type="radio" name="sex1" value="2">女<br>

可以写成

//value跟外面的值相同使用list集合
<s:radio list="{'男','女'}" name="sex2" label="性别"></s:radio>
//value跟外面的值不同使用map集合
<s:radio list="#{'1':'男','2':'女'}" name="sex2" label="性别"></s:radio>
(2)%

Struts2有些标签不解析ognl表达式,会将ognl表达式识别成普通的字符串,例如下面的代码,先向request存入name属性,再使用ognl表达式取出name的值,但是此时并没有将#request.name识别成ognl表达式

<%request.setAttribute("name","张三"); %>
<s:textfield name="name" value="#request.name"></s:textfield>
①强制解析OGNL

使用%可强制解析OGNL表达式,取出name属性的值

<%request.setAttribute("name","张三"); %>
<s:textfield name="name" value="%{#request.name}"></s:textfield>
②强制不解析OGNL(应用较少)

使用%也可强制不解析OGNL表达式,获取到的就是字符号串#request.name

<s:property value="%{'#request.name'}"/>
(3)$
①在配置文件中使用OGNL
  • 属性文件(少用)
    在properties文件中有可能会用到一些变量,例如
#session.user.username

此时会识别成字符串,加上$符之后才能识别为变量

${#session.user.username}
  • xml文件
    做文件下载效果时会用到
 <action name="download" class="xxx.DownloadAction">
	        <result type="stream">
				<param name="Content-Type">文件类型</param>	            
				<param name="Content-Disposition">attachment;filename=${文件名}</param>	            
	        </result>
</action>

十六、值栈

(一)什么是值栈

Struts2将XWork对Ognl的扩展这一套机制封装起来,这个对象叫ValueStack。
ValueStack实际上就是一个容器。它由Struts框架创建,当前端页面如jsp发送一个请求时,Struts的默认拦截器会将请求中的数据进行封装,并入ValueStack的栈顶。

Struts2在启动时,会创建一个ValueStack对象 当用户发送请求到对应的Action时,Struts2会把当前被请求的Action01放入CompoundRoot 对象的“栈空间”栈顶,请求结束,Action01会被清除。 (当下一次另一个请求到来时,Struts2会把该请求对应的Action02放入“栈顶”) 所以,我们可以通过Ognl表达式访问CompoundRoot对象栈顶的Action。
Struts2在请求到来时,首先会创建一个ValueStack; 然后,把当前的Action对象放入栈顶(CompoundRoot); Struts2会把ValueStack存放在request中,属性为”struts.valueStack“, 所以,标记库可以访问到ValueStack Struts2的很多标记就是通过访问ValueStack获得数据的:
 通过ognl从ValueStack取数据,并且显示
<s:property value=“ognl…”/>
 省略value,取出ValueStack的栈顶
<s:property />
 通过ognl从ValueStack取出集合,依次将集合中的对象置于栈顶,在循环中,ValueStack栈顶即为要显示的数据

  • ValueStack是Struts2的一个接口,OgnlValueStack是ValueStack的实现类,客户端发起一个请求struts2框架会创建一个action实例同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个Action的生命周期,struts2中使用OGNL将请求Action的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值
  • ValueStack类似于一个数据中转站,往里面存数据,在任何地方都能取数据,在action,jsp,配置文件中都可以取出

(二)值栈的内部结构

ValueStack中有两个主要的区域

  • root区域(CompoundRoot):其实就是一个ArrayList,存放的是一些java对象,获取root区域的对象不需要加#
  • context区域(OgnlContext):其实就是一个Map,其中有root,request,session,appication,parameters,attr(从小范围开始查找数据)等常用web对象数据的引用,获取context区域的对象需要加#

(三)值栈与ActionContext的关系

  • ActionContext:Action的上下文,当请求过来的时候,执行过滤器中doFilter方法,在这个方法中创建了ActionContext,在创建ActionContext过程中会创建ValueStack对象,将ValueStack对象传递给ActionContext对象,可以通过ActionContext来获取之值栈对象
  • ActionContext对象之所以能访问Servlet的API(访问的是域对象的数据),是因为在其内部有值栈的引用

(四)获得值栈对象

  • 通过ActionContext对象获取值栈
ValueStack valueStack = ActionContext.getContext().getValueStack();
  • 通过request对象获得,在Struts2底层,会将值栈对象存入到request中一份
ServletActionContext.getRequest().getAttribute("struts.valueStack")

通过上面两种操作获得的值栈是同一个

(五)操作值栈-向值栈中存入数据

一般指操作ValueStack中的root区域

  • 在Action中提供属性的get方法的方式,默认情况下,Struts2会将Action对象压入到值栈,Action的属性也会在值栈中
private User user;
	 
	public User getUser() {
		return user;
	}

	@Override
	public String execute() throws Exception {
		user = new User("张三","123");
		return SUCCESS;
	}

获取方式:<s:property value="user.username"/>

  • 使用ValueStack中本身的方法的方式
    push(Object obj)
    set(String key,Object obj)//创建一个Map集合,把map集合压入到栈顶
ValueStack valueStack = ActionContext.getContext().getValueStack();
User user = new User("张三","123");
//valueStack.set("name","张三");
valueStack.push(user);

获取方式:<s:property value="username"/>,<s:property value="name"/>先取栈顶的数据

(六)获取值栈数据

获取值栈数据就是在页面中使用OGNL表达式

(1)获取root区域的数据
  • 简单数据的获取看上面
  • 复杂数据的获取

先往值栈中存入数据:

	ValueStack valueStack = ActionContext.getContext().getValueStack();
		List<User> list = new ArrayList<User>();
		list.add(new User("张三","123"));
		list.add(new User("李四","123"));
		list.add(new User("王五","123"));
		valueStack.set("list", list);

取出:

<s:property value="list[0].username"/>
<s:property value="list[0].password"/><br>
<s:property value="list[1].username"/>
<s:property value="list[1].password"/><br>
<s:property value="list[2].username"/>
<s:property value="list[2].password"/>
(2)获取context的数据(不常用)

先往context中存入数据(其实就是向域对象存入数据):

//往request存数据
ServletActionContext.getRequest().setAttribute("name", "张三");
//往session存数据
ServletActionContext.getRequest().getSession().setAttribute("name", "李四");
//往application存数据
ServletActionContext.getServletContext().setAttribute("name", "王五");

取数据:

<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
//取到的数据是reqeust,如果request内没有数据则取session,session没有则取application
<s:property value="#attr.name"/>
//可以取到url传递过来的参数
<s:property value="#parameter.参数值"/>

(七)EL为何能访问值栈的数据

Struts2框架的底层对request.getAttribute(String name)方法进行了增强,先从request获取值,如果获取不到,则从值栈中获取值

ActionContext ctx = ActionContext.getContext();
        Object attribute = super.getAttribute(key);

        if (ctx != null && attribute == null) {
            boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));

            // note: we don't let # come through or else a request for
            // #attr.foo or #request.foo could cause an endless loop
            if (!alreadyIn && !key.contains("#")) {
                try {
                    // If not found, then try the ValueStack
                    ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);
                    ValueStack stack = ctx.getValueStack();
                    if (stack != null) {
                        attribute = stack.findValue(key);
                    }
                } finally {
                    ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
                }
            }
        }
        return attribute;
    }

十七、Struts2的拦截器

(一)什么是拦截器

interceptor:拦截器

java里的拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重用部分的方式。在AOP(Aspect-Oriented Programming)中拦截器用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。

(二)拦截器跟过滤器的区别

Filter:过滤器,过滤从客户端向服务器发送的请求。
interceptor:拦截器,拦截的是客户端对Action的访问

(三)自定义拦截器

(1)编写一个类实现interceptor接口或者继承AbstractInterceptor类
(2)对拦截器进行配置
  • 定义拦截器进行配置

在struts.xml中package标签下配置interceptors标签

<interceptors>
	        <interceptor name="interceptor1" class="interceptor.Interceptor1"></interceptor>
</interceptors>

定义完拦截器之后,需要在想要使用该拦截器的action标签中引入该拦截器,如果引入自定义的拦截器之后,Struts2默认拦截器栈拦截器就不执行了,需要再手动引入默认栈的拦截器

<action name="" class="">
		    <result></result>
		    <interceptor-ref name="interceptordemo1"></interceptor-ref>
</action>	  
  • 定义一个拦截器栈的方式

在struts.xml中package标签下配置interceptors标签

<interceptors>
	        <interceptor name="interceptor1" class="interceptor.Interceptor1"></interceptor>
</interceptors>

再在interceptors标签下定义一个拦截器栈

<interceptors>
	        <interceptor name="interceptor1" class="interceptor.Interceptor"></interceptor>
	<interceptor-stack name="myStack">
	        <interceptor-ref name="defaultStack"></interceptor-ref>
		    <interceptor-ref name="interceptor1"></interceptor-ref>
	 </interceptor-stack>
 </interceptors>

再在需要使用拦截器的action中引入拦截器栈

十八、Struts2的标签库

(一)通用标签库

在这里插入图片描述

(1)判断标签
  • <s:if test="">test里面放的是条件
  • <s:elseif test="">
  • <s:else test="">
(2)循环标签
<s:iterator><s:/iterator>
(3)其他常用标签
  • <s:property/>
  • <s:debug/>用于调试
  • <s:date/>在页面上进行日期格式化

(二)UI标签库(方便数据回显)

在这里插入图片描述

(1)传统表单跟UI表单对比
//传统表单
<form action="xxx.action" method="post">
	<input type="hidden" name="id">
	用户名:<input type="text" name="name"/><br>
	密码:<input type="password" name="password"/><br>
	年龄:<input type="text" name="age"><br>
	性别:<input type="radio" name="sex" value="男">男
	<input type="radio" name="sex" value="女">女<br>
	籍贯:<select name="city">
		<option value="">--请选择--</option>
		<option value="深圳">深圳</option>
		<option value="汕头">汕头</option>
	</select><br>
	爱好:<input type="checkbox" name="hobby" value="basketball">篮球
	<input type="checkbox" name="hobby" value="football">足球
	<input type="checkbox" name="hobby" value="volleyball">排球<br>
	介绍:<textarea name="info" rows="3" cols="6">xxx</textarea><br>
	<input type="submit" value="提交">
</form>
//UI表单
<s:form method="post" action="struts.xml中的action" namespace"/">
	<s:hidden name="id" value=""></s:hidden>
	<s:textfield name="name" label="用户名"></s:textfield>
	<s:password name="password" label="密码"></s:password>
	<s:textfield name="age" label="年龄"></s:textfield>
	<s:radio list="{'男','女'}" name="sex" label="性别"></s:radio>
	<s:select list="{'深圳','汕头'}" name="city" label="籍贯" headerKey="" headerValue="--请选择--"></s:select>
	<s:checkboxlist list="#{'basketball':'篮球' ,'football':'足球','volleyball':'排球'}" name="hobby" label="爱好"></s:checkboxlist>
	<s:textarea name="info" cols="6" rows="3" label="介绍" value="xxx"></s:textarea>
	<s:submit value="提交"></s:submit>
</s:form>

在这里插入图片描述
UI表单更整齐,因为UI表单有一个默认的样式,在Struts2常量处(default.properties)配置

struts.ui.theme=xhtml
struts.ui.templateDir=template
struts.ui.theme.expansion.token=~~~
struts.ui.templateSuffix=ftl

要修改UI标签的表单样式,可以在struts.xml配置常量,或者直接在表单行修改

<s:form method="post" action="" theme="">
	<s:textfield name="name" label="用户名"></s:textfield>
	<s:password name="password" label="密码"></s:password>
</s:form>

UI表单的好处:如果提交数据出错会回显数据,而且会告诉那个地方出错,密码默认不回显,如果要回显的话要在密码标签处加一个属性showPassword=“true”,而普通表单不会

回显的原因:值栈中会存有action,action的属性也会存入到值栈中去,如果使用了UI标签的话,会自动进行匹配,如果标签的name跟值栈中的相对应,就会自动回显数据

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值