Struts1.2 介绍(1)

Web组件的三种关联关系

  • 请求转发
  • URL重定向 
  • 包含

    存在以上关联关系的Web组件可以是JSP或Servlet,对于Struts应用,则还包括Action。这些Web组件都可以访问HttpServletRequest和HttpServletResponse对象,具有处理请求、生成响应结果的功能。

 

    1、请求转发


 

请求转发的代码:

 

request.getRequestDispatcher(“hello.jsp”).forward(request, response);

之所以要将request和response当做forward方法的参数,正说明转发与被转发的Web组件之间使用的是同一个request对象.

在JSP页面中, 可以使用<jsp:forward>标签来转发请求, 例如:

 

<jsp:forward page=”hello.jsp”/>

 

对于请求转发, 转发的源组件和目标组件共享request范围内的共享数据.


 2、URL重定向

 URL重定向与请求转发相似, 但也有着本质的不同

  • Web组件可以将请求重定向到任一UL, 而不仅仅是同一应用中的URL. 
  • 重定向的源组件和目标组件之间不共用同一个HttpServletRequest对象, 因此不能共享request范围内的共享数据


 

    如果当前应用的Servlet组件要把请求转发给URL “http://localhost:8080/asp1.1”,可以在Servlet的service()方法中执行以下代码:

 

