现在的位置: 首页 > 综合 > 正文

Struts控制器组件

2013年09月08日 ⁄ 综合 ⁄ 共 8995字 ⁄ 字号 评论关闭

Struts控制器组件

 

Struts控制器组件负责接受用户请求、更新模型,以及选择合适的视图组件返回给用户。控制器组件有助于将模型层和视图层分离,有了这种分离,就可以在同一个模型的基础上得心应手地开发多种类型的视图。Struts控制器组建主要包括:

 

·ActionServlet组件:充当Struts框架的中央控制器。

·RequestProcessor组件:充当每个子应用模块的请求处理器。

·Action组件:负责处理一项具体的业务。

 

Struts框架采用控制器组件来预处理所有的客户请求,这种集中控制方式可以满足MVC设计模式的两大需求:

 

·首先,控制器在用户输入数据和模型之间充当媒介 / 翻译者的角色,提供一些通用功能,如安全、登入和其他针对具体用户请求的重要服务,当系统的这些通用功能出现需求变更时,部需要修改整个应用,只需要修改局部的控制器组件即可。

·其次,由于所有的请求都经过控制器过滤,因此可以降低视图组件之间,以及视图组件和模型组件之间的相互依赖关系,提高每个组件的相对独立性。由控制器组件来决定把合适的视图组件返回给用用户,这可以减少视图组件之间直接的,错综复杂的连接关系,使应用更加灵活,便于维护。

 

Struts框架采用ActionServletRequestProcessor组件进行集中控制,并采用Action组件来处理单项业务。

 

 

控制器组件的控制机制

 

Struts的控制器组件主要完成以下任务:

·接受用户请求

·根据用户请求,调用合适的模型组件来执行相应的业务逻辑。

·获取业务逻辑执行结果。

·根据当前状态以及业务逻辑执行结果,选择合适的视图组件返回给用户。

 

1 Action

 

 
org.apache.struts.action.ActionServlet
类是Struts框架的核心控制器组件,所有的用户请求都先有ActionServlet来处理,然后再由ActionServlet把请求转发给其他组件。Struts框架只允许在一个应用中配置一个ActionServlet类,在应用的生命周期中,仅创建ActionServlet类的一个实例,这个ActionServlet实例可以同时响应多个用户请求。

 

(a)   
Struts框架初始化过程

(1)   
调用initInternal()方法,初始化Struts框架内在的消息资源,如与系统日志相关的同志、警告和错误消息。

(2)   
调用initOther()方法,从web.xml文件中加载ActionServlet的初始化参数,如config参数。

(3)   
调用initServlet()方法,从web.xml文件中加载ActionServletURL映射信息。此外还会注册web.xmlStruts配置文件所使用的DTD文件,这些DTD文件用来验证web.xmlStruts配置文件的语法。

(4)   
调用initModuleConfig()方法,加载并解析子应用模块的Struts配置文件;创建ModuleConfig对象,把它存储在ServletContext中。

(5)   
调用initModuleMessageResources()方法,加载并初始化默认子应用模块的消息资源:创建MessageResources对象,把它存储在ServletContext中。

(6)   
调用initModuleDataSources()方法,加载并初始化默认子应用模块的数据源。如果在Struts配置文件中没有定义<data-sources>元素,就忽略这一流程。

(7)   
调用InitModulePlugins()方法,加载并初始化默认子应用模块的所有插件。

(8)   
当默认子应用模块被成功地初始化后,如果还包括其他子应用模块,将重复流程(4)(7),分别对其他子应用模块进行初始化。

 

(b)   
ActionServletprocess()方法

ActionServlet实例接受到HTTP请求之后,在doGet()doPost()方法都会调用process()方法来处理请求。一下是ActionServletprocess()方法的源代码:

 

protected void
process (HttpServletRequest request, HttpServletResponse response)

throw
IOException, ServletException {

 

ModuleUtils.getInstance().selectModule(request,
getServletContext());

getRequestProcessor(getModuleConfig(request)).process(request,
response);

}

 

process()方法中,首先调用org.apache.struts.util.ModuleUtils类的selectModule()方法,这个方法选择负责处理当前请求的子应用模块,然后把与子应用模块相关的ModuleConfigMessageResources对象存储倒request范围中,这使得框架的其余组件可以方便地从request范围中读取这些对象,从而获取应用配置信息和消息资源。

