Struts2框架学习

喵呜

了解Struts2

如何更好的理解Struts2框架

本文会先通过我们以往接触的JavaWeb的设计架构,从普通Web项目出发,理解MVC+三层架构为何如此设计,普通的Web项目,Servlet充当控制器有什么优缺点,进而更好的理解Struts2框架为何出现,Struts2框架的出现解决了以往的哪些问题。

为什么使用Struts2控制器

了解我们以前所编写的JavaWeb应用的缺点,明确Struts2框架的优点。
我们以前同样使用MVC+三层架构来完成Web应用,分析以前使用的Web项目的结构
明确JavaWeb当中,使用Servlet充当控制器的缺点
M(Model):模型 使用JavaBean来充当我们的模型
V(View):视图 使用jsp来充当我们的视图,显示数据给用户,就是页面
C(Controller):控制器 使用一个Servlet来作为控制器处理用户的请求,配合三层架构来实现业务逻辑

三层架构
数据访问、持久层 Dao层 用于访问数据库 ,是最底层的东东
业务逻辑层 service层 主要用于处理业务逻辑实现
数据显示层 显示数据

MVC与三层架构的设计中 控制器(C) 作为程序的中心,JavaBean(M)承载数据作为数据的模型,流通于各层之间,V作为页面交互显示数据的视图
而业务层service作为整个应用的逻辑的处理,它负责调用dao层来完成控制器的请求并返回给控制器,最后由控制器来将数据传递到视图中进行显示

控制器
在Web应用中,我们使用一个Servlet作为控制器,并且该控制器往往只有一个,它的功能就是接收用户通过浏览器发出的请求,并且在控制器中应对不同的请求创建对应的方法,在方法中在访问service层业务逻辑层,在由业务逻辑层访问dao层,一级一级的下发,最后完成用户的请求,返回给用户数据 。
在以往的案例中,针对不同的请求,我们就需要在控制器中定义不同请求所对应的方法,那么随着请求的不同的累加,在控制器中的方法也就会不断的创建,新增,最后就会给控制器造成方法过多、冗杂的问题,这时控制器的所负担的功能也会越来越大。

那么问题来啦,我们如何解决控制器中的方法过多的问题? 解决问题也就是Struts框架的由来

全新的一个MVC+三层架构 真正意义上的开发模型
在真正的开发模型中,我们的控制器中并不会出现过多的冗余的方法,取而代之的是我们在JavaBean中定义本应该出现在控制器的方法,如何注册用户Customer 我们在Web应用需要创建对应的JavaBean实体类 Customer 我们将本应该出现在控制器中的addCustomer()方法定义在对应的JavaBean中,方法的功能一样,只不过是出现在了JavaBean中,而控制器只需要调用JavaBean中的对应的方法来完成用户的请求即可

我们将JavaBean中的方法作为一种动作 它本身所具有的功能来体现,并且将这种体现来一个配置文件中进行描述,如:Customer对应的JavaBean中,定义了addCustomer()方法,这个方法就是JavaBean的动作 Action 我们将这个动作在配置文件中进行描述,并且配置这个动作的详细信息,而控制器的功能就是当用户发出请求时,我们获取配置文件中的动作信息,根据不同的动作信息来查找对应的JavaBean,在对应的调动JavaBean中的动作方法即可,这样就完成了用户的请求

学习Struts2框架

Struts2是在WebWork2基础发展而来的。和Struts1一样,Struts2也属于MVC框架。不过有一点大家需要注意的是,尽管Struts2和Struts1在名字上的差别不是很大,但Struts2和Struts1在代码编写风格上几乎是不一样的。那么既然有了Struts1,为何还要推出Struts2,主要是因为Struts有以下优点:

Struts的优点

1、在软件设计上,Struts2没有像Struts1那样跟Servlet API和Struts api有着紧密的耦合,Struts2的应用可以不依赖于Servlet API和Struts API。 Struts2的这种设计属于无侵入式设计,而Struts1却属于侵入式设计。

举例说明 在Struts1处理浏览器的请求,我们来实现一个Action抽象类的完成该功能,该抽象类中有一个抽象方法 execute(),该方法的参数中就严重依赖着servlet的API

public class OrderListAction extends Action
{
    public ActionForward execute(ActionMapping mapping,ActionForm form,HttpServletRequest request ,HttpServletResponse response)
    {
    
    }
}

2、Struts2提供了拦截器(Interceptor),利用拦截器可以进行AOP编程 面向切面编程 实现如权限拦截等功能。
3、Struts2提供了类型转换器(Conversion),我们可以把特殊的请求参数转换成需要的类型。在Struts1中,如果我们要实现同样的功能,就必须向Struts1的底层实现BeanUtils注册类型转换器才行。
4、Struts2提供支持多种表现层技术,如:JSP、freeMarker、Velocity等。
5、Struts2的输入校验(validate)可以对指定方法进行校验,解决了Struts1的长久之痛
6、提供了全局范围、包范围和Action范围的国际化资源文件管理实现

搭建Struts2开发环境

在web应用下导入Struts2所需要的jar包 编写Struts2所需的配置文件 在web.xml文件中对Struts2的核心控制器进行描述映射

1、导入Struts2所需要的jar包

不同版本的Struts框架所需要的支持的jar包也不同,具体的我们需要看Struts2不同版本的文档所决定
每个Struts2框架都提供了很多jar包,这些jar包中包括了很多插件,我们搭建开发环境导入最少的支持Struts运行的jar包即可,以后用到什么功能在继续导入即可

以2.1.7为例 最少需要的jar包为7个

struts的核心类库

jar包含义
commons-fileupload-1.2.1.jar文件上传
commone-io.jar文件上传所依赖的jar包
commons-logging-1.0.4.jar日志jar包
freemarker-2.3.15.jar
struts2-core-2.1.8.1.jarstruts的核心类库
xwork-core-2.1.6.jarstruts2是在webWork的基础上进行创建的所以也需要导入webwork的核心

2、定义struts.xml文件

导入最少支持的jar包后,我们需要在类路径下定义一个struts.xml文件,该文件是Struts2框架控制器完成拦截的核心
该xml文件的节点定义被Struts框架中的dtd文件所约束,我们将dtd同样导入我们的项目目录中,并且在xml文件中声明导入的dtd约束
struts.xml文件,并且在文件中声明dtd约束,定义struts根节点

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
	"http://struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
	 
</struts>

3、在web.xml文件中对struts2框架的核心控制器进行映射配置

在web.xml文件中对struts2框架的核心控制器进行配置
在struts1.x中,struts框架是通过Servlet启动的。在struts2中
struts框架是通过filter启动的。他在web.xml中的配置如下:

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

StrutsPrepareAndExecuteFilterinit()方法中将会读取类路径下默认的
配置文件 struts.xml完成初始化操作
该操作只在服务端启动的时候初始化Filter时才进行,这也就是为什么
当struts框架的配置文件改动时,需要在重启服务端的原因

注意:struts读取到struts.xml的内容后,以JavaBean的形式存储
在内存中,以后struts框架对用户的每次请求处理将使用内存中的数据
而不是每次都会读取配置文件struts.xml

当环境搭建完毕后,我们启动tomcat服务器正常启动则说明环境搭建成功

Status2的第一个案例
掌握Status的编写步骤和配置文件中的节点的含义
步骤
1、在类路径下的struts.xml文件中对动作元素进行描述
2、根据配置文件中所描述的动作元素创建对应的JavaBean 创建对应的动作方法 通常在定义JavaBean的时候为了区别含有动作方法的JavaBean和普通JavaBean,一般在含有动作方法的JavaBean的名称后加上Action
3、创建结果处理页面 View视图
4、访问会被控制器所拦截的请求路径即可

访问路径: /包的名称空间/action动作名称 即可

访问路径的特点以及action名称的搜索顺序
struts控制器会拦截的请求路径 默认是 action和””
这些都在Struts框架的配置文件中进行了规定 default.properties
默认情况下访问请求拦截的路径 可以直接写action动作名称 或者 action动作名称.action

细节
action名称的搜索顺序
我们如果在package节点中定义了namespace属性 定义了名称空间,那么在访问action动作元素的时候,就需要匹配该名称空间,但是该名称空间可以为多个路径,只要有一个名称空间为正确那么最终都会访问到指定的action动作
如:在package节点中声明的namespace名称空间为 /test
在访问action的时候,指定名称空间为 /test/a/b/c/action动作元素
这样同样可以访问的到 只要第一个路径保证是正确的名称空间,那么剩下的路径任意匹配即可
这是因为在访问action动作元素的时候,控制器会按照请求的名称空间进行层层递减的查询所查找的动作元素
如:/test/a/b/c/action动作元素
控制器会根据名称空间来层级查找对应的动作元素
第一层 /test/a/b/c 如果有则找到action动作元素 如果没有 进入第二层
第二层 /test/a/b 抛出了/c 进行查找 如果查找到则执行对应的action动作元素 如果没有则继续搜索
第三层 /test/a …
第四层 /test 找到

struts.xml配置文件

我们使用Struts提供的控制器接收用户的请求来处理请求,就需要在类路径下的该配置文件对具体请求的动作进行描述 规定 。 当浏览器在请求服务端时,struts框架控制器会拦截本次请求,并且判断本次请求是否应该被处理,判断的依据就是我们在struts.xml文件中定义的动作元素中的信息,如果需要,则获取动作元素中对应的JavaBean对象,执行对应的方法,该方法的定义必须按照规范来进行定义,那就是方法的返回值必须是String类型且方法为public所修饰,方法没有参数。 JavaBean中对应的处理浏览器请求的方法也被叫做动作方法
在执行完JavaBean中的动作方法后,控制器会根据动作方法的返回值来进行结果的处理。

学习如何在struts.xml文件中定义动作元素来描述一次请求被拦截后的处理

1、定义包 package节点

该节点和我们在定义类的时候声明的包类似,在功能上有相同的作用,那就是防止多次请求的重名情况,对请求进行管理操作
我们在根节点struts中定义action节点来描述动作元素 ,一次请求可能对应着一次动作元素,当用户的请求操作类型一多,可能会出现动作元素出现重名的情况,这时我们可以使用package节点来进行管理,同一个package中的action节点不允许重名动作元素的出现但是在位于不同的package节点中,出现同名的action节点是可以的,这样更方便我们来拦截请求调用相应的动作
定义package节点