response.sendRedirect(“http://localhost:8080/asp1.1”);

3、包含

    包含关系允许一个Web组件聚集来自同一个应用中其他Web组件的输出数据,并使用被聚集的数据来创建响应结果. 包含关系的源组件和目标组件共用同一个HttpServletRequest对象, 因此它们共享request范围内的共享数据. 

 

 


     Servlet类使用javax.servlet.RequestDispatcher.include()方法包含其他的Web组件. 例如, 如果当前的Servlet组件包含了三个JSP文件: header.jsp、main.jsp和footer.jsp, 则可以在Servlet的service()方法中执行以下代码:

 

 

RequestDispatcher rd;
rd = req.getRequestDispatcher(“/header.jsp”);
rd.include(req, res);
rd = req.getRequestDispatcher(“/main.jsp”);
rd.include(req, res);
rd = req.getRequestsDispatcher(“/footer.jsp”);
rd.include(req, res);

 

在JSP文件中, 可以通过<include>指令来包含其他的Web资源, 例如:

 

	<%@ include file=”header.jsp”%>
	<%@ include file=”mainjsp”%>
	<%@ include file=”footer.jsp”%> 


MVC概述
MVC(Model-View-Controller), 即模型 – 视图 – 控制器. 
MVC Model 1:

 

 4MVC Model 1

 



 5MVC Model 2

 

    Model 1 与 Model 2 的区别,Model 2 下 JSP 不再承担控制器的责任,它仅仅是表现层角色,仅仅用于将结果呈现给用户,JSP页面的请求与Servlet(控制器)交互,而Servlet负责与后台的JavaBean通信。在Model 2 模式下,模型(Model)由JavaBean充当.

 

Struts,MVC 2 的一种实现



 图6 Struts 实现的MVC框架

关于Action 和 ActionForm Bean 在 MVC 里面的角色的问题的讨论
关于Struts中MVC划分的疑问? 2007年05月27日 12:09
表现层框架 设计模式 struts mvc模式

Struts是Java MVC WEB开发框架的事实标准,网上看了很多关于Struts组件在MVC模式中的划分,着实让初学Struts的我感到很混乱.
其中争议最多的是ActionForm,有人说它是属于Model层,有人把它归为View,我倾向把ActionForm归属于Model,因为ActionForm是和数据打交道的,在V和C中传输数据的桥梁,但是比起在Struts+Spring+hibernate架构中的Model,Dao之类的,把他称为模型感觉有点不合适.
再有就是Action了,有的把它归为M,有的是C,理论上来说Action因该属于C,但是想想ActionServlet,它才是老板,决定处理请求的Action,Action只是和业务层沟通,我觉得Action还是属于C层,毕竟它还要完成最后JSP页面的跳转控制和为V层准备数据,但是有时候在Action中写太多的业务逻辑的话,又感觉它有点像M,哎,郁闷啊,毕业设计做了个MVC的系统,现在对它的分层还不是很理解,答辩的时候不知道怎么去说啊


fw2003


发表文章: 12
注册时间: 2007年05月23日 08:57
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年05月28日 08:25

一般来说 actionservlet是控制器 而action是真正的业务实现者 也就是在action中调用相应的业务逻辑组件来完成数据交互 这样的话action应该也属于c
但是一般的小型应用 业务逻辑层的东西很少的话 也都是写在action里的

以上是我个人看法


coolzyt


发表文章: 10
注册时间: 2007年04月15日 09:05
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年05月28日 10:17

struts实现了MVC,actionform是model,action是c
加上Spring,hibernate就是多层架构了,又有新的model,但和mvc就没关系了


zhaochunhui


发表文章: 1
注册时间: 2007年05月28日 09:53
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年05月28日 10:31

actionfrom 是对应 view(如:jsp)的module,也就是form中有什么 actionform中就有什么 ,而不是对应持久层的,当页面流程出现变化 actionform也会出现变化 它不应该被传递到 业务层否则代码会出现相当大的改动
而action应该是 控制层(Control),里面不应该写过多的业务代码 它应该起到桥梁的作用


hwangita


发表文章: 21
注册时间: 2007年03月27日 22:52
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年06月03日 18:16

action应该是c啊,actionservlet是全局的,毫无疑问。
其实,我觉得,你吧整个流程搞清楚了,何必苦苦执着于M,V,C呢?

我的理解,一般开发时,在Struts配置文件里先写,

顺数大都这样,
先从jsp页面开始,经过actionform把数据取出来传给action,这里面有简单的业务处理,其实,真正的业务处理应该是在javabean中,action最后还要负责跳转,
而全局的控制器就是actionservlet。

所以,我觉得,局限于action和actionform的层次,是浪费你的精力,关注点不再这里!



ljw714


发表文章: 16
注册时间: 2006年05月02日 16:00
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年06月06日 17:37

actionform 是属于v层,它并不是进行数据持久化的.如你使用的是纯Struts的话,也就是说你把数据访问都写在action中,那action既是c层又是m层了.一般是会写数据访问类DAO的.其实现在使用Struts+spring+hibernate,整个Struts的东西动被看做是v层的.这是我的理解.


fw2003


发表文章: 12
注册时间: 2007年05月23日 08:57
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年06月13日 18:11


将J2EE的分层体系简单的认为是MVC 我觉得是错误的
J2EE大体分为 WEB层 业务层 持久层
那么MVC只是WEB层的实现模式而已
而Struts正是这样一种体现MVC模式的框架,也就是说你的系统的整个WEB层都由Struts来构建 而Hibernate则负责构建持久层




jacshan


发表文章: 17
注册时间: 2006年09月14日 20:32
悄悄话
个人博客
回复:re:关于Struts中MVC划分的疑问? 2007年06月14日 21:56

现在就是相对于Struts的MVC中,MVC分别是对应Struts的哪几个组件?
当然在Struts+Spring+Hibernate的架构中,那M应该是由Spring管理的业务层组件还有就是DAO接口和 其实现类吧


banq


发表文章: 10146
注册时间: 2002年08月03日 17:08
悄悄话
个人博客
回复:回复:re:关于Struts中MVC划分的疑问? 2007年06月15日 16:39

>现在就是相对于Struts的MVC中,MVC分别是对应Struts的哪几个组件?

Struts的Action属于MVC的C (controller); Struts的标签+html的JSP属于V(View),而ActionForm可以看成是Model的一个映射,在Struts2.0中Model可以直接作为Struts的ActionForm,MVC的M还有表示业务层Model的意思。



gougou3250


发表文章: 98
注册时间: 2007年05月08日 16:48
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年06月15日 18:16

用Struts2吧 么有actionForm


jacshan


发表文章: 17
注册时间: 2006年09月14日 20:32
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年06月15日 23:58

呵呵,明天答辩了,论文上是参考IBM上的几篇Struts的,他们是把Actoin做为Model
其实这分做什么并不是很重要,但是答辩嘛,还是的有个明确的说法的
[该贴被jacshan于2007年06月15日 23:59修改过]


gougou3250


发表文章: 98
注册时间: 2007年05月08日 16:48
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年06月16日 08:52

action应该是control吧,不应该是module


boyszz


发表文章: 15
注册时间: 2007年09月09日 04:22
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年09月09日 04:56

ActionForm是属于Model层的,毫无疑问。
需要提一下的是,WEB架构里是不存在MVC这样的架构的,MVC架构的名词起源于富客户端.
Model,即是你的领域对象,比如你的程序是展现一个人的基本信息,那这个领域对象就应该是Person这个类.(Struts需要领域对象继承ActionForm来自动把表单数据映射到对象,我想应该没有人会觉得这是个好的设计.)
Controller,是你的控制器,通常是根据用户的行为,执行改变你的领域对象操作.
View,是你的展现层,决定怎样的方式展现你的领域对象,比如你希望通过一个表格来展现,那这个表格就应该由View层来画.
MVC,是指当用户执行动作,Controller使Model发生改变,View层自动更新Model对象,而这在WEB应用里是不可能的.因为http连接是无状态的.不过因为两者的区别也不是太大,所以还是经常用MVC来代替WEB的架构体系.
[该贴被boyszz于2007年09月09日 04:59修改过]


hiworld


发表文章: 23
注册时间: 2007年09月09日 17:37
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2007年09月09日 19:42

我个人认为这个问题应该分开来说,针对不同的应用,担任不同的角色.在多层次的结构中.有的时候部分业务在其他层次中实现更加简便.因为在设计阶段就离散部分业务逻辑.因此为业务逻辑有时会渗透甚至融合到表示层和持久层.这一现象针对简单需求而产生的结构退化.或者是对特殊需求的正确权衡.一般来说,action是C层,但如果里面参入了太多的业务逻辑的话.就是M层了.而actionform有时候是V层.在没有各其他框架发生关系的情况下.actionform就是V层.如查Struts加入了其他框架.像Spring,hibernate.actionform就是M层.所以针对不同的情况与应用.角色会发生变化,这是应用的需要,也是Struts的扩展性.


boyszz


发表文章: 15
注册时间: 2007年09月09日 04:22
悄悄话
个人博客
回复:re:关于Struts中MVC划分的疑问? 2007年09月09日 20:06

MVC架构只是针对你的WEB层设计,并不是你整个J2EE的架构.通常,你的一个项目是由WEB层(或者说是UI层,这样更通用一点)、业务层和持久层构成的。
ActionForm是什么?是用来对应你表单里的数据的,通常也会是一个领域对象,例如用户注册的一个表单Form,那这个表单数据最终会生成一个User这样的领域对象,并调用业务层UserService.addUser( User user )这样一个方法来进行保存用户数据,判断用户名是否重复,写进数据库这些操作都应该是addUser方法做的。所以他属于Modle,如果一个领域对象不需要通过继承ActionForm,就可以映射到一个表单,对我们开发来说那就更好了。至少。你不用写一个User,一个UserForm,两个并无区别的对象.这也是Struts设计上的一个失误.并受人指责.

回复:回复:re:关于Struts中MVC划分的疑问? 2007年09月10日 09:52
表现层框架 设计模式 struts mvc模式

看一个东西是什么层次,就必须想想这个东西是怎么来的,为什么会有这个东西。

那ActionForm来看,为什么Struts设计的时候会有这么个玩意儿?其实目的很简单,两个目的(其实是一个目的),第一,将你提交的数据自动转换成一个类,第二,将执行完业务逻辑的结果数据自动映射到你的jsp中。所以ActionForm目的是为了和jsp中的哪些标签做一个自动匹配的玩意。那结论就简单了,既然jsp是属于View的,那么ActionForm自然也是属于View的,因为如果你改变了jsp(当然不是修改简单的文本什么的),那么你的ActionForm也需要跟着改变。

在从另外一个角度来看,什么是Model,那是业务模型,什么情况下业务模型会变,当然是业务变化的时候,模型才会变化,模型怎么可能随着jsp的变化而变化呢。

那么为什么会有人会觉得ActionForm是Model呢,因为这样可以减少一个从Model到ActionForm的转换啊。但是假如现在我们要增加一个非浏览器的Client,同样使用这个业务逻辑,怎么办,也让这个Client使用ActionForm?



freebox


发表文章: 365
注册时间: 2008年03月01日 01:08
悄悄话
个人博客
re:关于Struts中MVC划分的疑问? 2008年05月28日 10:48

不必保证领域实体和formBean是两个类,只要保证它们是两个对象即可。在领域实体和form一致的时候可以不必要再做一个formBean类,只要生成一个formBean对象即可。当然在不一致的时候formBean类还是有价值的。
  
另一篇关于Struts的关于MVC的文章,这里转载过来供大家讨论:
  

版权所有:(xiaodaoxiaodao)蓝小刀    [email protected]

http://www.blogjava.net/xiaodaoxiaodao/archive/2007/03/26/106522.html       

转载请注明来源/作者

 

struts 学习笔记之MVC模式

 

struts 中的MVC架构如下:

 



 

 

 

View :由JSPStruts自定义标记库、资源文件(MessageResources.properties)共同组成,通过ActionForm实现JSP表单的封装,并映射到Model部分中JavaBean的相应属性中,完成用户数据的封装。

 

注意:关于ActionForm,有些人认为它属于Model层(仁者见仁,智者见智)。

事实上它不是Model,真正的Model应该是一个封装了业务逻辑的的对象。

ActionForm仅仅是一个form-bean,封装了用户提交的表单数据(物理View),可以在其中进行一些非业务逻辑的验证,并没有真正的映射到模型数据,因为与层的关系比较密切,所以实际上它应该算是View层(逻辑View)。

 

Action 处理器对象可以直接对ActionForm进行读写,而不再需要和requestresponse对象进行数据交互。通过ActionForm组件对象实现了对ViewModel之间交互的支持。M模型层,原则上来说和业务逻辑有关的东西都在这里处理。

 

Controller :接收客户端的request,进行业务逻辑处理,response到客户端。在StrutsController功能由图中ActionServletActionMapping对象构成:核心是一个Servlet类型的对象ActionServlet(在struts-config.xml中配置),实际上是一个前端控制器(Front Controller)。ActionServlet根据ActionMapping对象的定义跳转到不同的Action,每个ActionMapping对象实现了一个requestAction对象之间的映射。

 

Controller层负责流程的控制,在处理Model层与View层之间的交互的同时,又将两者分离开来,从而实现了MVC模式。Model层包含了应用的核心部分,业务逻辑数据存取View层负责应用的界面。

 

在上面的图中看到Action位于Controller层,但也有很多人把它归于Model层,实际上 Action 仅仅描述"做什么",与"如何做"Model)关系不大,把它归于 Controller 层比较合适。

 