process()方法的第二步操作为获得RequestProcessor类的实例,然后调用RequestProcessor类的process()方法,来完成十几的预处理请求操作。

 

 

(c)   
扩展ActionServlet

Sturts 1.1 之前的版本中,ActionServlet类本身包含了很多处理请求的代码。从Struts 1.1 开始,多数功能被移到 org.apache.struts.action.RequestProcessor 类中,以便减轻ActionServlet类的控制负担。

 

尽管新版本的Struts框架允许在应用中创建矿展ActionServlet类的子类,但是这在多数情况下没有必要,因为控制器的多数控制功能位于RequestProcessor类中。

 

如果实际应用确实需要创建自己的ActionServlet类,则可以创建一个ActionServlet的子类,然后在web.xml文件中配置这个客户化ActionServlet类。

 

如果覆盖了init()方法,应该确保首先调用super.init(),它保证ActionServlet的默认初始化操作被执行。除了覆盖init()方法外,事实上,还可以根据十几需要覆盖ActionServlet的任何其他方法。

 

 

2 RequestProcessor

 

  对于多应用模块的Struts应用,每个子应用模块都有各自的RequestProcessor实例。在ActionServletprocess()方法中,一旦选择了正确的子应用模块,就会调用子应用模块的RequestProcessor实例的process()方法来处理请求。在ActionServlet调用这个方法时,会把当前的requestresponse对象传给它。

Struts框架只允许应用中存在一个ActionServlet类,但是可以存在多个客户化的RequestProcessor类,每个子应用模块都可以拥有单独的RequestProcessor类。如果想修改RequestProcessor类的一些默认功能,可以覆盖RequestProcessor基类中的相关方法。

 

(a)   
RequestProcessor类的process()方法

RequestProcessor类的process()方法负责实际的预处理请求操作。

RequestProcessor类的process()方法一次执行一下流程:

(1) 调用processMultipart()方法。如果HTTP请求方式为POST,并且请求的contentType属性以“multipart/form-data”开头,标准的HttpServletRequest对象将被重新包装,以方便处理“multipart”类型的HTTP请求。如果请求方式为GET,或者contentType属性不是“multipart”,就直接返回原始的HttpServletRequest对象。

(2) 调用processPath()方法,获得请求URI的路径,这一信息可用于选择合适的Struts Action组件。

(3) 调用processLocale()方法,当ControllerConfig对象的locale属性为true,将读取用户请求中包含的Locale信息,然后把Locale实例保存在session范围内。

(4) 调用processContent()方法,读取ControllerConfig对象的contentType属性,然后调用response.setContentType(contentType)方法,设置响应结果的文档类型和字符编码。

(5) 调用processNoCache()方法,读取ControllerConfig对象的nocache属性,如果nocache属性为true,在响应结果中将加入特定的头参数:PragmaCache-ControlExpires,防止页面被存储在客户浏览器的缓存中。

(6) 调用processPreprocess()方法。该方法不执行任何操作,直接返回true。子类可以覆盖这个方法,执行客户化的预处理请求操作。

(7) 调用processMapping()方法,寻找和用户请求的URI匹配的ActionMapping。如果不存在这样的ActionMapping,则向用户返回恰当的错误消息。

(8) 调用processRoles()方法,先判断是否为Action配置了安全角色,如果配置了安全角色,就调用isUserRole()方法判断当前用户是否具备必需的角色;如果不具备,就结束请求处理流程,向用户返回恰当的错误消息。

(9) 调用processActionForm()方法,先判断是否为ActionMapping配置了ActionForm,如果配置了ActionForm,就先从ActionForm的存在范围内寻找该ActionForm实例;如果不存在,就创建一个实例。接下来把它保存在合适的范围中,保存时使用的属性keyActionMappingname属性。

(10) 调用processActionForm()方法。如果为ActionMapping配置了ActionForm,就先调用ActionFormreset()方法,再把请求中的表单数据组装到ActionForm中。

