MVC设计模式
模型、视图、控制器
强制性地将输入、处理、输出分开
MVC的处理过程
首先控制器接受用户的请求,并决定用哪一个模型来进行处理,然后模型根据用户请求进行相应的业务逻辑处理,并返回数据;最后控制器调用
相应的视图来格式化模型返回的数据,并通过视图呈现给客户。
MVC的优点
1. 多视图能共享一个模型
2. 模型是自包含的,与视图和控制器保持相对独立,可以方便地改变应用程序的数据层和业务规则
3. 控制器提高了应用程序的灵活性和可配置性
优点:
(1) 可以为一个模型在运行时同时建立和使用多个视图。变化-传播机制可以确保所有相关的视图及时得到模型数据变化,从而使所有关联的视图
和控制器做到行为同步。
(2) 视图与控制器的可接插性,允许更换视图和控制器对象,而且可以根据需求动态的打开或关闭、甚至在运行期间进行对象替换。
(3) 模型的可移植性。因为模型是独立于视图的,所以可以把一个模型独立地移植到新的平台工作。需要做的只是在新平台上对视图和控制器进行
新的修改。
(4) 潜在的框架结构。可以基于此模型建立应用程序框架,不仅仅是用在设计界面的设计中。
缺点:
(1) 增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更
新操作,降低运行效率。
(2) 视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦
然,这样就妨碍了他们的独立重用。
(3)视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,
也将损害操作性能。
(4) 目前,一般高级的界面工具或构造器不支持MVC模式。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成使用MVC的困难。
MVC的适用范围
不适合小型应用程序
MVC会使大型应用程序在健壮性、代码重用、结构方面上一个新台阶
***************************************************************************************************
Struts就是在JSP Model2的基础上实现MVC框架
M:实现业务逻辑的JavaBean或EJB组件
V:JSP
C:ActionServlet和Action
ActionServlet:继承java.servlet.http.HttpServlet,中央控制器,负责接受HTTP请求,根据配置文件struts-config.xml
的配置信息,把请求转发给适当的Action对象,如果Action对象不存在,ActionServlet会先创建这个Action对象。
Action:负责调用模型的方法,更新模型状态,帮助控制应用程序的流程。
ActionForm Bean用来进行视图和控制器之间表单数据的传递
Struts工作流程
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对象并调用execute()方法。
Struts框架的JSP有以下优点
1 没有任何的JAVA代码
2 使用了STRUST的客户标签
3 没有提供文本内容,由资源文件提供文本信息
数据验证
1 表单验证 ---->由ActionForm 的validate()方法完成
2 业务逻辑验证 ----->由Action 的execute()方法完成
Struts应用的web.xml文件
-------------------------------------------------------------------------------------------
<web-app>
<servlet> ----->声明ActionServlet
<servlet-name>action</servlet-name> ----->定义servlet名称
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class> ------>指定servlet完整类名
<init-param> ------>声明Servlet初始化参数
<param-name>config</param-name> ------>参数名
<param-value>/WEB-INF/struts-config.xml</param-value> ------>Struts配置文件的相对路径
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern> ----->ActionServlet处理所有以".do"扩展名结尾的URL
</servlet-mapping>
<welcome-file-list> ----->配置欢迎文件清单(一个<welcome-file-list> 可以包含多个<welcome-file>
<welcome-file>welcome.jsp</welcome-file> 首先找第一个,按顺序找下去,如果没有的话就返回“HTTP 404 Not Found”)
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page> ----->配置异常处理
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
<error-page> ----->配置异常处理
<exception-type>javax.servlet.ServletException</exception-type>
<location>/error.jsp</location>
</error-page>
<jsp-config>
<taglib> ------>配置Struts标签库
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> ------>指定标签库的相对/绝对YRI地址
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location> ------>指定标签库描述文件在文件资源系统的物理位置
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
</taglib>
</jsp-config>
</web-app>
-------------------------------------------------------------------------------
Struts配置文件
Strust在启动时,会把Struts配置文件中的配置信息读入内存中,并把它们存放在org.apache.struts.config包中相关JavaBean类的实例中。
org.apache.struts.config包中的每一个类都和Struts配置文件中特定的配置元素对应,当Struts框架完成了对配置文件的验证和解析之后,
就把配置文件的信息存放在这些类中。
--------------------------------------------------------------------------------------------------------------------------
struts-config.xml(必须按照DTD指定的先后顺序来配置,如果颠倒,Strurs应用在启动时会生成XML解析错误)
<struts-config> ------>Struts配置文件的根元素
<data-source> ------>配置数据源
<data-source key="A"type="org.apache.commons.dbcp.BasicDataSource"> ----->数据源的实现类
<set-property property="autoCommit" value="true"/>
<set-property property="description" value="MySQL Data Source"/>
<set-property property="driverClass" value="com.mysql.jdbc.Driver"/>
<set-property property="maxCount" value="10"/>
<set-property property="minCount" value="2"/>
<set-property property="user" value="root"/>
<set-property property="password" value="root"/>
<set-property property="url" value="jdbc:mysql://localhost:3306/addressbooksample"/>
</data-source>
</data-source>
配置完数据源后就可以在Action类中访问数据源了,如果有多个数据源就要设key属性
(dataSource = getDataSource(request) or dataSource = getDataSource(request,"A");
myConnection = dataSource.getConnection();)
<form-beans> ------>配置多个ActionForm Bean,可有多个<form-bean>子元素
<form-bean
name="helloForm" ------>name该ActionForm Bean的标识(必须)
type="hellodemo.HelloForm" /> ------>type ActionForm的完整类名(必须)
</form-beans>
<global-exception> ------>配置异常处理,可有多个<exception>子元素
<exception ------>
key="global.error.invalidlogin" ------>
path="/error.jsp"
scope="netstore.framework.exception.InvalidLoginException"
typ""/>
</global-exception>
<global-forwards> ------>声明全局转发关系,可有多个<forward>子元素
<forward name="hello1" path="/hello1.jsp" />
<forward name="hello2" path="/hello2.jsp" />
</global-forwards>
如果JSP想转发到hello1.jsp
<html:link foreard="hello1"> or <logic:forward name="hello1>
如果Action的execute()方法想把请求转发给hello2.jsp
return (mapping.findForward("hello2"))
<action-mappings> ------>描述从特定的请求路径到相应的Action类的映射,可有多个<exception><forward>
<action ------>Action中的forward、include、type属性相互排斥
input="/hello.jsp" ------>当表单验证失败后,将请求转发到该URL
name="helloForm" ------>和该Action关联的ActionForm Bean的名字
path="/HelloWorld" ------>指定访问Action的路径
scope="request" ------〉ActionForm Bean的有效范围,request,session,默认是session
type="hellodemo.HelloAction" ------>Action的完整类名
validate="true"> ------>是否要ActionForm Bean 验证表单,默认是true
<forward name="SayHello" path="/hello.jsp" /> ----->局部转发关系,只有当前Action可以访问
</action>
</action-mappings>
<message-resources parameter="hello.application" />
</struts-config>
局部转发的优先级大于全局转发
<controller
contentType="text/html;charset=gbk" ------>指定响应的内容类型和字符编码
locale="true" ------>是否把locale对象保存到session中,默认是true
processorClass="CustomRequestProcessor"/> ------>负责请求的JAVA类的完整类名,默认是RequestProcessor
<message-resources ----->配置Resource Bundle
key="images"
null="false"
parameter="defaultResources"/> ----->指定Resource Bundle的消息资源文件名
<plug-in className="插件的JAVA类">
<set-property property="插件的JAVA类成员变量"
value="/WEB-INF/xml">
提取资源
<bean:message key=""/> or <bean:message key="" bundle="上面key的值"/>
Struts配置文件可以改名
<init-param>
<param-name>config/qqq</param-name> ---->要以config/开头
<param-value>/WEB-INF/struts-qqq.xml</param-value>
</init-param>
--------------------------------------------------------------------------------------------------------
Struts控制器组件
控制器组件有助于将模型和视图分离
Struts控制器组件包括:
1 ActionServlet 中央控制器
2 RequestProcessor 每个应用模块的请求控制器
3 Action 负责处理一项具体的业务
Struts框架利用ActionServlet和RequestProcessor组件进行集中控制,并才用Action组件来处理单项业务
控制器的主要任务
1 接收用户请求
2 根据用户请求,调用合适的模型组件来执行相应的业务逻辑
3 获取业务逻辑的执行结果
4 根据当前状态以及业务逻辑执行结果,选择合适的视图组件返回给客户
ActionServlet
是Struts的核心控制器组件,所有的请求先由ActionServlet来处理,然后由ActionServlet把请求转发个其他组件
当ActionServlet实例接收到HTTP请求后,在doGet()或doPost()方法中都会调用process()方法来处理请求
Struts框架只允许在一个应用中配置一个ActionServlet类
ActionServlet.provess()
1 先调用org.apache.struts.util.ModuleUtiles类的selectModule()方法
2 获取RequestProcessor类的实例,然后调用RequestProcessor类的provess()方法
扩展ActionServlet类
如果覆盖init()方法,就应该确保首先调用super.init();它保证ActionServlet的默认初始化操作被执行
如果Struts应用使用自定义的ActionServlet类,要在web.xml中对其进行配置:
<url-pattern>/action/*</url-pattren>
表明ActionServlet负责处理所有的以"/action"为前缀的URL
RequestProcessor
多应用模块的Struts应用,没个子应用都有各自的RequestProcessor实例
Action
用户请求和业务逻辑之间的桥梁
ActionForward类
Action的execute()返回一个ActionDorward对象
new ActionForward("实例的逻辑名","转发路径",boolean[true代表重定向,false代表请求转发])
通过web.xml来配置ActionForward;
在Action的execute()中只需调用ActionMapping实例的findForward("实例逻辑名")方法就能获得ActionForward实例
常见的内置的Struts Action 类
forwardAction ----->专门用于转发请求,不执行任何其他业务操作
<action
input="/hello.jsp"
name="helloForm"
path="/HelloWorld"
scope="request"
parameter="/hi.jsp"
type="org.apache.struts.action.ForwardAction"
validate="true">
</action>
IncludeAction ----->提供包含其他Web组件的功能
<action
input="/hello.jsp"
name="helloForm"
path="/HelloWorld"
scope="request"
parameter="/hi.jsp" ------>要包含的组件
type="org.apache.struts.action.IncludeAction"
validate="true">
<forward name="SayHello" path="/hello.jsp" />
</action>
DispatchAction ------>在同一个Action中完成一组相关的业务操作
不必覆盖execute()方法,而是创建一些实现实际业务操作的方法,要和execute()方法具有同样的方法签名,即返回参数和类型要相同,要抛异常
如何实现DispatchAction
1、继承DispatchAction
2、定义自己的处理方法,不要覆盖execute()方法
3、struts-config.xml中action元素增加parameter属性
<action name="dynaStuForm"
parameter="method"
path="/studentMgtAction"
scope="request"
type="studentapp.StudentMgtAction"
validate="false">
4、用户请求URL应有如下格式
---------------------------------------------------------------------------------------------
http://localhost:8080/abcWeb/studentMgtAction.do?method=<ur_method_name>
LookupDispatchAction ----->DispatchAction 的子类,用于一个表单中有多个同名的提交按纽
<action
input="/hello.jsp"
name="helloForm"
path="/HelloWorld"
scope="request"
parameter="action" ------>一定要和<html:submit>标签的property保持一致
type="HelloAction"
validate="true">
<forward name="SayHello" path="/hello.jsp" />
</action>
-------------------------------------------------------------------------------------------------------
如何实现LookupDispatchAction
1、继承LookupDispatchAction
2、定义自己的处理方法,覆盖getKeyMethodMap()方法,不要覆盖execute()方法
getKeyMethodMap()完成资源文件中Key与Action中方法映射
------------------------------------------------------------------------------
protected Map getKeyMethodMap() {
//Map中的Key与资源文件映射,Value与方法名映射
Map map = new HashMap();
map.put("submit.add", "add");
map.put("submit.modify", "modify");
return map;
}
public ActionForward modify()...
public ActionForward add()...
-------------------------------------------------------------------------------------
3、struts-config.xml中为Action增加parameter属性
<action name="dynaUserForm" parameter="abc" path="/userAction" ...>
4、提交页面按钮有如下格式
html:submit的property属性值与Action中的parameter属性值映射
<html:submit property="abc">
<bean:message key="submit.add"/>
</html:submit>
SwitchAction ------>用于子模块之间的切换
Token(同步令牌)解决表单的重复提交
isTokenValid(request) ------>判断当前用户会话中的令牌值是否和请求参数的令牌值匹配,如果匹配就返回true,否则就返回false
返回false 1 不存在HttpSession对象
2 在sessuin范围内没有保存令牌值
3 在请求参数中没有令牌值
4 存储在当前的用户session范围内的令牌值和请求参数中的令牌值不匹配
saveToken(request) ------>创建一个新的令牌,并把阿嚏保存到当前session范围内,如果HttpSession对象不存在,就先创建HttpSession
resetToken()request ------>从当前session范围内删除令牌属性
流程:
1 在用户请求JSP页面时,首先把请求转发给Action,它调用savaToken()方法,创建一个新的令牌,并保存到session范围中,在转发到JSP页面
2 JSP页面中<html:form>标签的处理类判断在session范围内是否有Token,如果存在的话,就在表单中生成一个包含Token信息的隐含字段,这段
逻辑由org.apache.struts.taglib.html.FormTag类的renderToken()方法完成
3 在用户提交表单后,由Action处理请求,在Action中,首先调用isTokenValid()方法,判断当前会话中的令牌值和请求参数中的令牌值是否匹配。
如果不匹配,就生成错误信息,并调用saveToken()方法,创建一个新的令牌,然后返回。如果匹配,就调用resetToken()方法,从当前会话中删除
Token,然后执行操作。
实用类
org.apache.struts.util.RequestUtils 为Struts控制框架提供一些处理请求的通用方法
org.apache.struts.taglib.TagUtil 为JSP标签处理类提供许多实用方法
org.apache.struts.taglib.ModuleUtils 提供了处理子模块的实用方法
org.apache.struts.Globals 提供一组公共类型的静态常量,被用做在特定范围内存放JavaBean 的属性key
------------------------------------------------------------------------------------------------------------
Action编程技巧:
1、尽量避免使用实例变量或静态变量
2、使用自己的BaseAction完成Action的公共操作,其余Action可从BaseAction派生
3、Action不要代替Model工作
4、在设计多层架构时,使用Business Delegate可降低层与层之间的耦合度
---------------------------------------------------------------------------------------------------------
Struts模型组件
模型代表应用的业务数据和逻辑
应该把所有的模型组件放到系统的同一个位置中,这有利于维护数据的完整性,减少冗余,提高可重用性
模型应该和视图以及控制器保持独立。
---------依赖关系加强----------->
视图层 控制层 模型层 持久化层 网络层
<--------依赖关系减弱-----------
业务对象(BO)
业务对象包括状态和行为
业务对象的特征:
1 包含状态和行为
2 代表业务领域的人、地点、事物或概念
3 可以重用
业务对象的类型
1 实体业务对象
2 过程业务对象
3 事物业务对象
ORM框架是一种持久化框架
DAO设计模式是实现持久化框架的一种设计模式
OJB是DAO设计模式的一种实现
Struts视图组件
同一个模型可以有多种视图
Struts利用JavaBean来创建数据传输对象(DTO),DTO用于不同层之间传递数据
采用DTO传输数据的好处
1 减少传输数据的冗余,提高传输效率
2 有助于实现各个层之间的独立,使每个层的分工明确
模型层应该和WEB应用层保持独立,由于ActionForm类使用了Servlet API,因此不提倡直接把ActionForm Bean传给模型层,而应该在控制层把
ActionForm Bean的数据重新组装到自定义的DTO中,再把它传给模型层
ActionForm实例在request范围内是当返回响应结果给客户端时销毁,如果再请求就创建一个新的
在session范围内只要是同一个HTTP请求就始终是这个ActionForm实例
动态表单
自定义的ActionForm要继承Struts的ActionForm类,同时,可以有选择性地覆盖两个方法:reset()、validate()
可以减少FormBean class的数量,完成对html form属性的动态映射,更便于应用维护。
-----------------------------------------------------------------------------
实现动态表单功能:通过struts-config.xml配置
<form-bean name="dynaStuForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="id" type="java.lang.String" />
<form-property name="name" type="java.lang.String" />
<form-property name="gender" type="java.lang.String" />
<form-property name="age" type="java.lang.Integer" />
<form-property name="classNo" type="java.lang.String" />
</form-bean>
其中,type为动态表单类型,由struts提供
---------------------------------------------------------------------------------------
扩展Struts框架
Struts框架的一个优势在于它允许开发人员根据实际需要来扩展框架,定制客户化的功能
一般性扩展点:Struts插件(PlugIn)、扩展Struts配置类
控制器的扩展点:扩展ActionServlet类、RequestProcessor类和Action类
视图的扩展点:扩展Struts客户化标签
模型的扩展点:扩展SessionContainer类和ApplicationConrainer类
一般性扩展点
Struts插件实际上就是一个JAVA类,它在Struts应用启动时被初始化,在应用关闭时被销毁
任何作为插件的JAVA类都应该实现org.apache.struts.PlugIn接口
<plug-in className="插件的JAVA类">
<set-property property="插件的JAVA类成员变量"
value="/WEB-INF/xml">
控制器的扩展点
RequestProcessor通过
<controller
processorClass="扩展RequestProcessor类的的路径"
>
完成
模型的扩展点
SessionContainer类,和HttpSession实现的功能一样,不过不用设置KEY值,要在扩展Action中定义相应的方法
SessionContainer.setUserView(value);
SessionContainer.getUserView();
Struts应用国际化
Struts的异常处理
Struts HTML标签库