Model :一般Model层可以划分为三部分:公共入口业务逻辑Bussiness Logic),数据持久化DAO+JavaBean)。

Spring+Hibernate的架构中,M可以用 Spring Bussiness Interface + Bussiness Implement)表示业务逻辑Hibernate实现数据持久化


 

 

PO/POJO/BO/DTO/VO的区别

---------------------------------------------------------

PO persistent object持久对象

1 .有时也被称为Data对象,对应数据库中的entity,可以简单认为一个PO对应数据库中的一条记录。

2 .在hibernate持久化框架中与insert/delet操作密切相关。

3 PO中不应该包含任何对数据库的操作。

 

---------------------------------------------------------

POJO plain ordinary java object 无规则简单java对象

一个中间对象,可以转化为PODTOVO

 

1 POJO持久化之后==PO

(在运行期,由Hibernate中的cglib动态把POJO转换为POPO相对于POJO会增加一些用来管理数据库entity状态的属性和方法。PO对于programmer来说完全透明,由于是运行期生成PO,所以可以支持增量编译,增量调试。)

2 POJO传输过程中==DTO

3 POJO用作表示层==VO

 

PO VO都应该属于它。

 

----------------------------------------------------------

BO business object 业务对象

封装业务逻辑为一个对象(可以包括多个PO,通常需要将BO转化成PO,才能进行数据的持久化,反之,从DB中得到的PO,需要转化成BO才能在业务层使用)。