(11) 调用processValidate()方法,如果为ActionMapping配置了ActionForm,并且ActionMappingvalidate属性为true,就调用ActionFormvalidate()方法。如果validate()方法返回的ActionErrors对象中包含ActionMessage对象,说明表单验证失败,就把ActionErrors对象存储在request范围内,再把请求转发到ActionMappinginput属性指定的Web组件。如果ActionFormvalidate()方法执行表单验证成功,就继续执行下一步请求处理流程。

(12) 调用processForward()方法,判断是否在ActionMapping中配置了forward属性。如果配置了这个属性,就调用RequestDispatcherforward()方法,请求处理流程结束,否则继续下一步。

(13) 调用processInclude()方法,判断是否在ActionMapping中配置了include属性。如果配置了这个属性,就调用RequestDispatcherinclude()方法,请求处理流程结束,否则继续下一步。

(14) 调用processActionCreate()方法,先判断是否在Action缓存中存在这个Action实例,如果不存在,就创建一个Action实例,把它保存在Action缓存中。

(15) 调用processActionPerform()方法,该方法再调用Action实例的execute()方法。execute()方法位于try/catch代码中,以便捕获异常。

(16) 调用processActionForward()方法,把Actionexecute()方法返回的ActionForward对象作为参数传给它。processActionForward()根据ActionForward对象包含的请求转发信息来执行请求转发或重定向。

 

(b)   
扩展RequestProcessor

开发人员可以很方便地创建客户化的RequestProcessor类。

Struts配置文件中,<controller>元素的processorClass属性RequestProcessor类。

 

 

3 Action

Action类是用户请求和业务逻辑之间的桥梁。每个Action充当客户的一项业务代理。在RequestProcessor类预处理请求时,在创建了Action的实例后,就调用自身的processActionPerform()方法,该方法再调用Action类的execute()方法。

Actionexecute()方法调用模型的业务方法,完成用户请求的业务逻辑,然后根据执行结果把请求转发给其他合适的Web组件。

 

(a)   
Action类缓存

为了确保线程安全(thread-safe),在一个应用的生命周期中,Struts框架只会为每个Action类创建一个Action实例。所有的客户请求共享一个Action实例,并且所有请求线程可以同时执行它的execute()方法。

RequestProcessor类包含一个HashMap,作为存放所有Action实例的缓存,每个Action实例在缓存中存放的属性keyAction类名。在RequestProcessor类的processActionCreate()方法中,首先检查在HashMap中是否存在Action实例,如果存在,就返回这个实例;否则,就创建一个新的Action实例。创建Action实例的代码位于同步代码块中,以保证只有一个线程创建Action实例。一旦线程创建了Action实例并把它存放到HashMap中,以后所有的线程就会直接使用这个缓存中的实例。

 

(b)   
ActionForward

Action类的execute()方法返回一个ActionForward对象。ActionForward对象代表了Web资源的逻辑抽象,这里的Web资源可以是JSP页、Java ServletAction。从execute()方法中返回ActionForward对象有两种方法:

·
execute()方法中动态创建一个ActionForward实例

·
Struts配置文件中配置<forward>元素。

       配置了<forward>元素后,在Struts框架初始化时就会创建存放<forward>元素;

   execute()方法中只需调用ActionMapping实例的findForward()方法,来获取特定的ActionForward实例。findForward()方法先调用findForwardConfig()方法,在Action级别(即<action>元素内的<forward>子元素)寻找匹配的ActionForward对象。如果没有,再在全局级别(即<global-forwards>元素内的<forward>子元素)中寻找匹配的ActionForward对象。如果找到,就返回该ActionForward对象。如果findForward()方法没有找到匹配的ActionForward对象,它不会抛出异常,而是返回null。在浏览器端,用户将收到一个空白页。

采用第二种方式,无需在程序中硬编码来指定转发资源的无力路径,而是在配置文件中配置转发资源,程序中秩序引用转发资源的逻辑名即可,这提高了应用的灵活性和可维护性。

 

(c)   
创建支持多线程的Action

Struts应用的生命周期中,只会为每个Action类创建一个实例,所有的客户请求共享这个实例。因此,必需保证在多线程环境中,Action也能正常工作。保证线程安全的重要元素是在Action类中仅仅使用局部变量,谨慎地使用实例变量。

