Struts最佳实践

来源:互联网 发布:宁夏干部培训网络学习 编辑:程序博客网 时间:2024/05/05 05:05

Struts的最佳实践

Build the best performing large applications

译者:下面的实践在现在看来有些可能并不是最佳,译出来主要是供大家参考。

带动态域的屏幕(Screen with dynamic fields

Form bean必须为在JSP中的每个域提供gettersetter方法,问题是如何为动态域写这些方法。

Struts best practice:

       可能的解决方案:

ü         JSP页面的域是指定的方式,例如field1field2field3等等,在form bean中提供他们的gettersetter方法,能够出现在页面的域的数量不能多于form bean中的变量的数量

ü         为在JSP页面中的所有动态域利用在form bean中的可用的被索引的gettersetter方法。

第二种方法中,在JSP中增加域的数量不需要在任何组件中进行修改;因此,这是推荐的最佳实现。实现细节如下:

1.  假设一个带在form bean中所有的动态域资源ID的字符串数组,JSP页面可以这样写:

<logic:iterate name= "FormName" property="propertyName" indexId="abc" >

  <html:nested property='dynaProperty(<bean:write name="abc")'/>

</logic:iterate>

2.  form bean中声明两个方法,如下所示。这些方法会作为JSP页面中所有的动态域的gettersetter方法。在上面的JSP页面中dynaProperty在()中出现的被作为key,从form beangetDynaproperty()setDynaProperty()方法调用。这些值应该被存在HashMap对应的key中,以后在Action类中可以通过对应的值从HashMap取出来。

 

public class testVarForm extends ActionForm

{

  private HashMap hMap = new HashMap();

 

  public testVarForm() {  }

  public void setDynaProperty(String key, Object value)  {

    this.hMap.put(key, value);

  }

  public Object getDynaProperty(String key)   {

    return this.hMap.get(key);

  }

  public HashMap getHashMap()   {

    return this.hMap;

  }

  public void setHashMap(HashMap newHMap)

  {

    this.hMap =newHMap;

  }

}

保护JSP页面(Safeguarding JSP pages

防止查看源码,直接访问JSP

Struts最佳实践

 

ü         不要让用户直接访问任何JSP页面,起始页可以是HTML文档。在web.xml中增加下面的行来阻止用户直接访问JSP页面。

<web-app>
   ...
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>no_access</web-resource-name>
      <url-pattern>*.jsp</url-pattern>
    </web-resource-collection>
    <auth-constraint/>
  </security-constraint>
  ...
</web-app>

ü         最常见的选择是把JSP页面藏在WEB-INF文件夹的后面。这只能算一种折衷方案。例如,你不能把javascript/css文件隐藏在WEB-INF后面,并且如果你使用Struts组件,可能还会遇到一些上下文相关的问题。

 

第二种方法允许一些JSP页面(那些不能被放在WEB-INF后面的)直接可见。不要求描述文件,因而,最佳实践是把页面放在WEB-INF后面。

 

错误分类(Error categorization

N层应用中错误处理变得复杂。在基于浏览器的应用中,错误处理可以在客户端使用JavaScript以及在使用专用java方法的WEB层或EJB层。Struts提供了ActionMessages/ActionErrors类来维护需要被报告的错误信息堆栈,能使用<html:error>之类的tag显示给用户的错误信息。问题是用不同的风格,例如error,warning或者information,报告不同类型的错误。

1.  在合适的类别下注册错误

2.  识别这些信息,并且始终如一的显示他们。

 

Struts最佳实践

       StrutsActionError类方便的解决了不同类型堆栈信息的第一个问题。要显示不同类型的错误信息,在一个接口内定义这些类型,FATAL,ERROR,WARNING,INFO。然后在Actionform bean中,你可以这样使用:

errors.add("fatal", new ActionError("...."));  
errors.add("error", new ActionError("...."));  
errors.add("warning", new ActionError("...."));  
errors.add("information", new ActionError("....")); 
saveErrors(request,errors);

      

要根据这些分类来显示,使用下面的代码:

<logic:messagePresent property="error"> 
<html:messages property="error" id="errMsg" >
    <bean:write name="errMsg"/>
</html:messages>
</logic:messagePresent >
<logic:messagePresent property="error"> 
<html:messages property="error" id="errMsg" >
    showError('<bean:write name="errMsg"/>'); // JavaScript Function
</html:messages>
</logic:messagePresent >

      

服务请求确认(Validation of service requester

在基于WEB的应用中,认证可以在任何类中被用到,取决于是否使用的是基于SSOJAAS机制。这里的挑战是检查服务请求者的真实性和用户Session的有效性。

 

       Struts最佳实践

       通常的做法是在HttpSession中保存用户认证后的证书。以后调用在session上下文中的证书来检查。问题是在哪放置这些检查。要考虑性能,应用易于管理和以后的变化。

ü         在做任何操作前对Session context进行验证(在Struts-example.warCheckLoginTag.java中就是这样做)。

ü         Action类中进行对Session context的认证。

ü         写执行认证的servlet请求过滤。

ü         扩展RequestProcessor

 

头两个选择要求每个JSP页面或Action类执行对Session context的认证。接口的变化会导致所有这些JSP页面和类的变动。第三个选项是有效的,但是过度的处理了这个问题。

 

最佳实践是扩展RequestProcessor,在诸如processActionPerform()processRoles()的方法中执行认证。

 

应用安全(Application security

WEB应用中的常见的安全需求有页面级、函数级、数据行级和域级。如果设计的不合适,那么引起的不仅是性能问题,还会带来维护的梦魇。

对于上面提到的所有的安全类型,首选的方法是放置安全检查代码在一个类中,而不是在每个组件(例如JSPAction类)中到处乱放。

Struts为页面级和函数级的安全检查提供了方法processRoles(),然而为列级和域级安全类型什么也没提供,写好他们是大多数Struts使用者的最大挑战。

 

       Struts最佳实践

ü         对于页面级和函数级的安全,继承RequestProcessor覆写processRoles()方法来执行针对保存着角色和页面ID/函数ID映射的HashMap的检查。

ü         行级安全最好在应用的OR-Mapping中实现。

ü         对于域级的安全,扩展taglib来检查针对fieldID的安全。

 

预读取(Prepopulation

在下拉列表(HTML Select tag)或其他域的数据预读取是一个需求难点。这里的一些值是从数据库中获得,一些是从应用的上下文中获得,而剩下的一些可能是从调用页面传递的。这些值必须提前准备出来。

      

       Struts最佳实践

Form bean的预先读取可以通过下面的方法来实现

ü         按需延迟加载技术(lazy-loading)适用于所有的这些需要预读取的数据在应用的上下文里。页面的调用机制经常通过相应Action类的指定方法路由,有关的预加载的代码可以放在这个方法中。

ü         不同的Action类,一个处理请求,另外一个提交JSP页面。整个模块或应用能够共享请求使用的Action类。

 

对于小应用,第二个方法会工作的很好,但是对于大型应用,管理数量超级多的Action类真是费力不讨好的事情。因而,应该采用第一种方法。

 

(译者)对于第一种方法的补充,第一种方法的思路是创建一个工具类从数据库中为下拉列表框读取值,在应用范围内保存数据。作者说延迟加载是因为在集群环境中,当你在应用范围内保存数据时,不会传播到其他节点。

 

堆栈维护(Stack maintenance

开发人员经常需要以交叉的方式访问不同的页面,应用必须要记住访问过的页。

 

Struts最佳实践

补偿是在Stack(java.util.Stack)中保存所有转向过的JSP页面的路径。

ü         继承RequestProcessor类,覆写processActionPerform()方法。在覆写的方法中,调用super.processActionPerform()方法后,ActionForward的路径应该被保存在Stack中。

ü         在应用的父Action类中,提供方法在堆栈中向前或向后访问。

 

另一种选择是利用已经存在的开源项目,例如sf.netOpen Tranquera

上下文相关问题(Context-related problems

如果ActionForward的路径和控制被转向的路径不同,会导致上下文问题。在基于Struts的应用中这种上下文相关的问题非常常见。

 

Struts最佳实践

JSP文件的名字作为路径给ActionForward,并且从一个预配置的地方取得前缀,例如,JSP页面的访问路径。通过下面的方法来做到这点:

 

struts-config.xml有一个controller标签,在这个标签中增加一个属性,叫做forwardPattern。这个属性值能够被前置到所有的ActionForward路径。这个属性的默认值是$M$P,表示模块的前缀会被增加到ActionForward的前缀。我们可以把这个值改为任何内容,例如:WEB-INF/pages/$M$P,这样让所有的ActionForward路径搜索路径WEB-INF/pages/<module-prefix>/

 

要使用上面的解决办法,将ActionForwardcontextRelative属性置为false。如果这个属性是trueActionForward路径的情况会发生,因为他们没有被修改。

还要注意,forwardPattern要工作,所有的ActionForward路径必须由 / 开始。

 

Form-bean范围(Form-bean scope

管理良好的Session context能够很大的提升应用性能。了解form bean标示为request,sessionapplication的含义非常重要。然而,下面的混乱经常会出现:

ü         如果form bean的范围被被标记为request,在一个页面可用的信息如何也能为其他的可用?

ü         如果form beansession,服务器如何知道client不在对form bean感兴趣,因而销毁它?

 

Struts最佳实践

状态可以在HttpSession中维护(在Web层),可以在有状态的Session Bean中(EJB层),或者是在数据库中。首屈一指的规则是保持状态封闭。因而,最佳实践是在HttpSession中保持状态,例如:将Form beanscope设置为session

 

重要的问题是,什么时候,如何销毁保存在HttpSession中的form beanSession中的bean应该销毁,一种方法是,在一个基于LRAleast-recently-accessed,to-be-removed逻辑的RequestProcessor的扩展中选择销毁。另一种方法是,用户一调用新的业务操作就销毁所有的form bean,可能从应用的菜单中做处理。

 

数据传输对象DTO实现(Data transfer object implementation

通常DTO被用于在WEB层和EJB层之间传递数据,而传递view-helper类(form bean)到EJB层可不是一个好主意,主要是因为form bean的所有域都是字符串类型。使用单独的类作为DTO。问题是如何从form beanDTO传输数据。

 

Struts最佳实践

两个方法可用。

ü         创建一个DTO,从form bean拷贝数据到DTO。在数据转换前,数据类型的转换必须先被处理好。同样,返回的时候,你也必须重复同样的工作。

ü         使用Commons项目的BeanUtils类,使用反射API来实现这个功能。

 

开发人员经常使用第一种方法,虽然也正确,就是每次都需要重复,但是导致了臃肿和丑陋的代码。当拷贝数据时合适的数据类型转换代码必须在DTOsetter方法中手工编写,转换专用的业务类型。

 

你可以使用BeanUtils中的copyProperties()方法来避免这种编码。这个方法从一个bean拷贝数据到另一个bean。好处很多。

 

异常(Exceptions

作为通用的原则,错误应该被在他们发生的地方正确的捕捉显示有意义的错误信息。要实现这个原则,程序员必须写他们自己的定制异常类。封装实际的异常到他们的定制异常类,把他们抛出到要处理的地方。在基于Struts的应用中,异常在Web/EJB层抛出,在Action类中或者form bean中处理。通常的实践是在Action类或form bean中写try-catch代码捕捉异常,使用从应用属性文件中派生的用户友好的错误信息创建一个ActionError对象,使用ActionMapping.findForward()方法。

 

你必须在Action类中编写所有的这些异常处理代码。如果错误处理策略改变了,每个Action或者form bean都要改变。

 

Struts最佳实践

Struts用适当的方法解决异常处理问题,使用声明型的异常处理。与编码型的异常处理相反,异常处理使用RequestProcessor类的processException()方法和一个Struts配置文件。要使用声明型的异常处理,你必须按照下面的步骤:

1.  创建定制应用异常类。可以设计他们,以便处理超过一种异常。

2.  WEB/EJB层,捕获应用错误并且封装错误到定制异常中,把异常抛出。

3.  struts-config.xml文件中,增加如下内容:

 

 

<struts-config>
 
<action-mappings>
    <action
      path="yourAction"
      type="your.package.yourAction" 
      input="input.jsp" >
      <exception 
        key="your.error.property.key"
        path="yourException.jsp"
        type="your.application.custom.exception"
   </action>
</action-mappings>

 

Action链(Action chaining

如果要得到JSP页面和Action的清晰的关系,大型应用应该在JSP页面和Action类间有一对一的关系。但这样可以预料到的问题是会导致在Action类中的重复代码。要避免逻辑重复,需要一些从一个Action类调用另外的Action类的方法。此外,对于交叉页面流,你也需要Action链,总而言之,是职责链设计模式(CoR)的一种实现。

 

       Struts最佳实践

       下面是可能的解决方案

ü         RequestProcessor维护一个HashMap保存相同模块的所有的Action类实例。继承RequestProcessor类,提供getter方法读取这个HashMap。在应用的父Action类中提供方法,调用相同module中的其他Action类的方法。使用反射API来完成这个有用的方法。

 

对于大型应用而言,这种方式是最佳实践,因为如果在异常处理中有一些变更,无需修改代码。

 

http://www.javaworld.com/javaworld/jw-09-2004/jw-0913-struts.html?page=1

 
原创粉丝点击