关于BO主要有三种概念

1 、只包含业务对象的属性;

2 、只包含业务方法;

3 、两者都包含。

在实际使用中,认为哪一种概念正确并不重要,关键是实际应用中适合自己项目的需要。

 

----------------------------------------------------------

VO value object值对象 / view object表现层对象

1 .主要对应页面显示(web页面/swtswing界面)的数据对象。

2 .可以和表对应,也可以不,这根据业务的需要。

 

:在struts中,用ActionFormVO,需要做一个转换,因为PO是面向对象的,而ActionForm是和view对应的,要将几个PO要显示的属性合成一个ActionForm,可以使用BeanUtilscopy方法。

 

----------------------------------------------------------

DTO TO Data Transfer Object数据传输对象

1 .用在需要跨进程或远程传输时,它不应该包含业务逻辑。

2 .比如一张表有100个字段,那么对应的PO就有100个属性(大多数情况下,DTO 内的数据来自多个表)。但view层只需显示10个字段,没有必要把整个PO对象传递到client,这时我们就可以用只有这10个属性的DTO来传输数据到client,这样也不会暴露server端表结构。到达客户端以后,如果用这个对象来对应界面显示,那此时它的身份就转为VO

 

----------------------------------------------------------

DAO data access object数据访问对象

1 .主要用来封装对DB的访问(CRUD操作)。

2 .通过接收Business层的数据,把POJO持久化为PO

 

 

Struts概览

  • 视图

视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并能接收用户的输入数据,但是它并不进行任何实际的业务处理。视图可以像模型查询业务状态,但不能改变模型。视图还能接受模型发出的数据更新事件,从而对用户界面进行同步更新。

Web工程中,视图就是一组JSP文件。在这些JSP文件中没有业务逻辑,也没有模型信息,只有标签。

此外,通常把Struts框架中的ActionForm Bean 也划分到视图模块中。ActionForm Bean 也是一种JavaBean,Struts框架把用户输入的表单数据保存在ActionForm Bean 中,把它传递给控制器,控制器可以对ActionForm Bean中的数据进行修改,JSP文件使用Struts标签读取修改后的ActionForm Bean的信息,重新设置HTML表单。



 图7 ActionForm Bean 的作用

  • 模型