package属性
  • name属性
    给该包起一个名字,name属性是唯一的,因为如果包在重复,那么对动作元素的管理也就没有意义,其他包想要继承该包也会找不到继承的对象

  • namespace属性
    命名空间 该属性的作用是充当访问package节点下的动作元素的路径使用
    该属性可以不设置 ,当不设置namespace属性时,namespace属性的属性值为空字符串""
    namespace属性的值往往以“/”开头 ,当访问package下的指定动作元素时,需要在加上namespace属性定义的路径
    例如 namespace属性的属性值为 /customer action的name属性为 addCustomer
    那么会被该动作元素拦截的请求路径就必须是 /customer/addCustomer

  • extends属性
    继承其他包 我们定义的package包同样也可以继承其他包,获取其他包下 定义的动作元素
    extents属性的默认属性为struts-default,并且通常这个属性我们都不会去更改,默认都会让当前声明的包继承自struts-default包 因为Struts2很多核心的功能都是拦截器来实现的。如从请求中把请求参数封装到action文件上传和数据验证等等 都是通过拦截器实现的。struts-default定义了这些拦截器和Result类型。可以这么说:当包继承了struts-default才能使用struts2提供的核心功能。struts-default包是在struts-core-2.x.x.jar文件中的struts-default.xml中定义的
    struts-default.xml也是Struts2的默认配置文件,Struts2每次都会自动加载struts-default.xml文件
    extends属性的默认值是struts-default 该包是Struts2框架中的一个包,该包中定义了Struts2中很多拦截器和Result结果类型,所以我们一般不会更改这个包的属性值,默认就使用struts-default

  • abstract属性
    不常用
    属性值为true 或者 false
    当该属性的属性值为true时 package节点下不可以定义action节点

<package name="test" namespace="/test" extends="struts-default"</package>

2、定义动作元素(Action节点)

action节点
动作元素 该节点就对应着会被控制器拦截的请求,我们在action节点中描述请求
定义请求的路径和声明动作方法所处的JavaBean对象 还有指定处理请求的JavaBean中的方法

action属性
  • name属性
    指定请求操作 控制器会根据本次的请求在struts.xml文件中进行匹配
    匹配的依据就是首先根据package的命名空间找到指定的包 在根据请求的 url来找到对应的action节点,控制器就是根据action节点的name属性找到对应的action节点

  • class属性
    指定哪个JavaBean,该class属性的属性值就是JavaBean对应的类路径 控制器会利用反射来创建JavaBean的实例化对象,然后在运行对应的方法进行处理请求

  • method属性
    指定拦截的请求处理的方法 控制器会创建class属性对应的JavaBean对象
    然后根据method属性找到对应的方法运行指定的方法,然后根据方法的返回值转向结果页面
    来完成请求

	 	<action name="addCustomer" class="cn.itcast.domain.Customer" method="addCustomer"> 
	 		<!-- 定义返回结果处理 Result -->
	 		<result name="success">/success.jsp</result> 
	 	</action>

3、result节点

该节点的功能就是匹配在action节点中的method属性对应的处理请求的JavaBean方法的返回值,根据返回值来匹配result节点中name属性的值来转向对应的结果页面进行显示

  • name属性

唯一,和method属性中的方法的返回值相匹配 根据方法的方法值来跳转的主体对应的页面中。

 <struts>
	<!-- 声明package节点,对action动作元素进行管理 -->
	 <package name="test" namespace="/test" extends="struts-default">
	 	<!-- 定义action节点,描述请求对应的动作元素 -->
	 	<action name="addCustomer" class="cn.itcast.domain.Customer" method="addCustomer"> 
	 		<!-- 定义返回结果处理 Result -->
	 		<result name="success">/success.jsp</result> 
	 	</action>
	 </package> 
</struts>

Struts配置文件中的提示问题

1、联网的时候有提示
2、如果我们有下载完毕的dtd文件可以在Eclipse中进行配置,可以在编写的时候进行提示
1、struts.xml配置文件编写是没有提示的问题?
方法一:上网即可
方法二:
1、拷贝http://struts.apache.org/dtds/struts-2.1.7.dtd地址
2、Eclipse的window、preferences,搜索XML Catelog
3、点击add按钮
Location:dtd文件的路径
Key Type:URI
Key:http://struts.apache.org/dtds/struts-2.1.7.dtd

控制器会拦截用户的请求,并且根据用户的请求来进行处理判断 ,在Struts2框架中定义了多个拦截器来处理用户的不同请求

学习action动作元素属性的默认值

在struts.xml文件中定义action节点来描述动作action节点中的一些属性可以不进行声明来使用属性的默认值

学习这些默认值的意义

  • package节点

    namespace属性的默认值是"" 代表了空字符串,当namespace属性的属性 值为空时,访问action时不需要在指定namespace

  • Action节点

    name属性 必须要有 且唯一 name属性没有默认值

    class属性:该属性可以不进行声明定义,该属性的默认值是 com.opensymphony.xwork2.ActionSupport
    当不对class属性进行值的定义的时候,class属性的默认值就是ActionSupport的类路径,该类是一个普通类,该类继承了Action抽象类,并且实现了所有的抽象方法
    在该类中定义了一个execute()方法,该方法当action节点的method属性的属性值为execute时会运行execute()方法,并且在该类中定义很多用于表示执行动作方法的返回值常量
    在实际业务开发中,我们定义的动作类通常继承该类,并且使用该类的常量来表示动作方法的返回值

    ActionSupport中的用于表示动作方法返回结果的常量值

常量名称对应的字符串
SUCCESSsuccess
NONEnone
ERRORerror
INPUTinput
LOGINlogin

method属性的默认值 execute 对应的就是 ActionSupport对象中的execute()方法 该方法
的返回值是一个ActionSupport中的常量字段 SUCCESS
当method的属性没有被赋值的时候,会调用ActionSupport类中的execute()方法,并且
返回success结果 ,所以我们定义的动作类 都推荐继承ActionSupport类

当method的属性值为空时,会默认调用ActionSupport类中的execute()方法,如果class指定的属性
中的字节码文件中没有execute()方法,或者并没有实现ActionSupport类时 ,会出现没有这个方法的错误

  • result节点

    各属性的默认值
    type属性
    默认是dispatcher dispatcher 转发 使用转发的方式处理结果页面

    type属性的属性值在struts-default中被规定
    dispatcher是默认的 表示处理结果页面的方式为请求转发
    chain 表示将本次请求的结果转发给另一个action节点去处理

    当result节点的type属性指定为chain时,指定转发本次的请求处理到另一个 action去处理,当接收处理的action在同一个包下时,直接在result节点内容指定action节点的name属性名称即可当需要接收处理的action节点在另一个package节点时,我们就需要声明另一个包下的action的名称空间 我们需要使用result节点的另一种声明方式

    在action节点中使用result节点来处理指定动作方法后返回的结果处理方法

    result元素的写法:

     方式一:
     <result type="chain" name="success">a2</result>
     方式二:
     <result type="chain" name="success">
     	name对应的chain的处理器中的setActionName()方法
     	<param name="actionName">a2</param>
     </result>
    

注意:如果要转向的是在另外一个名称空间的动作,那么只能使用方式二

type值含义
redirect重定向
dispatcher普通的转发到某个页面
chain普通的转发到某个动作名称
redirectAction重定向到一个动作名称
plainText以纯文本的形式输出JSP内容
<struts>
	<!-- 声明package节点,对action动作元素进行管理 -->
	 <package name="test" namespace="/test" extends="struts-default">
	 	<!-- 定义action节点,描述请求对应的动作元素 -->
	 	<action name="addCustomer" class="cn.itcast.domain.Customer" method="addCustomer">
	 		<!-- 定义返回结果处理 Result -->
	 		<!-- 当result节点的type属性指定为chain时,指定转发本次的请求处理到另一个action去处理,当接收处理的action在同一个包下时,直接在result节点内容指定action节点的name属性名称即可当需要接收处理的action节点在另一个package节点时,我们就需要声明另一个包下的action的名称空间 我们需要使用result节点的另一种声明方式 -->
	 	<!-- 
			<result type="chain" name="success">a2</result>
	 	 -->
	 		<!-- result节点的另一个使用方式,在result节点中定义param子节点,使用子节点来设置chain方式指定的另一个包中的action节点 
	 		 -->
	 		  <result type="chain" name="success">

使用param子节点,指定name属性的属性值为actionName来设置接收请求的action的名称在chain定义的请求的方式中在struts-default配置文件中chain方式对应的一个类对象为ActionChainResult对象在该对象的类中,就定义了对应的actionName字段名称和对应的setActionName()方法用于设置接收请求的action名称 同样对应的还有一个namespace字段和对应的setNameSpace()方法用于设置指定的action的命名空间在result节点下定义两个param子节点分别设置name属性的值为 actionName和namespace进行设置即可

	<param name="actionName">a2</param>
	<param name="namespace">namespace2</param>
	 		</result> 
	 		
	 	</action>
	 	
	 	 
	 </package>
 	<!--
 		 当一个package下的action节点中的result结果处理的type属性chain时,它想交给两个package的action处理时,需要在指定action名称的同时在指定action所位于的包的命名空间
 	 -->
 	<package name="p2" namespace="namespace2" extends="struts-default">
 		 	<!-- 接收addCustomer动作的转发结果处理 -->
 		 	
<action name="a2">
	 <result type="redirect"name="success">/chain1.jsp</result>	<result name="error">/error.jsp</result>
</action>
	 	
</package>
</struts>

result 节点的type属性继续学习 和获取和设置action节点对应的Action类中的字段值

掌握redirectActionplainText 两个请求方式 还有设置action类中的属性值和获取action中的属性值

redirectAction:result节点中的type属性的取值之一,指定方式为重定向另一个action节点来处理这次结果

重定向的action可以是同包下的也可以位于不同包下的action 不同包下的action节点需要使用result节点的子节点param来进行设置不同包下的action的名称和包的命名空间进行访问