如果在Actionexecute()方法中定义了局部变量,对于每个调用execute()方法的线程,Java虚拟机会在每个线程的堆栈中创建局部变量,因此每个线程拥有独立的局部变量,不会被其他线程共享。当线程执行完execute()方法时,它的局部变量就会被销毁。

如果在Action类中定义了实例变量,那么在Action实例的整个生命周期中,这个实例变量被所有的请求线程共享。因此不能在Action类中定义代表特定客户状态的实例变量。

Action类中定义的实例变量代表了可以被所有请求线程访问的共享资源。为了避免共享资源的竞争,在必要的情况下,需要采用Java同步机制对访问共享资源的代码块进行同步。

 

(d)
Action
类的安全

在某些情况下,如果Action类执行的功能非常重要,则只允许具有特定权限的用户才能访问该Action。为了防止未授权的用户来访问Action,可以在配置Action时指定安全角色。

<action>元素的roles属性指定访问这个Action的用户必须具备的安全角色,多个角色之间以逗号隔开。

RequestProcessor类在预处理请求时会调用自身的processRoles()方法,该方法先检查在配置文件中是否未Action配置了安全角色,如果配置了安全角色,就调用HttpServletRequestisUserInRole()方法,来判断用户是否具备了必要的安全角色。如果不具备,就直接向客户端返回错误。

 

 

 

使用内置的Struts
Action

 

Struts提供了一些现成的Action类,在Struts应用中直接使用这些Action类可以大大节省开发时间。

 

1 org.apache.struts.actions.ForwardAction

  JSP网页中,尽管可以直接通过<jsp:forward>标签把请求转发给其他Web组件,但是Struts框架提倡先把请求转发给控制器,再由控制器来负责请求转发。

 

  有控制器来负责请求转发有以下一些优点:

·控制器具有预处理请求功能,它能够选择正确的子应用模块来处理请求,并且把子应用模块的ModuleConfigMessageResources对象存放在request范围内。这样,请求转发的目标Web组件就可以正常地访问ModuleConfigMessageResources对象。

·如果JSP页面中包含HTML表单,那么控制器能够创建和这个表单对应的ActionForm对象,把用户输入表单数据组装到ActionForm中。如果<action>元素的validate属性为true,那么还会调用ActionForm的表单验证方法。控制器把ActionForm对象存放在requestsession范围内,这样请求转发的目标Web组件也可以访问ActionForm

·JSP网页之间直接相互转发违背了MVC的分层原则,按照MVC设计思想,控制器负责处理所有请求,然后选择适当的视图组件返回给用户,如果直接让JSP相互调用,控制器就失去了流程控制作用。

 

  对于用户自定义的Action类,既可以负责请求转发,还可以充当客户端的业务代理,如果仅仅需要Action类提供请求转发功能,则可以使用org.apache.struts.actions.ForwardAction类。ForwardAction类专门用于转发请求,不执行任何其他业务操作。

  ActionServlet把请求转发给ForwardActionForwardAction再把请求转发给<action>元素中parameter属性指定的Web组件。总之,在Web组件之间通过ForwardAction类来进行请求转发,可以充分利用Struts控制器的预处理请求功能。此外,也可以通过<action>元素的forward属性来实现请求转发。

 

2 org.apache.struts.actions.IncludeAction

  JSP网页中,尽管可以直接通过<include>指令包含另一个Web组件,但是Struts框架提倡先把请求转发给控制器,再由控制器来负责包含其他Web组件。IncludeAction类提供了包含其他Web组件的功能。与ForwardAction一样,Web组件通过IncludeAction类来包含另一个Web组件,可以充分利用Struts控制器的预处理功能。

<action>paramter属性指定需要包含的Web组件。此外,也可以通过<action>元素的include属性来包含Web组件。

 

3 org.apache.struts.actions.DispatchAction

  通常,在一个Action类中,只能完成一种业务操作,如果希望在同一个Action类中完成一组相关的业务操作,可以使用DispatchAction类。

  创建一个扩展DispatchAction类的子类,不必覆盖execute()方法,而是创建一些实现实际业务操作的方法,这些业务方法都应该和execute()方法具有同样的方法签名,即他们的参数和返回类型都应该相同,此外也应该声明抛出Exception

  在配置DispatchAction类时,需要把parameter属性设置为“method”。设置之后,当用户请求访问DispatchAction

抱歉!评论已关闭.