模型是应用程序的主体部分。模型表示业务数据和业务逻辑。一个模型能为多个视图提供数据。由于同一个模型可以被多个视图重用,所以提高了应用的可重用性。大型应用中,业务逻辑通常由JavaBean或EJB组件来实现。

  • 控制器

控制器接手用户的输入并调用模型和视图去完成用户的需求。当Web用户单击Web页面中的提交按钮来发送HTML表单时,控制器接收请求并调用相应的模型组件去处理请求,然后调用相应的视图来显示模型返回的数据。

Struts控制器由ActionServlet类和Action类来实现。ActionServlet类是Struts框架中的核心组件。ActionServlet继承了javax.servlet.http.HttpServlet类,它在MVC模型中扮演中央控制器的角色。ActionServlet主要负责接收HTTP请求信息,根据配置文件struts-config.xml的配置信息,把请求转发给适当的Action对象。如果该Action对象不存在,ActionServlet会先创建这个Acton对象。

Action类负责调用模型的方法,更新模型的状态,并帮助控制应用程序的流程。

  • Struts的配置文件struts-config.xml

上面讲到一个用户请求是通过ActionServlet来处理和转发的。那么,ActionServlet如何决定把用户请求转发给哪个Action对象呢?在Struts中,这些配置信息都存储在特定的XML文件struts-config.xml中,在该配置文件中,每一个Acton的映射信息都通过一个<action>元素来配置。

这些配置信息在系统启动时被读入内存,供Struts在运行期间使用。在内存中,每一个<action>元素都对应一个org.apache.struts.action.ActionMapping类的实例。

 

 

Struts的工作流程

 

对于采用Struts框架的Web应用,在Web应用启动时就会加载并初始化ActionServlet,ActionServlet从struts-config.xml文件中读取配置信息,把它们存放到各种配置对象中,例如Action的映射信息存放在ActionMapping对象中。

当ActionServlet接收到一个客户请求时,将执行如下流程。


(1)检索和用户请求匹配的ActionMapping实例,如果不存在,就返回用户请求路径无效的信息

(2)如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中。

(3)根据配置信息决定是否需要表单验证。如果需要验证,就调用ActionForm的validate()方法。

(4)如果ActionForm的validate()方法返回null或返回一个不包含ActionMessage的ActionErrors对象,就表示表单验证成功。

(5)ActionServlet根据ActionMapping实例包含的映射信息决定将请求转发给哪个Action。如果相应的Action实例不存在,就先创建这个实例,然后调用Action的execute()方法。

(6)Action的execute()方法返回一个ActionForward对象,ActionServlet再把客户请求转发ActionForward对象指向的JSP组件。

(7)ActionForward对象指向的JSP组件动态生成网页,返回给客户。

对于以上流程(4)、如果ActionForm的validate()方法返回一个包含一个或多个ActionMessage的ActionErrors对象,就表示表单验证失败,此时ActionServlet将直接把请求转发给包含用户提交表单的JSP组件。在这种情况下,不会在创建Action对象并调用Action的execute()方法。

 


图8 Struts响应用户请求的工作流程

 

 

 

第一个实例:helloapp

1、创建视图组件

 

  • 一个JSP文件,hello.jsp
  • 一个ActionForm Bean:HelloForm Bean

 

hello.jsp

 

<%@ page language="java" pageEncoding="utf-8"%>

<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html:html lang="true">
  <head>
    <html:base />
    
    <title><bean:message key="hello.jsp.title"/></title>

	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->

  </head>
  
  <body bgcolor="white">
    <h2><bean:message key="hello.jsp.page.heading"/></h2><p>
    <html:errors/><p>
    
    <logic:present name="personbean" scope="request">
    	<h2>
    		<bean:message key="hello.jsp.page.hello"/>
    		<bean:write name="personbean" property="userName"/>!<p>
    	</h2>
    </logic:present>
    
    <html:form action="/HelloWorld.do" focus="userName">
    	<bean:message key="hello.jsp.prompt.person"/>
    	<html:text property="userName" size="16" maxlength="16"></html:text><br>
    	<html:submit property="submit" value="Submit"></html:submit>
    </html:form><br>
    
    <html:image page="/struts-power,gif" alt="Powered by Struts"></html:image>
  </body>
</html:html>

 

 

2、创建消息资源文件

     hello.jsp使用<bean:message>标签来输出文本内容。这些文本来自于Resource Bundle,每个Resource Bundle都对应一个或多个本地的消息资源文件,本例中的资源文件为application.properties,

 

#Application Resources for the "Hello" sample application