redirectAction的功能和chain类似,不同的是 chain是转发一次请求的结果处理给另一个action进行处理 ,而redirectAction是重定向一个请求的结果处理给另一个action进行处理 重定向的action名称会改变

chain和redirectAction如果想要对请求转接到另一个包下的action时,都需要使用节点下的节点为namespace属性和actionName来赋值

plainText :会将jsp页面的内容以文本的形式输出显示。

action元素对应的JavaBean中的字段的设置与获取
设置值 在action元素中为对应的Action类(JavaBean)进行动态赋值
使用节点进行赋值 name属性指定字段的名称 在节点主体定义值即可
注意:如果想要在action元素中为JavaBean中的字段进行赋值,那么该字段必须要遵循JavaBean字段的定义规范且要有对应的setter方法来可以完成赋值

<!-- 在action节点中,新增一个param节点,该节点可以给action节点对应的JavaBean中的字段进行赋值 ,前提是该字段遵循JavaBean定义规范 -->

<param name="name">maokexing</param>

小知识点
struts.xml文件的加载问题
当我们配置struts框架的配置文件时,需要在重新启动服务端来重新加载更新的配置文件,在实际开发中,重新启动服务端是不被允许的,我们可以在struts.xml文件中进行设置
在struts.xml文件中定义节点来设置
同样该节点的name属性和value属性在struts.xml文件中设置
设置服务端自动加载被更改过的配置文件

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

设置会被控制器所拦截的请求路径

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

使用constant节点来设置struts.xml在更改内容时,不用在重新启动服务器

default.properties配置文件

在struts的核心jar包中struts-2-core中有一个default.properties配置文件
在配置文件中,配置了struts框架的一些基本信息

struts.devMode = false

指定服务器是否需要重新启动来加载struts.xml文件
默认值是false 当设置为true的时候,我们就不用在重新启动服务器来加载struts.xml文件

struts.action.extension=action

设置会被控制器拦截的请求路径 , 默认是只支持action和空字符串

获取值

动态获取JavaBean中的字段的值

主要体现在我们获取字段的值,并且将字段的值以请求参数的形式交给result节点中指定的结果处理页面进行处理
怎么获取action元素对应的JavaBean中的字段的值
${字段名称}
注意:在对应的JavaBean中必须要定义对应的getter方法 ,如果没有getter方法,则获取不到

<!-- 在action元素中获取对应的JavaBean类中的字段的值 使用${}获取,获取的值可以直接作为参数,携带在result处理结果的页面中 -->
<action name="removeCustomerById" class="cn.itcast.domain.Customer" method="removeCustomerById">
  <result type="dispatcher" name="success">/customer/removeCustomer.jsp?id=${id}</result>
</action>

配置全局结果视图

在package节点中定义一个节点 声明同一个包中的共享结果视图,但是仅限于同一个包中
如果想要其他包中的action节点来能使用定义的结果视图,可以将结果视图单独定义在一个package包中,包继承struts-default 在由其他包继承定义的package包即可

编程思想:如果多个包中使用的是一个全局结果视图,那么我们定义在单个包中的节点的结果视图在其他包下就访问不到我们可以创建一个包,该包中不参与业务逻辑,package实现struts-default并且在包下定义global-results节点 声明全局结果视图 在让其他包继承该包即可

<package name="customer" namespace="/customer" extends="struts-default">
<!-- 如果每一个动作元素都具体同一个结果提示处理,那么,我们将将该处理请求定义在package节点下的global-results节点中作为同一个包下的全局结果视图
global:全体的
<global-results>节点 ,该节点中只允许有一个子节点 那就是用于
提示结果视图的result节点 当在<global-results>节点中定义了result
节点后,那么在同包中的所有action节点都可以使用
-->
<global-results>
<result type="dispatcher" name="error">/customer/error.jsp</result>
</global-results>

Struts2常量

使用节点在struts.xml文件中指定default.properties规定的常量,设置Struts框架的一些功能或者信息

<constant name="" value=""></constant>

也可以在其他Struts框架的配置文件中来设置这些常量,但是在struts.xml文件中是最方便的
常量可以在以下多个配置文件中进行定义,下面的排列顺序也是struts2加载常量搜索的顺序

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

常用的常量了解

指定需要Struts2处理的请求后缀 会被控制器所拦截处理的请求后缀

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

指定默认编码集,作用于HttpServletRequest的setCharacterEncoding()方法和freemarker、velocity的输出

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

设置浏览器是否缓存静态内容 默认值为true(产品发布是使用,有助于减轻服务端的压力),开发阶段最好关闭 ,因为我们要测试页面效果
默认是false

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

该标签的功能和response设置控制页面缓存的3个请求头功能一样

Expires -1
Cache-Control:no-cache
Pragma:no-cache

当struts的配置文件修改后,系统是否自动重新加载该文件 默认是false 开发阶段建议为true

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

功能和上面的一样,更推荐这个 ,因为这样可以打印出更详细的错误信息

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

上传文件的大小限制

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

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

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

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

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

默认的视图主题

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

Struts2的处理流程

Struts2的核心控制器 StrutsPrepareAndExecuteFilter

在这里插入图片描述

用户的每次请求被控制器拦截处理,都会创建一个Action实例 也就是对应的JavaBean实例来完成请求 ,所以不会担心有线程安全问题

小知识点

  • 拆分Struts的配置文件

Struts.xml文件 我们可以定义多个类似于Struts.xml文件的配置文件,它的功能和struts.xml文件一样,我们可以将action元素定义在和struts.xml文件相同的配置文件中,该配置文件要使用和struts.xml文件相同的dtd约束
在struts.xml中使用静态包含 包含定义的其他struts.xml文件

<include file=""></include>
  • 动态方法调用

动态方法调用:在一个action类中,可能会创建多个动作方法,比如add()方法和update()方法,我们可以在调用add()方法的时候 使用!号来动态的调用action类中的其他方法

使用default.properties配置文件中定义的键值对来设置是否支持动态方法的调用

<!-- 禁用动态方法调用 -->
<constant name="struts.enable.DynamicMethodInvocation" value="false"></constant>

虽然使用动态方法调用执行了Action类中的其他方法,但是结果集的处理,还是原有的action元素的result

  • 使用通配符来定义action

通配符:*
action节点中的name属性声明了我们方法的请求路径,该路径中可以使用通配符 ,该通配符的值也就是浏览器访问该请求时,匹配的值,并且该通配符还支持占位符
比如:

<action name=”order_*” class=”cn.itcast.OrderAction” method=”${1}”>
	<result type=”dispatcher” name=”success”>${1}.jsp</result>             
</action>

当用户的请求路径为/order_find 时 ,*匹配的值就是find,而占位符中对应的就会
匹配Action类中的find方法,执行find方法,结果集的处理也会自动的匹配find值
而找到find.jsp页面

使用通配符可以对struts.xml文件的编写更加简洁,方便

获取表单的请求参数

Struts2框架控制器拦截表单的请求,将表单提交的数据封装到对应的JavaBean中

框架会拦截表单的请求,并且将表单请求中name属性对应的JavaBean中的字段的值设置为表单输入域中输入的值,不过前提是,name属性要和JavaBean中的字段名称一致,并且字段要有对应的getter和setter方法,这样才可以成功的设置值

不同的JavaBean中字段类型
数组类型 在页面中表单的表现形式为 复选框 选择的多个值,同样也可以使用控制器来封装到JavaBean中的字段中对象类型,赋值对象的字段,使用复合类型参数,只需要在页面中表单的name属性设置上使用对象名称.字段名即可

post请求框架会自动进行编码设置 ,设置的是default.properties进行设置的
而get请求则需要我们进行重编码的方式来进行乱码的解决,我们可以设置一个过滤器来做这个问题

也有不同版本的框架解决了get方法提交数据的乱码问题,不同的框架不同

ActionContext对象

Action类中的上下文,和ServletContext对象类似,我们也可以将我们需要的信息数据存储到ActionContext中,该对象内部就维护了一个map集合,可以在页面中使用el表达式获取存放在ActionContext中的数据

自定义类型转换器 Converter

为Struts2框架注册类型转换器,完成表单提交的数据与对应的JavaBean中的字段的类型转换,表单提交的数据都是字符串

以表单中的birthday输入框的值转换为JavaBean中对应的Date类型的birthday为例
默认情况下 struts2框架会默认进行本地时间格式的转换,当格式不为本地默认类型的时候,我们就需要自定义类型转换器,完成类型转换
当表单类型转换失败和验证错误的时候数据回显的问题
result节点的name属性指定为input的时候,可以指定页面去转向进行数据回显

<result type="dispatcher" name="input">/customer/add.jsp</result>

当我们的表单提交的数据与JavaBean中真正保存的数据类型转换错误或者验证失败时,框架会自动的将表单提交的数据转向该节点指定的页面

自定义类型转换器和注册转换器
自定义类型转换器的步骤
1、创建一个类继承DefaultTypeConverter 该类是框架规定的类
2、复写DefaultTypeConverter类中的convertValue()方法,将类型转换的代码定义在方法中

@Override
public Object convertValue(Map<String, Object> context, Object value, Class toType)
{
	//观察要被转换的表单的值是哪种类型的
	System.out.println(value.getClass().getName());
	
	//获取要转换的类型
	System.out.println(toType);
	
	return null;
 }

3、学习DefaultTypeConverter中的converterValue()方法中的参数含义
Map<String,Object> context ognl表达式使用的
Object value 表示了自定义类型转换器转换的值 也就是表单中输入的值
Class toType 转换为哪一种类型的值

注册自定义类型转换器
我们注册已经定义成功的类型转换器到struts框架中,实现类型转换
注册步骤:
1、在我们使用转换器的类路径下创建一个指定名称的properties配置文件
该配置文件的名称就为 Action类名-conversion.properties
例如 UserAction-conversion.properties

2、在该配置文件中定义一个键值对,该键就是表单中的name属性的属性值,也就是待转换的值,对应的JavaBean中的字段要一致,键对应的值就是类型转换器的类路径
birthday=cn.itcast.convertor.DateConvertor

注册全局类型转换器
在WEB-INF目录下的classes的目录下创建一个指定名称的properties配置文件,该配置文件的名称必须指定为xwork-conversion.properties
我们在该配置文件中定义一个键值对用于注册全局类型转换器
键值对:待转换类型=类型转换器的类路径