#Application Resources that are specific to the hello.jsp file

hello.jsp.title=Hello - A first Struts program
hello.jsp.page.heading=Hello World! A first Struts application
hello.jsp.prompt.person=Please enter a UserName to say hello to :
hello.jsp.page.hello=Hello 


#Validation and error messages for HelloForm.java and HelloAction.java

hello.dont.talk.to.monster=We don't want to say hello to Monster!!!
hello.no.username.error=Please enter a <i>UserName</i> to say hello to!

 

 

3、创建ActionForm Bean

Struts框架中定义的ActionForm类是抽象的,必须在应用中创建它的子类,来放具体的HTML表单数据。

HelloForm.java

 

package hello;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

public final class HelloForm extends ActionForm {

    private String userName = null;

    public String getUserName() {
        return (this.userName);
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
   
    /**
     * Reset all properties to their default values.
     */
    public void reset(ActionMapping mapping, HttpServletRequest request) {
        this.userName = null;
    }

    /**
     * Validate the properties posted in this request. If validation errors are
     * found, return an <code>ActionErrors</code> object containing the errors.
     * If no validation errors occur, return <code>null</code> or an empty
     * <code>ActionErrors</code> object.
     */
    public ActionErrors validate(ActionMapping mapping,
                                 HttpServletRequest request) {

        ActionErrors errors = new ActionErrors();

        if ((userName == null) || (userName.length() < 1))
            errors.add("username", new ActionMessage("hello.no.username.error"));

        return errors;
    }
}

 

从以上代码可以看出,ActionForm Bean 实质上是一种JavaBean,不过它除了具有JavaBean的常规方法,还有两种特殊方法:

 

  • validate(): 用于表单验证
  • reset(): 把属性重新设置为默认值

 

4、数据验证

Struts框架中的数据验证分为两种类型:表单验证和业务逻辑验证 

(1)表单验证,HelloForm中的validate()方法

 

public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) 
{
        ActionErrors errors = new ActionErrors();

        if ((userName == null) || (userName.length() < 1))
        {
                errors.add("username", new ActionMessage("hello.no.username.error"));
        }

        return errors;
}

 

当用户提交了HTML表单后,Struts框架将自动把表单数据组装到ActionForm Bean中。接下来Struts框架会自动调用ActionForm Bean的validate()方法进行表单验证。如果validate()方法返回的ActionErrors对象为null,或者不包含任何ActionMessage对象,就表示没有错误,数据验证通过。如果ActionErrors中包含ActionMessage对象,就表示发生了验证错误,Struts框架会把ActionErrors对象保存到request范围内,然后把请求转发到恰当的视图组件,视图组件通过<html:errors>标签把request范围内的ActionErrors对象中包含的错误消息显示出来,提示用户修改错误。 

(2)业务逻辑验证,业务逻辑验证由Action负责处理。

 

5、创建控制器组件

控制器组件包括ActionServlet类和Action类。ActionServlet类是Struts框架自带的,它是整个Struts框架的控制枢纽。Struts框架提供了可扩展的Action类,它用来处理特定的HTTP请求。

HelloAction.java

 

package hello;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.util.MessageResources;

public final class HelloAction extends Action {

    /**
     * Process the specified HTTP request, and create the corresponding HTTP
     * response (or forward to another web component that will create it).
     * Return an <code>ActionForward</code> instance describing where and how
     * control should be forwarded, or <code>null</code> if the response has
     * already been completed.
     */
    public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
    throws Exception {

        // These "messages" come from the ApplicationResources.properties file
        MessageResources messages = getResources(request);

        /*
         * Validate the request parameters specified by the user
         * Note: Basic field validation done in HelloForm.java
         *       Business logic validation done in HelloAction.java
         */
        ActionMessages errors = new ActionMessages();
        String userName = (String)((HelloForm) form).getUserName();

        String badUserName = "Monster";

        if (userName.equalsIgnoreCase(badUserName)) {
           errors.add("username", new ActionMessage("hello.dont.talk.to.monster", badUserName ));
           saveErrors(request, errors);
           return (new ActionForward(mapping.getInput()));
        }

        /*
         * Having received and validated the data submitted
         * from the View, we now update the model
         */
        PersonBean pb = new PersonBean();
        pb.setUserName(userName);
        pb.saveToPersistentStore();

        /*
         * If there was a choice of View components that depended on the model
         * (or some other) status, we'd make the decision here as to which
         * to display. In this case, there is only one View component.
         *
         * We pass data to the View components by setting them as attributes
         * in the page, request, session or servlet context. In this case, the
         * most appropriate scoping is the "request" context since the data
         * will not be neaded after the View is generated.
         *
         * Constants.PERSON_KEY provides a key accessible by both the
         * Controller component (i.e. this class) and the View component
         * (i.e. the jsp file we forward to).
         */

        request.setAttribute( Constants.PERSON_KEY, pb);

        // Remove the Form Bean - don't need to carry values forward
        request.removeAttribute(mapping.getAttribute());

        // Forward control to the specified success URI
        return (mapping.findForward("SayHello"));

    }
}

 

 

Action 的工作机制

所有的Action类都是org.apache.struts.action.Action的子类。Action子类应该覆盖父类的execute()方法。当ActionForm Bean被创建,并且表单验证通过后,Struts框架就会调用ActionForm类的execute()方法。

 

访问封装在MessageResources中的本地化文本

 

在本例中,Action类的execute()方法首先获得MessageResources对象:

 

MessageResources message = getResources(request);

在Action类中定义了getResources(HttpServletRequest request)方法,该方法返回当前默认的MessageResources对象,它封装了Resource Bundle中的文本内容。接下来Action类就可以通过MessageResources对象访问文本内容。例如,如果要读取消息key为“hello.jsp.title”对应的文本内容,可以调用MessageResources类的getMessage(String key)方法:

 

String title = messages.getMessage("hello.jsp.title");

 

业务逻辑验证 

 

        ActionMessages errors = new ActionMessages();
        String userName = (String)((HelloForm) form).getUserName();

        String badUserName = "Monster";

        if (userName.equalsIgnoreCase(badUserName)) {
           errors.add("username", new ActionMessage("hello.dont.talk.to.monster", badUserName ));
           saveErrors(request, errors);
           return (new ActionForward(mapping.getInput()));
        }

 

 

6、访问模型组件

 

HelloAction类创建了一个模型组件PersonBean对象,并调用它的saveToPersistentStore()方法保存userName属性:

 

PersonBean pb = new PersonBean();
pb.setUserName(userName);
pb.saveToPersistentStore();

 

 

7、向视图组件传递数据

Action类把数据存放在request或session范围内,以便向视图组件传递信息。

request.setAttribute( Constants.PERSON_KEY, pb);

// Remove the Form Bean - don't need to carry values forward
request.removeAttribute(mapping.getAttribute());

以上代码完成两件事:

 

  • 把PersonBean对象保存在request范围内
  • 从request范围内删除ActionForm Bean。由于后续的请求转发目标组件不再需要HelloForm Bean,所以可将它删除

 

 

8、把HTTP请求转发给合适的视图组件

 

        // Forward control to the specified success URI
        return (mapping.findForward("SayHello"));

 

9、服务器端装载 hello.jsp 的流程

 

(1)<bean:message>标签从Resource Bundle 中读取文本, 把它输出到网页上。

(2)<html:form>标签在 request 范围中查找 HelloForm Bean。如果存在这样的实例,就把 HelloForm 对象中的 UserName 属性赋值给 HTML 表单的 userName 文本框。由于页面首次加载时还不存在 HelloForm 对象,所以忽略这项操作。

(3)把hello.jsp的视图呈现给客户

 

10、表单验证的流程

在hello.jsp网页上,不输入姓名,直接单击【Submit】按钮,会看到下图中的网页:



图9、表单验证失败的hello.jsp 

当客户提交 HelloForm 表单时,请求路径为“、HelloWorld.do”;

 

<html:form action="/HelloWorld.do" focus="userName">

那么服务器端执行表单验证的流程如下:

(1)Servlet容器在web.xml文件中寻找<url-pattern>属性为“*.do”的<servlet-mapping>元素:

 

	<servlet-mapping>
		<servlet-name>action</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

(2)Servlet容器依据以上<servlet-mapping>元素的<servlet-name>属性“action”,在web.xml文件中寻找匹配的<servlet>元素:

  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
     <load-on-startup>2</load-on-startup>
  </servlet>

(3)Servlet容器把请求转发给以上<servlet>元素指定的ActionServlet,ActionServlet依据用户请求路径“、HelloWorld.do”,在Struts配置文件中检索path属性为“、HelloWorld”的<action>元素:

    <action    path      = "/HelloWorld"
               type      = "hello.HelloAction"
               name      = "HelloForm"
               scope     = "request"
               validate  = "true"
               input     = "/hello.jsp">
        <forward name="SayHello" path="/hello.jsp" />
    </action>

   更确切的说,ActionServlet此时检索的是ActionMapping对象,而不是直接访问Struts配置文件中的<action>元素。因为在ActionServlet初始化的时候,会加载Struts配置文件,把各种配置信息保存在相应的配置类的实例中,例如<action>元素的配置信息放在ActionMapping对象中。