注册全局类型转换器

  • 全局类型转换器可以对当前struts.xml文件中配置的所有Action对象都有效
  • 注册全局类型转换器同样也需要一个properties配置文件,该配置文件必须存放在WEB-INF目录下的classes目录下 ,必须指定配置文件的文件名为 xwork-conversion.properties
  • 在该配置文件中定义一个键值对注册全局转换器 该键值对的键就是待转换类型,对应的值就是类型转换器的路径

使用ActionContext对象获取Servlet的域范围

Servlet有三个域范围

域名称对应域含义
ServletContextapplication域对应着当前web应用的域范围
ServletRequestrequest域请求域范围 一次请求有效
HttpSessionsession域会话域范围 一次会话有效

ActionContext 获取三个域范围,向域范围中存储数据
创建ActionContext对象的实例

ActionContext ac = ActionContext.getContext();

获取ServletContext对象的application域范围

调用ActionContext对象的getApplication()方法返回一个map对象,该map集合就对应着ServletContext对象内部维护的map域范围

Map<String,Object> application = ac.getApplication();

获取HttpSession对象的session域范围
调用ActionContext对象的getSession()方法,返回一个map对象,该map集合就对应着HttpSession对象内部维护的map域范围

Map<String,Object> sessionScope = ac.getSession();

向request域对象中存储数据
将对象数据存储到ActionContext 范围就是一次请求。

ActionContext ac = Action.getContext();
ac.put(”request”,”request_map”);

在定义的Action类中获取Servlet的常用对象
两种方式
方式一:使用ServletActionContext类
该类中定义了对应的get方法,用于获取对应的Servlet中的对象
获取ServletContext对象

ServletContext context = ServletActionContext.getServletContext();

获取ServletResponse对象

ServletResponse response = ServletActionContext.getResponse();

获取ServletRequest对象

ServletRequest request = ServletActionContext.getRequest();

方式二:在Action类上实现
ServletRequestAware ServletResponseAware ServletContextAware接口

该接口中都对应的有一个set方法用于初始化ServletRequest、ServletResponse、ServletContext对象,实现该方法,就可以有框架初始化对应的Servlet中的对象

使用Struts2来完成上传文件

控制器拦截表单上传文件的请求,交给指定的Action类来处理
上传文件的前提
1、from表单的method属性为post
2、enctype属性为multipart/form-data
3、file输入域

原理:控制器拦截表单上传文件的请求,交给对应的Action类进行处理,控制器会将上传的文件封装到对应的Action类中的File字段中,前提是表单的name属性值与Action中的file字段名称一致,且提供了对应的getter和setter方法

在对应的Action类中的动作方法对存储了上传文件的File字段进行处理即可

如何获取上传的文件名和文件的Mime类型
在Action类中创建两个指定名称的String类型的字段
文件名: 表单中file域name属性值+FileName
文件的MIMe类型:表单中的file域name属性值+ContentType
当表单请求被控制器所拦截时,Struts2中的拦截器就会对上传的文件进行处理,将文件封装到Action类中对应的File字段中,将文件的名称封装到对应的Action类中的指定命名的String字段中,将文件的MIMe类型封装到指定的命名字段中

我们获取了文件的MIMe类型就可以继续对文件的上传类型进行控制

//定义一个字段,用来表示上传文件的真实名称,该字段的命名必须由表单file域的name属性值
//加上FileName构成,当控制器拦截上传文件的请求时,会将文件名称赋值到该字段中
    private String imageFileName;
    //获取表单上传文件的MIME数据类型
    //在对应的action类中创建一个字段
    //可以使用该字段获取的上传文件的文件类型来约束页面上传的文件
    private String imageContentType;

对文件的保存处理,文件上传的服务端,服务端需要将文件保存到指定的目录下,我们可以使用输入流和输出流来进行读写保存,也可以使用Struts2提供的工具类FileUtils来保存文件

FileUtils中的copyFile(srcFile,descFile);方法

srcFile:源
descFile:复制到