(4)ActionServlet根据<action>元素的name属性,创建一个HelloForm对象,把客户提交的表单数据传给HelloForm对象,再把HelloForm对象保存在保存在<action>元素的scope属性指定的request范围内。

(5)由于<action>元素的validate属性为true,ActionServlet调用HelloForm对象的validate()方法执行表单验证:

 

    public ActionErrors validate(ActionMapping mapping,
                                 HttpServletRequest request) {

        ActionErrors errors = new ActionErrors();

        if ((userName == null) || (userName.length() < 1))
            errors.add("username", new ActionMessage("hello.no.username.error"));

        return errors;
    }

(6)HelloForm对象的validate()方法返回一个ActionErrors对象,里面包含一个ActionMessage对象,这个ActionMessage对象封装了错误信息,消息key为“hello.no.username.error”,在Resource Bundle中与之匹配的消息文本为:

  Please enter a <i>UserName</i> to say hello to!

(7)ActionServlet把HelloForm的validate()方法返回的ActionErrors对象保存在request范围内,然后根据<action>元素的input属性,把客户请求转发给hello.jsp。

(8)hello.jsp 的 <html:errors> 标签从 request 范围内读取 ActionErrors 对象, 再从ActionErrors 对象中读取 ActionMessage 对象, 把它包含的错误消息显示在网页上。

 

11、逻辑验证失败的流程

(1)重复上面(1)~(4)

(2)ActionServlet调用HelloForm对象的validate()方法,这次validate()方法返回的ActionErrors对象中不包含任何ActionMessage对象,表示表单验证成功。

(3)ActionServlet查找HelloAction实例是否存在,如果不存在就创建一个实例。然后调用HelloAction 的 execute() 方法。

(4)HelloAction 的 execute()方法先进行逻辑验证,由于没有通过逻辑验证,就创建一个 ActionMessage 对象,这个ActionMessages errors = new ActionMessages();

execute()方法把 ActionMessage 对象保存在 ActionMessages 对象中,再把ActionMessage对象存放在request范围内。最后返回一个 ActionForward 对象, 该对象包含的请求转发路径为<action>元素的input属性指定的hello.jsp。

 

String userName = (String)((HelloForm) form).getUserName();

String badUserName = "Monster";

if (userName.equalsIgnoreCase(badUserName)) 
{
      /*
       * 创建错误信息 —— new ActionMessage("hello.dont.talk.to.monster", badUserName )),
       * 将 ActionMessage 对象保存到 ActionMessages 对象中。
       * 接下来调用在 Action 基类中定义的 saveErrors() 方法,它负责把 ActionMessages 对象保存到 request 范围内。
       * 最后返回 ActionForward 对象,该对象包含的请求转发路径为<action>元素的 input 属性指定的 hello.jsp —— new ActionForward(mapping.getInput());
       */
      errors.add("username", new ActionMessage("hello.dont.talk.to.monster", badUserName ));
      saveErrors(request, errors);
      return (new ActionForward(mapping.getInput()));
}

有时候业务逻辑验证会对数据进行更为复杂的验证。在很多情况下,需要模型组件的介入,才能完成业务逻辑验证。 

(5) ActionServlet 依据 HelloAction 返回的 ActionForward 对象, 再把请求转发给 hello.jsp

(6)hello.jsp 的 <html:errors> 标签从 request 范围内读取 ActionMessages 对象, 再从 ActionMessages 对象中读取 ActionMessage 对象, 把它包含的错误消息显示在网页上。

 

12、验证成功的流程

(1) 重复 (1)~(3)

(2)HelloAction 的 execute() 方法先执行逻辑验证, 这次通过了验证,执行相关的业务逻辑,最后调用 ActionMapping.findForward() 方法,

 

return (mapping.findForward("SayHello"));

(3)ActionMapping.findForward() 方法从 <action> 元素中寻找 name属性为 “SayHello” 的 <forward> 子元素,然后返回与之对应的 ActionForward 对象, 它代表的请求转发路径为 “/hello.jsp” 。

更确切地说,ActionMapping 从本身包含的 HashMap 中查找 name 属性为 “SayHello” 的ActionForward对象。在ActionServlet 初始化时会加载Struts配置文件,把<action>元素的配置信息存放在ActionMapping对象中,<action>元素中可以包含多个<forward>子元素,每个 <forward> 子元素的配置信息存放在一个 ActionForward 对象中, 这些 ActionForward 对象存放在 ActionMapping 对象的 HashMap 中。

(4)HelloAction 的 execute() 方法然后把 ActionForward 对象返回给 ActionServlet, ActionServlet 再把客户请求转发给 hello.jsp

 

 


你可能感兴趣的:(mvc,bean,jsp,servlet,struts)