//FileUtils工具类,保存上传的文件 copyFile()
FileUtils.copyFile(image,new File(realPath+"\"+imageFileName));

多个文件上传
在Action类中定义数组来保存多个上传的文件即可
//定义数组类型的变量来表示多个文件上传
private File [] images;

//定义一个数组类型的imagesFileName表示多个上传文件的文件名称
private String [] imagesFileName;

//定义数组类型的imagesContentType表示多个上传文件的文件的MIME类型
private String [] imagesContentType;

Struts2拦截器学习 Interceptor

Struts2的原理就是通过不同的拦截器对于请求进行拦截处理,数据封装验证等等都是通过拦截器来处理的

Struts2默认的拦截器定义在struts-default.xml文件中,当一次请求被控制器所拦截后,这次请求就会被默认的拦截器进行拦截,这是如果是指定功能的请求,拦截器就会完成对应的功能。如:文本上传,就会经过文件上传的拦截器,将上传的文件的信息进行处理。

指定控制器默认的拦截器

<default-interceptor-ref name="defaultStack"/>

学习自定义拦截器
步骤:
1、创建一个类,实现Interceptor接口

2、Interceptor接口中定义了三个方法
方法:
init() 初始化拦截器,当拦截器被创建后,它就会驻留在内存中 当服务端启动的时候,就会创建拦截器的实例
destroy() 当拦截器被销毁时,执行该方法
intercept(ActionInvocation invocation) 我们将拦截器拦截请求处理的代码定义在intercept()方法中

3、intercept()方法 ,该方法中传入一个ActionInvocation参数,该参数代表了动作对象,使用invocation调用invoke()方法,就相当于调用了Action类中的动作方法,我们定义拦截器的意义就是在运行动作方法前,由拦截器预先处理这次请求,对这次请求的数据进行处理,当请求的数据处理完毕后,我们在运行对应的action类的动作方法,继续完成请求。我们就使用invocation对象的invoke()调用原来的动作方法。

注册拦截器
对我们定义好了的拦截器在struts.xml文件中进行注册
注册局部拦截器 只对一个包下的action进行拦截处理

注册局部拦截器
步骤:
1、给我们自定义的拦截器起一个名称,该名称没有意义,方便标识和被引用
在该节点下创建节点对拦截器进行命名

<!-- 给自定义的拦截器起一个名称 -->
<interceptors>
<interceptor name="拦截器名称" class="自定义拦截器类全名"></interceptor>
</interceptors>

2、在想要被拦截器拦截的action节点内,使用来声明拦截器

引用我们自定义的拦截器,根据名称进行引用,但是要注意当我们引入自定义的拦截器后,控制器就不会在使用struts.default中规定的默认拦截器,一些最主要的拦截功能也就会丢失,我们可以在引用自定义的节点前再次引用struts.default文件中规定的默认的拦截器 defaultStack
在引用自定义拦截器之前,引入struts的默认拦截器

<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="自定义拦截器名称"></interceptor-ref>

注册全局拦截器,可以对多个包下的多个action进行拦截处理
注册全局拦截器,可以使用一个包package来实现
定义一个package 包,该包继承了struts-default,不耽误控制器的正常使用,在该包中,对自定义拦截器进行声明,并且还需要引用struts规定的默认拦截器

<package name="myPackage2" namespace="/myPackage2" extends="struts-default">
		<!-- 描述全局拦截器,多个package可用 -->
		<interceptors>
			<interceptor name="自定义拦截器名称" class="拦截器类全名"></interceptor>
			<interceptor-stack name="permissionStack"> 声明引用自定义拦截器的名称
				导入Struts规定的默认拦截器
                   <interceptor-ref name="defaultStack"></interceptor-ref>
导入自定义拦截器
			    <interceptor-ref name="permissionInterceptor"></interceptor-ref>
			</interceptor-stack>
	 		
		</interceptors>
</package>

定义一个包,在包中对自定义拦截器和核心拦截器进行组合

 <interceptors>
 	描述自定义拦截器
 	<interceptor name="" class=""></interceptor>
 	组合自定义拦截器和核心拦截器,当在此使用的时候,只需要使用组合的名称即可
 	<interceptor-stack name="">
 		<interceptor-ref name="default-Stack"></interceptor-ref>
 		<interceptor-ref name="自定义拦截器"></interceptor-ref>
 	</interceptor-stack>
 </interceptors>

在不同的package想要引用自定义拦截器时,只需要继承定义的package,然后在想要拦截器拦截处理的action下使用节点指定自定义拦截器的名称即可

输入校验

在Struts2框架中,我们可以对Action类中的所有方法进行校验,也可以对Action类中的指定方法进行校验

对于输入校验Struts2提供了两种方式
1.手写代码实现
2.使用xml文件实现

手动编写代码实现输入校验
原理:在ActionSupport类中有一个validate()方法,我们可以复写该方法在Action类中,并且编写对应的验证数据的代码,在该方法中,我们对Action中的字段进行校验,校验是否符合规定,如果不符合规定,我们可以将字段和对应的字段的错误信息都封装到一个map集合中,使用addFieldError()方法,封装字段的错误信息到一个map集合中,当map集合中封装了错误信息时,那么控制器将不会在进行执行对应的动作方法,而是会在action节点中找到input类型的result节点指定的验证错误的提示页面,进行错误信息的提示,错误信息的提示就是在页面中将字段对应的字段错误信息输出到页面中,使用Struts2框架的标签来完成该提示功能
标签<s:fielderror></s:fielderror> 将addFieldError()方法添加的所有字段的错误信息都打印到页面中
<s:fielderror fieldName=""/> 指定字段名称打印对应的错误信息

也就是说:当控制器拦截一次请求,指定Action类中的动作方法时,如果Action类中有validate()方法时,控制器会先执行validate()方法,如果方法执行结束后,数据并没有不符合要求的,也就是addFieldError()并没有像内部的map集合中添加错误信息,控制器才会执行对应的动作方法,如果出现了错误,那么就会转向提示页面 input

手动编写输入验证Action类中所有动作方法的步骤

1、Action类中必须要继承了ActionSupport类
2、复写ActionSupport类中validate()方法,该方法并没有返回值,只是作为验证Action类中的数据是否符合规范使用,如果符合规范,则会继续调用请求对应的Action中的动作方法,如果不符合规范,那么,就会将不符合的规范的字段和错误信息使用addFieldError(String fieldName,String errorMessage)方法将错误的字段和信息都封装到内部的map集合中,当执行完validate()方法后,如果map集合中存储错误信息,那么将不会执行对应的动作方法,而是跳转到input 类型的result节点指定的页面去提示
3、在input类型的result节点指定的页面上,使用Struts2框架指定的标签来显示字段对应的错误信息
导入Struts2的标签库
<%@ taglib uri="/struts-tags" prefix=“s” %>
4、使用Struts2框架的标签提示错误信息
<s:fielderror fieldname=""/> 指定字段名称查询map集合中的错误信息进行提示
<s:fielderror></s:fielderror> 提示map集合中所有的错误信息
1、手工编程验证,针对该动作类中的所有的动作方法
步骤:
a、动作类继承ActionSupport
b、覆盖调用public void validate()方法
c、在validate方法中,编写不符合要求的代码判断,并调用父类的addFieldError(String fieldName,String errorMessage)
如果fieldError(存放错误信息的Map)有任何的元素,就是验证不通过,动作方法不会执行。
Struts2框架会返回到name=input的result
d、在name=input指定的页面上使用struts2的标签显示错误信息。<s:fielderror/>

手动编写指定action类中的动作方法的输入验证
当控制器拦截请求时,找到该请求对应的Action类,判断是否有指定的验证方法,如果有则进行验证,判断,判断成功与否在进行执行动作方法

对指定的Action类中的动作方法编写验证
当请求对应的动作方法时,会先调用验证方法进行判断

编写方式
1、继承ActionSupport类
2、明确添加验证的动作方法,在Action类中新添一个方法 该方法的方法名称为validateXxx() validate+指定添加验证的动作方法,动作方法的方法名第一个首字母大写 如:我们要为add()方法添加验证方法,validateAdd()
3、在方法中添加验证代码即可,调用addFieldError()方法,存储错误信息
4、如果出现错误信息,就会跳转到input类型的result结果视图,没有验证错误则执行动作方法

输入验证的流程
1、类型转换器对请求参数执行类型转换,并把转换后的值赋给action中的属性。
2、如果在执行类型转换的过程中出现异常,系统会将异常信息保存到ActionContext,conversionError拦截器将异常信息封装到fieldErrors里。不管类型转换是否出现异常,都会进入第三步
3、系统通过反射技术先调用动作类中的validateXxx()方法,Xxx为方法名称。
4、在调用action中的validate()方法
5、经过上面4步,如果系统中的fieldErrors存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fieldErrors没有任何错误信息,系统将执行action中的动作方法

输入校验的方式二 基于xml配置文件的验证

基于XML配置文件的方式:
动作类中的所有方法进行验证:
在动作类的包中,建立一个名称为:动作类名-validation.xml ,比如要验证的动作类名是UserAction UserAction-validation.xml
内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">

<validators>
	<field name="username">
<!-- 内置验证器都是定义好的,在xwork-core.jar com.opensymphony.xwork2.validator.validators包中的default.xml文件中 -->
<field-validator type="requiredstring"><!-- 不能为null或者""字符串,默认会去掉前后的空格 -->
		<message>用户名不能为空</message>
		</field-validator>
	</field>
</validators>

步骤:
1、在action类路径下创建一个xml配置文件,该配置文件的名称为actionClassName-validation.xml
2、导入编写校验xml的dtd约束,该约束存放在x-work.jar下 约束名称为xwork-validator-1.0.3.dta
3、在xml配置文件定义节点来校验action类中的字段

<!-- validators xml配置文件输入校验的根节点 -->
<validators>
	<!-- field节点,定义校验信息,该节点中有一个type属性,指定
		校验的action类中的指定字段名称
	 -->
 
	<!-- field节点,指定action类中的字段,定义校验规则 -->
	<field name="username">
		<!-- field-validator type="" type属性指定验证规则 
		type属性的值在xwork-core.jar中的com.opensympony.xwork2.validator.validators
		包中的default.xml文件中进行了规定
		-->
		<field-validator type="requiredstring">
<!-- requiredstring 指定匹配的字段的规则不能为空,会默认去除字段两端的空格>
		 
<!-- 指定提示信息,如果匹配错误,错误信息将会被转发到input类型的result结果视图 -->
			<message>用户名不能为空</message>
		</field-validator>
	</field>
	
	<field name="password">
		
		<field-validator type="requiredstring">
			<param name="trim">true</param>
			<message>密码不能为空</message>
		</field-validator>
	
		<!-- 指定field-validator节点的type属性的属性值为regex 匹配正则表达式来校验输入 -->
		<field-validator type="regex">
			<param name="expression"><![CDATA[\d{3,6}]]></param>			 
			<message>密码必须为3到6位</message>
		</field-validator>
	</field>
	
	
</validators>

使用xml配置文件来进行输入校验
对action类中的所有动作方法进行输入校验
在需要输入校验的Action类路径下创建一个指定名称的xml配置文件
该配置文件的名称为 actionClassName-validation.xml
这是一个会被Struts2框架所识别的xml配置文件,该xml配置文件的节点定义需要被dtd所约束 ,导入dtd约束
定义xml配置文件中的节点,对action类中的所有动作方法进行输入校验
根节点
在根节点下,我们可以有两种方式的节点定义来完成动作方法输入校验的功能

第一种
使用field节点,指定name属性,该name属性指定的就是Action类中的字段,被校验的字段名称

<field name="username">
	在field节点下定义我们的校验规则和提示信息
	使用field-validator节点来定义校验规则 该规则可以是Struts2提供的内置的验证也可以自定义验证器
	
    <field-validator type="">
    在field-validator节点中定义提示信息,获取验证器所需的参数信息
    	<param name=""></param> 参数
    	<message></message> 提示信息	
    </field-validator>
	一个field节点下可以有多个field-validator节点,指定多个验证规则
	....

</field>

第二种,使用第二种节点在validator节点中来对Action类中的字段进行校验节点,将对字段的验证规则定义在validator节点中

<validator>
	定义param节点,使用name属性,指定属性值为fieldName param节点主体的值	就是验证的字段的值
	<param name="fieldName">字段名称</param>
	<message></message> 错误提示信息
</validator>

使用xml配置文件,对Action类中的指定方法进行输入校验
指定方法进行输入校验也是依赖于配置文件,对配置文件的名称进行指定的命名
ActionClass-动作方法(该动作方法名称应该是struts.xml文件中配置的action请求名称)-validation.xml

Struts2中的内置验证器

自定义基于xml文件的输入校验的验证器
自定义校验规则,在基于xml文件的校验中,引用自定义的验证器
步骤:
1、编写一个类,继承FieldValidatorSupport
2、复写父类中的抽象方法,validate(Object object),该方法中有一个参数Object object,该object参数就代表了请求的动作对象
3、在validate()方法中定义验证规则
4、FieldValidatorSupport类中定义了两个方法获取验证的字段名称和需要验证的字段值
getFieldName()获取动作类中需要被验证的字段名称
getFieldValue(String fieldName,Object object) 获取动作类中需要被要验证的字段的值,指定字段名称和对应的字段所属的动作类

5、在validate()方法中定义验证规则

注册自定义的验证器
在WEB-INF/class目录下创建一个validators.xml文件,在该文件中进行注册自定义的验证器
根节点
注册自定义验证器在validators节点中定义validator节点进行描述
validator name属性指定自定义的验证器名称,class属性指定继承自FieldValidatorSupport的类全名,自定义验证器的类路径

<validators>
	<validator name=”” class=””></validator>
</validator>

国际化

Struts2实现国际化
原理:使用ResourceBundle来读取指定位置的properties配置文件中的国际化信息
Struts2对其进行了封装
国际化就是我们页面或者Action类中的一些信息,在不同的国家地区文化中有不同的显示提示
国际化的应用分为两个地方
在JSP中使用国际化 在action类中使用国际化

创建国际化的配置文件 properties配置文件,然后分别在JSP和Action类中获取国际化的数据

全局范围的资源文件
存放properties配置文件的位置
在WEB-INF/classes中存放properties配置文件
也就是类路径 src的目录下
创建properties的格式
基名_地区语言_国家.properties
如:itcast_zh_cn.properties itcast_en_us.properties
定义好国际化的配置文件后,需要在Struts2框架的配置文件中,进行配置将资源文件配置为全局资源文件通过default.properties文件中规定的常量进行配置struts.custom.i18n.resources

<constant name="struts.custom.i18n.resources" value="itcast"></constant>

分别在JSP页面中和Action类中使用根据当前国家地区语言来显示对应的信息

在JSP页面中访问资源文件中定义的国际化信息使用Struts2框架定义的标签来访问
导入标签库
<%@ taglib uri="/struts-tags" prefix="s"%>
使用<s:text name=""></s:text>标签来访问资源文件中的国际化信息,name属性指定配置文件中的key(键)

在Action类中访问资源文件中定义的国际化信息
前提 Action类必须继承ActionSupport类
使用父类ActionSupport中的getText(String key)方法
参数key值就是资源文件中的key,来访问国际化信息

国际化 输出带参数占位符的国际化信息在资源文件定义国际化信息的时候,我们可以使用参数占位符,当在访问国际化信息的时候,可以为参数占位符进行赋值,输出信息{0},xxx,{1}xxx
在JSP页面中访问资源文件的国际化信息并且对参数占位符进行赋值
在<s:text>节点下定义子节点 <s:param>节点,在节点主体中定义参数值
<s:text name=“welcome”>
<s:param>孙艳文</s:param> 为参数占位符进行赋值
</s:text>

在Action类中访问资源文件的国际化信息并且对参数占位符进行赋值
getText()方法有一个重载的形式,接收指定资源文件的key值,接收一个String类型的数组作为参数占位符的值getText(String key,String [])

包范围资源文件:服务于一个包下的所有动作类的资源文件
将国际化的资源文件,定义指定的文件名称存储在指定包下,对该包中的所有动作类进行服务,当action类中访问国际化的信息时,首先就会在包范围中进行查询指定的key值,查找不到,在搜索全局资源文件
包范围资源文件的定义方式: 定义在action类处于的包中
资源文件的名称为package_语言_国家.properties另外,处于该包或该子包下的Action类都可以访问包范围资源文件

动作类的资源文件:服务于指定动作类的资源文件
Action名称_语言_国家.properties 专门服务于某一个动作类的资源文件,该动作类访问国际化的资源时,首先会先从所属的动作类的资源文件中查询

*在JSP页面中随机访问国际化的资源文件 <s:i18n></s:i18n>
Struts2提供了一个标签,可以实现在JSP页面中,随机访问在应用中定义的国际化的资源文件

使用Struts2的标签来实现在JSP页面中随意访问国际化的资源文件

<s:i18n name="cn/itcast/action/package">
 <!-- 使用name属性来指定资源文件的类型1、直接写基名:全局资源文件2、写一个包下的package 如:cn/itcast/action/package-->
<s:text name="welcome">
<s:param>***</s:param>
</s:text>
</s:i18n>

Struts2中的OGNL表达式

有难度
EL表达式和OGNL表达式的区别?是什么?能干什么?怎么用?

简介:OGNL表达式全称为 Object Graphic Navigation Language(对象图导航语言),它是一个开源项目,Struts2框架使用OGNL作为默认的表达式语言。

Struts2的核心WebWork使用的就是OGNL表达式

在Struts2框架中同样可以使用el表达式,但是el表达式可以做的事,使用OGNL表达式同样可以做,el表达式不能做的事情,OGNL也能做

OGNL能干什么?
相对于el表达式,OGNL表达式提供了平时我们更需要的功能
1、支持对象方法调用,如XXX.sayHello();
2、支持类静态方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名|值名] 例如:@java.lang.String@format(‘foo %s’,’bar’) 或者 @cn.itcast.Cnostant@APP_NAME;
3、操作集合对象 OGNL表达式可以创建List集合和Map集合

OGNL有一个上下文(Context)的概念,说白了上下文就是一个MAP结构,它实现了java.util.Map接口。在Struts2中上下文的实现为ActionContext

OGNL内部维护了一个map集合,我们使用OGNL表达式的一系列操作都是对于内部的context 上下文 也就是对内部维护的map集合进行操作,OGNL内部的context上下文又是由ActionContext来实现的

OGNL内部的Context结构示意图
在这里插入图片描述

当Struts2控制器拦截一次请求时,OGNL会创建ActionContext对象,创建valueStack对象,创建请求的Action的实例,并且将Action的实例存储到valueStack中,所以使用OGNL表达式能直接访问请求Action的实例

学习使用OGNL表达式
ONGL表达式不可以直接使用,必须结合Struts2的标签一起使用

使用OGNL表达式来访问OGNL内部的context中的数据
ognl的操作应用1

(命名空间)使用#符号来 访问OGNL内部维护的context上下文中存储的对象
访问上下文(Context)中的对象需要使用#符号标注命名空间,如#application、#session
另外OGNL会设定一个根对象(root对象),在struts2中根对象就是ValueStack(值栈)。如果要访问根对象(即ValueStack)中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。

ognl规定的context上下文中的结构
request:请求域
session:会话域
application:页面域
parameters:对应着el表达式中的param内置对象,代表了请求参数
valueStack:ognl的根对象(root),内部维护的是一个堆栈结构的list集合,存放着请求创建的Action的实例对象

在Struts2中,根对象valueStack的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象。在OgnlValueStack类里有一个List
类型的root变量,就是使用它存放一组对象。

在struts2框架上,我们不可以直接使用ognl表达式,必须结合Struts2的标签来一起使用
使用ognl表达式结合struts2框架的标签一起使用
获取访问的Action动作类中的数据,当访问的是请求的Action类中的数据时可以不使用ognl表达式规定的#符号来访问,因为当struts2控制器拦截一次请求时会依次创建ActionContext、ognl上下文中的valueStack以及请求的action对象,并且将action对象的实例存放到valueStack中,valueStack内部是一个list集合,存放到valueStack中的变量,可以直接使用ognl表达式来获取

valueStack内部的list结构是一个堆栈的数据结构,堆栈的进出原则是先进后出,最新存储到valueStack内部的list结构中的数据永远会出存放在栈顶的位置,所以如果不指定角标取出的数据永远取的都是第一个新添加的栈顶的数据

valueStack的搜索顺序
不指定#符号时,会从valueStack内部维护的list集合的栈顶依次查找当栈顶查找不到指定的属性时,就会继续查找list集合中的下一个对象,继续查找指定的属性,直到找到为止。

ognl的上下文中的其他结构

<s:property value="password"/>
<!-- 取的其实是在valueStack中内部的list集合中第二个action的请求实例对象 -->
<br/>
<!-- 使用ognl表达式搜索上下文中的application域  -->
<s:property value="#application.people"/>
<br/>
<!-- 使用ognl表达式搜索上下文中的session域 -->
<s:property value="#session.people"/>
<br/>
<!-- 使用ognl表达式搜索上下文中的parameters域 -->
<s:property value="#parameters.sex"/>
<br/>
<!-- 使用ognl表达式搜索上下文中的request域 -->
<s:property value="#request.people"/>
<br/>
<!-- 使用ogn表达式内部的attr来依次搜索域中的数据 -->
<!-- attr会默认从page、request、session、application域中查询指定的对象 -->
<s:property value="#attr.people"/> 

application:指的是当前web应用的应用域,如果想要使用ognl表达式
从application域中取数据,必须要使用#符号

ognl上下文中的parameters,代表了get方式携带在浏览器状态栏中的请求参数

<s:property value="[0].username"/>

<!-- debug 标签 struts2中的标签可以调试,并且可以查看当前各个域中都存放了什么对象,方便用ognl表达式进行操作 -->
<s:debug></s:debug>
<!-- 通过debug标签来查看ognl上下文中存储的对象有哪些,可以看出请求的Action对象的实例被存放到valueStack中,并且valueStack内部维护了一个list集合,我们同样可以使用下标索引的方式来访问valueStack中存储的Action对象-->

使用el表达式同样也可以在一次请求中获取action实例中的数据
使用el表达式同样也可以直接获取一次请求中控制器所创建的action实例中的数据,这是因为Struts2对HttpServletRequest进行了重新的封装,改写HttServletpRequest中的getAttribute()方法 这样当使用el表达式时,也可以获取action实例中的数据但是要注意,既然Struts2改写的是HttpServletRequest类中的getAttribute()方法,这就说明,只有在一次请求的时候,才可以获取存储在ognl上下文中valueStack
中的action实例的数据,使用<s:property>同理,如果不是一次请求,那么也获取不到了

ognl的操作2 可以创建list集合和map集合
使用ognl创建list集合和map集合,并向集合中存放数据,默认会保存到ognl的context上下文中

创建方式,结合Struts2的标签来创建list集合和map集合
<s:set var=”集合的名称” value=”{集合的内容}”></s:set>
set标签的scope属性可以指定存放的域范围,scope属性的值被struts-tag.tld中被规定,可以存放的域取值为 request、session、application、page action. action就是默认的ognl的上下文
Set标签用于将某个值放入指定范围。
scope:指定变量被放置的范围,该属性可以接受application、session、request、page或action。如果没有设置该属性,则默认放置在OGNL Context中。
value:赋给变量的值。如果没有设置该属性,则将ValueStack栈顶的值赋给变量。

*创建一个list集合
<s:set var=”list1” value=”{‘aa’,’bb’,‘cc’}”></s:set>

*创建一个map集合
<s:set var=”map1” value=”#{‘key1’:’value1’,’key2’:’value2’}”></s:set>
创建一个map集合时,必须使用#符号
创建好了的集合默认会被存放到ognl的上下文中context

遍历集合,在页面中使用Struts2的标签来遍历使用ognl创建的集合对象
使用<s:iterator value=””>标签来遍历 value属性指定存放到上下文中的集合对象

<s:iterator value="#list1" var=“l”>
<s:property value="#l"/>
</s:iterator>

<s:iterator value="#map1">
<s:property value=“key”/>=<s:property value=“value”/>
</s:iterator>

遍历ognl创建的集合
方式1:使用Struts2框架提供的<s:iterator>标签进行遍历所有的集合元素
<s:iterator>标签的value属性指定需要被遍历的集合对象 需要指定域范围 (如:#session,#request,#application) var属性代表了集合中遍历的每一个对象
在<s:iterator>子节点中使用<s:property >进行打印

方式2:使用JSP标签 JSTL核心标签库<c:forEach>进行打印
<c:forEach items=”${}” var=””>

</c:forEach>

获取集合中的单个元素
如果是list集合就指定下标索引即可 如果是map集合则需要使用.号运算符或者[]指定键获取值

取map集合中的某个值
<s:property value="#map1[‘key2’]"/>
取map集合中的某个值
<s:property value="#map1.key2"/>

***ognl表达式的逻辑判断操作 也是对集合的操作
使用Struts2标签结合ognl表达式判断指定集合中是否含有指定元素 in
Struts2中的判断标签 <s:if test=””> test属性指定一个表达式,表达式返回的结果要求为一个布尔类型的值

in 和 not in 运算符的操作 ognl表达式可以使用in 和 not in 来判断某个元素是否存在于指定的list集合或者map集合中(map集合判断的是值)

对于集合类型,OGNL表达式可以使用in和not in两个元素符号。其中,in表达式用来判断某个元素是否在指定的集合对象中;not in判断某个元素是否不在指定的集合对象.

<s:if test="‘aa’ in #list1">
<s:if>

OGNL表达式的投影功能 对集合遍历的单个元素进行筛选
使用ognl表达式的投影功能来完成遍历对象的筛选
操作符:? ^ $
?:获取所有符合逻辑规则的元素
^:获取第一个符合逻辑规则的元素
KaTeX parse error: Expected '}', got 'EOF' at end of input: … value="books.{#this.price>70}" var=“book”>
<s:property value="#book.name"/>

<s:property value="#book.price"/>

</s:iterator>

Struts2中的常用标签
学习Struts2提供的在页面中使用的常用标签,这些标签有一些是逻辑验证的、有一些是和html标签的功能类似的。
想要使用Struts2的标签需要事前导入Struts的标签库
<%@ taglib uri="/struts-tags" prefix=“s” %>
标签学习:
<s:property/>
<s:set><s:set>
<s:iterator></s:iterator>
<s:if></s:if>
<s:elseif></s:elseif>
<s:else></s:esle>
<s:url></s:url>
<s:param></s:param>

1、<s:property/>
property标签的功能和JSTL的out标签类似,用于将指定的值输出到页面中
value属性指定输出的值。

属性
value:指定输出的内容,value属性默认会将属性值的内容当做ognl的表达式进行处理,所以如果我们想要输出字符串的内容时,需要使用’'单引号将字符串括起,否则会默认当做表达式来运算

value属性,如果有单引号则是字符串,如果没有单引号则是表达式

value属性还支持表达式计算,直接定义表达式即可,不需要在进行单引号括起

当property标签不指定value属性时,标签会默认从当前的ValueStack对象维护的list集合的栈顶取数据进行打印

default属性 当value属性指定的值为null时,会默认输出default指定的值

escape属性
value属性中可以指定html的标签,默认是当做普通文本进行格式化处理的,如果当escape属性值为false时,则会编译html的标签

<s:property value="'<h1>value属性中可以指定html标签</h1>'" escape="true"/>

2、<s:set>标签
使用该标签可以创建集合,并向集合中存放一些数据,也可以创建一个类似于键值对形式的对象存储到ognl维护的context中
**使用set标签创建list集合
创建list集合的时候,value属性直接使用{}来指定集合元素集合
<s:set var=‘指定集合名称’ value=’{指定集合元素}’ scope=‘指定存储的域范围’>
**使用set标签创建map集合
创建map集合时,value属性的定义形式必须使用#{}的形式

<s:set var='指定集合名称' value='#{key:value}' scope='指定存储的域范围'>

使用set标签存储单个值,指定字符串时,需要使用单引号,如果不使用单引号,则会当成ognl表达式进行处理
<s:set var=‘指定存储的名称’ value=‘存储的内容使用单引号括起’ scope=‘指定存储的域范围’>

3、<s:iterator>
iterator标签可以遍历集合,获取集合中的每一个元素
当使用iterator遍历集合时,遍历出来的集合元素会默认的存储到当前ValueStack的栈顶的位置,所以在iterator中使用<s:property>标签可以不指定value值来打印遍历出来的集合元素
Struts2中的iterator标签,遍历集合对象
value属性指定遍历的集合对象 当不指定时,默认会从valueStack栈顶取
Value:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合。
id:可选属性,指定该元素的标识。(过时)
status:可选属性,该属性指定迭代时的IterateStatus实例。该实例包含如下几个方法:
int getCount(),返回当前迭代了几个元素。
int getIndex(),返回当前迭代元素的索引。
boolean isEven(),返回当前被迭代元素的索引是否是偶数。
boolean isOdd(),返回当前被迭代元素的索引是否是奇数。
boolean isFirst(),返回当前被迭代元素是否是第一个元素
boolean isLast(),返回当前被迭代元素是否是最后一个元素

4、<s:if> <s:elseif> <s:else>
逻辑判断,test属性指定一个表达式,功能和JSTL中的<c:if>一样

5、<s:url>
功能和JSTL中的<c:url>标签功能一样 可以构建一个url路径
不同的是,使用<s:url>标签可以构建一个action请求路径
action属性:对应的就是被Struts2控制器拦截处理的请求路径
action属性:对应的就是struts.xml节点的action节点的name属性Struts2控制器会拦截的action请求 使用action属性指定的action请求动作,会默认加上当前web应用

namespace属性:对应的是访问action请求的包路径 namespace
namespace属性 :对应的就是struts.xml节点的namespace属性,代表了访问action动作请求的包路径namespace不可以单独存在,必须配合action属性使用,当namespace不指定/绝对路径时,是不会加上当前应用路径的

使用action属性+namespace属性就可以构成一个会被控制器所拦截处理的请求路径

value属性:url的值可以从域中进行获取,因为实际开发中,我们的url路径可能在类中
定义或者是数据库早已经定义好了的,这时我们就必须从域中在获取定义的url路径但是url标签的value属性并不支持表达式,如果我们想要在value属性中使用表达式那么就必须使用%{}符号,在%{}中定义表达式

如果浏览器禁用Cookie,那么url标签将会对路径进行URL重写

使用param标签为url标签构造的url路径添加请求参数,如果是使用param添加多个参数,那么需要设置url的escapeAmp属性的属性值为false。
<s:url var=“url4” value="%{#addUser}" escapeAmp=“false” >
<s:param name=“username” value="‘maokexing’"></s:param>
<s:param name=“password” value="‘123123123’"></s:param>
</s:url>

Struts2中常用的表单标签

<s:form> <s:textfield> <s:password> <s:checkboxlist> <s:checkbox> <s:radio> <s:submit> <s:select>

Struts中的form标签 由form标签指定的表单提交是专门交给Struts2的控制器来处理 action属性指定请求名称 namespace属性指定包路径 同样可以指定method属性和enctype属性

checkboxlist标签 Struts2提供的复选框标签,该标签指定的list属性可以接收一个list集合或者map集合作为多个复选框存在
name属性 必须指定name属性
listKey属性:作为复选框的提交的key
listValue属性:作为复选框显示的value
value属性:被选择的复选框
实际应用:
应用在从Action类中获取被选择数据和所有的复选框数据(增删改时使用)
应用在显示JavaBean中的字段 listKey属性和listValue属性同样可以指定JavaBean中的字段名称

**防止表单重复提交 <s:token></s:token>
防止表单重复提交
使用Struts提供的一个token标签来完成表单的重复提交
防止表单的重复提交
原理:在页面的form表单中使用struts2框架提供了token标签,该标签是一个隐藏表单token标签会默认的产生一个token ID ,并且会把该id保存到session域中,当提交表单的数据时控制器会获取该token标签中的id,并且判断与session中存放的id是否一致,如果一致则执行提交的动作请求
如果不一致,则不会处理。

步骤:
1、在页面的form表单中使用struts2框架提供的token标签
2、我们需要在表单对应的请求Action节点中定义一个拦截器 token拦截器 该拦截器会负责判断表单是否重复提交

<!-- 默认的拦截器 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<!-- 判断表单是否重复提交的拦截器 -->
<interceptor-ref name="token"></interceptor-ref>

3、定义token拦截器判断表单重复提交后的处理页面, result节点的name属性为invalid.token
token拦截器判断后,如果判断表单是重复提交的话,会默认的转向一个页面进行提示指定result节点的name属性值为invalid.token来处理表单重复提交的视图页面

<result type="dispatcher" name="invalid.token">/token.jsp</result>

Struts2使用第三方插件

Struts2框架支持很多插件,如报表、整合其他框架等,这些插件都存放在struts2的lib目录下(名称有plugin的jar)

学习使用报表插件 jfreechart
struts2-jfreechart-plugin-2.1.8.1.jar
在程序运行中,可以会产生很多数据,这些数据如果想要被更清晰的展示给用户,就需要使用到报表

如何使用jfreechart插件 生成报表
1、导入jar包
*导入struts2的lib目录的struts2-jfreechart-plugin.jar
该jar包是Struts2框架支持的jfreechart插件的jar包,必须导入
*光导入框架支持的jar包还不够,我们还需要导入支持jfreechart工作的jar包 两个jar包
jcommon.jar和jfreechart.jar 这是struts2支持jfreechart插件的最少jar包

2、在web应用中创建一个Action类,在类中创建jfreechart的核心对象,组件数据,生成报表

创建Action类,在类中定义JFreeChart对象的实例,在动作方法中
构建数据,创建报表

在struts.xml配置文件中,对Action类的请求进行设置
定义一个package,JFreeChart插件规定,如果要使用该插件,那么package必须继承 jfreechart-default,该包也默认继承了Struts-default
在package节点中定义action节点,对动作类中的动作方法进行描述
result节点的type属性必须为 chart

<!-- 对浏览器访问报表的请求进行设置如果是访问JFreechartChart报表的话,定义的package节点需要继承jfreechart-default包这是使用插件必须规定的
-->
<package name="Mychart" extends="jfreechart-default">
	<action name="myChart" class="cn.itcast.action.ChartAction">
	<!-- 指定result节点,显示报表	result节点的type属性必须指定为chart 插件规定的并且result节点不需要在指定视图页面,因为报表以二进制流的方式直接传输到浏览器中 可以在result节点下定义param节点。来设置报表显示的高和宽
	-->
		<result name="success" type="chart">
			<param name="height">600</param>
			<param name="width">800</param>
		</result>	
	</action>
</package>

对象栈与属性驱动和模型驱动

/**
 *  ValueStack  对象栈 值栈 的理解
 * valueStack的内部结构 原理 为什么会放到栈顶
 * ValueStack是ognl表达式的上下文结构中的根对象(root)
 * 在struts2中ValueStack的实现类为OgnlValueStack 该对象的内部封装了一个list结构
 * 在OgnlValueStack中,封装了一个CompoundRoot对象,该对象继承了ArrayList类,该对象就相当于栈顶中存储数据的list结构
 Struts2封装表单数据到Action类对应字段的两种方式 
 * 属性驱动和模型驱动
 * 
 * 属性驱动和模型驱动
 * 两种不同的数据封装方式
 * 属性驱动:
 * JSP页面中的表单的name属性与对应的请求的action类中的字段名称一致,并且该字段提供了对应的
 * getter和setter方法 属性驱动的前提就是当前的Action对应位于ValueStack值栈的栈顶
 * 属性赋值实现过程
 * 就是通过一个拦截器来完成的 ParametersInterceptor
 * 通过该拦截器,并且要属性赋值的action类位于ValueStack的栈顶的位置 
 * 模型驱动 ModelDriven接口
 * 和属性驱动不同,属性驱动是在Action类中需要定义表单name属性对应的字段,而模型驱动则是
 * 将所有的字段统一定义到一个JavaBean中,在Action类中只需要实现一个接口,创建JavaBean对象
 * 的实例,实现ModelDriven接口中的方法即可
 * 模型驱动依赖与ModelDrivenInterceptor 该拦截器的功能是将JavaBean放置到ValueStack栈顶的位置
 * 然后在通过ParametersInterceptor拦截器进行填充数据
 * 使用模型驱动设置的JavaBean也会出现在ValueStack值栈的栈顶位置
 * 
 */

@SuppressWarnings("serial")
public class UserAction extends ActionSupport implements ModelDriven<User>
{
	private String username;

	//定义模型驱动封装数据的JavaBean的实例
	private User model = new User();
	
	//ModelDirvenInterceptor拦截器会调用该方法获取模型驱动的JavaBean的实例
	//并且将该实例放置到ValueStack值栈的栈顶位置,然后在通过ParametersInterceptor
	//拦截器设置值
	@Override
	public User getModel()
	{
		return this.model;
	}
	
	 
	
	public String getUsername()
	{
		return username;
	}



	public void setUsername(String username)
	{
		this.username = username;
	}



	public String setValue()
	{
		//通过ActionContext的实例获取ValueStack的对象
		//通过源代码可以发现 ValueStack是一个接口,ValueStack的实例是由OgnlValueStack
		//来实例化的 
		ValueStack vs = ActionContext.getContext().getValueStack();
		//在ValueStack类中定义了一个CompoundRoot类型的对象,该类默认就继承了ArrayList
		//类,CompoundRoot就是ValueStack内部的数据结构,action对象就存储到了ValueStack
		//内部的CompoundRoot实现的list结构中
		//CompoundRoot cr = vs.getRoot();
		
		//观察是否被添加到了栈顶位置
		//cr.add(1,123123);
		//方法可以向内部的CompoundRoot中的list结构中存放数据
		//vs.setValue("name","红帽子猫");
		//可以设置JavaBean中没有的字段吗 也是需要对应的JavaBean中的字段的getter和setter方法
		 
		
		//拦截器 
		ParametersInterceptor pi = new ParametersInterceptor();
		
		System.out.println(pi);
		
		System.out.println(model.getName());
		
		return Action.SUCCESS;
	} 
}	

Struts2中的结果集架构

result
结果集的本质 :Servlet中的response就规定了结果集的两种方式
1:转发 forward include
2:重定向 redirect

但是Struts2并不满意这两种结果集,又重新定义出很多基于这两种的新的结果集
Struts2的结果集的架构
***Result接口
***ServletResultSupport抽象类

分析结果集处理的几种方式,以及Struts2结果集架构为什么这么设计?
结果集的处理方式:
从页面刷新来看,可以将结果集的处理方式总的分为两种
*第一种是需要页面刷新的结果集 请求转发、请求包含、重定向等。
*第二种是AJAX技术实现的页面无刷新异步请求技术。

当页面需要刷新的时候,结果集需要获取result节点中的主体内容,按照主体内容根据结果集的处理方式来进行处理,而当页面无刷新的时候,result节点中不需要定义主体内容

这种可以将结果集的处理方式分为两种
1、有主体内容的需要刷新页面的结果集处理,有主体的结果集又需要根据需要的处理方式进行特殊处理,或是转发或是重定向。
2、无主体内容的不需要刷讯页面的结果集处理

Struts2的结果集架构针对这两种结果集处理方式定义了接口和抽象类,通过接口和抽象类的方式根据不同的结果处理方式进行不同的处理

Struts2结果集架构与接口与抽象类的功能
Result接口:Struts2结果集的规范
在Result接口中定义了一个抽象方法
public void execute(ActionInvocation invocation) throws Exception;
这个抽象方法就是Struts2结果集的规范,execute()方法中的invocation参数可以获取处理结果的信息

ServletResultSupport抽象类
该抽象类实现了Result接口,实现了execute()方法
public void execute(ActionInvocation invocation) throws Exception {
获取result标签的主体内容
lastFinalLocation = conditionalParse(location, invocation);
交给ServletResultSupport类中的抽象方法
doExecute(lastFinalLocation, invocation);
}
在ServletResultSupport抽象类中的execute()中,通过invocation对象获取了result节点中指定的主体内容,并且将结果集内容封装到本类定义的抽象方法
doExecute(lastFinalLocation, invocation);中
protected abstract void doExecute(String finalLocation, ActionInvocation invocation) throws Exception;

Result接口和ServletResultSupport抽象类
在接口中定义规范,抽象类中实现这个规范,并且根据对于具体的实现通过抽象方法交给实现类去处理
这就是Struts2的结果集架构
当我们result节点不需要主体内容的时候,只需要实现result接口即可
当我们result节点需要主体内容,则实现ServletResultSupport抽象类,复写doExecute()方法根据主体内容在进行细致的操作,具体结果集处理的类型交给实现ServletResultSupport的实现类去做,而ServletResultSupport只负责将主体内容的数据封装到抽象方法,具体的结果处理类型交给实现类中去处理

这就是Struts2结果集架构处理结果集的方式

从接口、抽象类、实现类可以总结出
在业务设计上,我们将最顶层的规范抽象到接口中,继而由我们的抽象类实现这个接口,复写这个规范,然后抽象类又可以根据这个规范进而产生新的特性,具体的特性又在抽象类中继续抽象,但是仍然保留了接口中的规范
具体的实现步骤就交给实现类去做。


自定义结果集
实现Result接口或者ServletResultSupport抽象类
在package节点中进行声明

<package name="jfreechart-default" extends="struts-default">
   	<result-types>
   		<result-type name="chart" class="">
   			<param name="height">150</param>
   			<param name="width">200</param>
   		</result-type>
   	</result-types>
</package>

tomcat启动的时候,Struts2框架做了些什么事?
我们在配置Struts2的环境的时候,在web.xml文件下配置了一个过滤器
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
StrutsPrepareAndExecuteFilter,当tomcat启动的时候,会加载StrutsPrepareAndExecuteFilter对象进内存中,并且调用对象内部的init()方法,Struts2的初始化工作就开始了

在StrutsPrepareAndExecuteFilter的init()方法运行的时候
会依次加载如下的东西
1、default.properties配置文件
该配置文件中定义了Struts2框架的一些常量信息
2、加载struts-default.xml文件
struts-default.xml配置文件中定义了Struts2核心的拦截器、结果处理方式
加载bean Interceptor result
另外struts-default.xml文件中的bean节点规定了Struts2框架的一些核心类
ObjectFactory 当加载bean节点时,会加载bean节点指定的ObjectFactory
这是一个对象工厂,用于创建Struts2核心的action、Interceptor、validator

在ObjectFactory类中就定义了对应的buildXxx()方法用于创建对象的初始化
我们发出的请求被Struts2控制器所拦截交给Action对象处理,Action对象就是由ObjectFactory对象中的buildAction()所创建的

<bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />
<bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />

我们也可以在自定义的Struts2的核心配置文件struts.xml中定义
节点,指定创建Action的方式,因为struts.xml要比struts-default.xml文件后加载,会覆盖先定义的bean
spring框架会负责Struts2框架创建Action对象

3、加载struts-plugin.xml 文件
Struts2框架提供了很多插件,包括hibernate和spring框架的整合都可以使用插件来进行,所以Struts2框架通过struts-plugin.xml来配置插件的信息,在struts-plugin.xml文件中可以做很多事
package、result-type 、bean…

struts-plugin.xml struts-default.xml struts.xml这三个配置文件使用的dtd约束是同一个

4、加载struts.xml文件
加载程序员自定义的配置文件,在struts.xml文件中通过include节点包含进来的配置文件

Struts2的核心解析

Struts2的控制器时一个Filter过滤器,当浏览器发出一次请求时,Struts2的控制器都做了什么?

Struts2请求流程

1、创建ActionContext对象
创建ValueStack
将ActionContext放入到ThreadLocal中
2、创建ActionProxy对象
1、创建Action对象
2、将Action对象放置到ValueStack的栈顶位置
3、获取Struts2默认的所有拦截器
3、ActionProxy.execute()
执行所有的拦截器
执行action类中的动作方法
执行结果集

Struts2的流程

分为两个出发点来出发
1、当服务端容器启动的时候 Struts2框架做了什么
2、当浏览器请求服务端资源,被Struts2控制器拦截请求后,Struts2又做了什么

当Tomcat服务器启动的时候,会加载在web.xml文件中配置的Struts2的核心控制器 StrutsPrepareAndExecuteFilter,创建实例对象,并且调用init()方法初始化该对象,在调用init()方法的时候,就会对Struts2控制器进行一系列的初始化动作
执行init()方法时,就会默认的加载如下的配置文件
1、default.properties 配置文件中规定了struts2框架使用的一些常量信息
2、struts-default.xml 配置文件中声明了struts2使用的拦截器、结果集信息 Interceptor result
当加载struts-default.xml文件的时候,会加载配置文件中的节点指定的ObjectFactory对象的实例,该对象是一个对象工厂,用于创建拦截器、结果集和我们请求的action对象。在对象中封装了对应的buildResult()/buildInterceptor()/buildAction()方法,当浏览器请求Action的时候,ObjectFactory就会创建Action的对象

3、struts-plugin.xml 加载struts-plugin.xml文件,该配置文件就定义了struts中插件的使用方式
4、struts.xml文件 加载定义的struts的配置文件

当struts2框架的控制器拦截浏览器的一次请求时,Struts2的处理流程
1、struts2框架会获取这次请求的url,判断是否被struts2框架拦截处理,如果需要则继续处理,如果不需要则访问想要访问的资源
2、执行struts2框架的控制器的doFilter()方法 struts2框架的控制器使用了一个Filter作为其控制器,执行doFilter()方法,对request、response以及编码等信息进行设置
3、创建ActionContext对象,创建ValueStack对象,并且将ActionContext对象存放到当前的ThreadLocal中
4、创建Action对象,并且将Action对象放置到ValueStack对象中的栈顶位置,暴漏访问的Action都对象的属性信息
5、创建Struts2默认的所有的拦截器对象,并且创建拦截器的迭代器
6、执行所有的拦截器,对Action中的字段进行处理,如上传文件,属性封装等
7、执行Action类中的请求的动作方法
8、根据动作方法返回的String类型匹配struts2配置文件中的结果集处理器 result
9、对结果集进行处理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值