据国外媒体报道,著名开源Spring框架厂商SpringSource宣布,将基于开源Apache Tomcat服务器,推出一款企业级的Web应用服务器。

据SpringSource表示,这个SpringSource tc服务器增加了企业管理和诊断功能,成为一个更简单的完整应用服务器组合。SpringSource的高级副总裁Peter Cooper Ellis表示,这个产品保留了所有Tomcat的优点,并且在其基础上增加了一些运营人员所需要的功能。

通过这个改进版的tc服务器,SpringSource向开源Tomcat用户提供了更多的运营和诊断功能。该服务器的另一个目标市场是目前部署在高端应 用服务器上的Web应用,诸如IBM的WebSphere和甲骨文的WebLogic。SpringSource公司认为,与上述高端应用服务器相 比,tc服务器是一个更经济的解决方案。

tc服务器还具有SpringSource应用程序管理套装,可以让用户从某单一平台上集中化管理Tomcat服务器。这些服务器能够以组的形式被定义和管理,用户能够从中心系统上获得一个应用,并将其向组推送。

ELLis表示,"我们将引入一个非常系统的方式来向众多服务器部署应用程序,你可以管理一组分布式部署的Tomcat服务器群,这是目前市场中所不具备的功能。"

通过Tomcat的诊断功能,用户可以探测到死锁和抑制状态,当两个应用程序同时访问相同的资源时有可能会发生上述状态。

鉴于SpringSource的开发者已经是Tomcat项目的贡献者,因此该公司相信自己可以为tc服务器提供专家级别的关键任务支持。

SpringSource将在2008美国SpringOne大会上宣布推出它的tc服务器。(LUPA)
Posted on January 14, 2009 1:06 PM | | Comments (0) | TrackBacks (0)
如何使用spring的作用域:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>

这里的
scope就是用来配置spring bean的作用域,它标识bean的作用域。

     在spring2.0之前bean只有2种作用域即:singleton(单例)、non-singleton(也称 prototype), Spring2.0以后,增加了session、request、global session三种专用于Web应用程序上下文的Bean。因此,默认情况下Spring2.0现在有五种类型的Bean。当然,Spring2.0对 Bean的类型的设计进行了重构,并设计出灵活的Bean类型支持,理论上可以有无数多种类型的Bean,用户可以根据自己的需要,增加新的Bean类 型,满足实际应用需求。

1、singleton作用域
当一个bean的作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把 一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都 将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中 只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时候, spring的IOC容器中只会存在一个该bean。

配置实例:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="singleton"/>
或者
<bean id="role" class="spring.chapter2.maryGame.Role" singleton="true"/>


2、prototype
     prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。 清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用 bean的后置处理器,该处理器持有要被清除的bean的引用。)
配置实例:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="prototype"/>
或者
<beanid="role" class="spring.chapter2.maryGame.Role" singleton="false"/>
 
3、request
   request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
request、session、global session使用的时候首先要在web.xml中做如下配置:

     如果你使用的是Servlet 2.4及以上的web容器,那么你仅需要在web应用的XML声明文件web.xml中增加下述ContextListener即可:
 <web-app>
  ...
  
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  
</listener>
  ...
</web-app>

,如果是Servlet2.4以前的web容器,那么你要使用一个javax.servlet.Filter的实现:
<web-app>
 ..
 
<filter> 
    
<filter-name>requestContextFilter</filter-name> 
    
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
 
</filter> 
 
<filter-mapping> 
    
<filter-name>requestContextFilter</filter-name> 
    
<url-pattern>/*</url-pattern>
 
</filter-mapping>
  ...
</web-app>

接着既可以配置bean的作用域了:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="request"/>


4、session
    session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效,配置实例:
配置实例:
和request配置实例的前提一样,配置好web启动文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="session"/>


5、global session
    global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。
配置实例:
和request配置实例的前提一样,配置好web启动文件就可以如下配置:
<bean id="role" class="spring.chapter2.maryGame.Role" scope="global session"/>


6、自定义bean装配作用域
      在spring2.0中作用域是可以任意扩展的,你可以自定义作用域,甚至你也可以重新定义已有的作用域(但是你不能覆盖 singleton和prototype),spring的作用域由接口 org.springframework.beans.factory.config.Scope来定义,自定义自己的作用域只要实现该接口即可,下面给 个实例:
我们建立一个线程的scope,该scope在表示一个线程中有效,代码如下:
publicclass MyScope implements Scope 
     privatefinal ThreadLocal threadScope = 
new ThreadLocal() {
          
protected Object initialValue() {
            returnnew HashMap(); 
          }
 
    }

     
public Object get(String name, ObjectFactory objectFactory) 
        Map scope = (Map) threadScope.get(); 
        Object object = scope.get(name); 
        
if(object==null
          object = objectFactory.getObject(); 
          scope.put(name, object); 
        }
 
        
return object; 
     }
 
     
public Object remove(String name) 
        Map scope = (Map) threadScope.get(); 
        
return scope.remove(name); 
     }

     publicvoid registerDestructionCallback(String name, Runnable callback) 

     }

    
public String getConversationId() {
       
// TODO Auto-generated method stub
       returnnull;
    }
 
          }
Posted on September 17, 2008 9:49 PM | | Comments (0) | TrackBacks (0)

介绍

你是否觉得当你的Web应用越来越复杂,理解和管理由用例编排而成的页面流程也越来越困难了呢?而被迫使用特定的方式做事情并且无法重用是不是让你感觉很累?你是否觉得使用了太多时间开发你自己特定的方法去解决普遍问题就像会话状态管理?

进入Spring Web Flow。

什么是Spring Web Flow?

Spring Web Flow (SWF) 是Spring Framework的一个脱离模块。这个模块是Spring Web应用开发模块栈的一部分,Spring Web包含Spring MVC。

Spring Web Flow 的目标是成为管理Web应用页面流程的最佳方案。当你的应用需要复杂的导航控制,例如向导,在一个比较大的事务过程中去指导用户经过一连串的步骤的时候,SWF将会是一个功能强大的控制器。

以下是一个受控制的导航的例子,使用UML状态图描述:

SpringWebFlow

图 1 - 一个航空订票服务流程

聪明的读者会认出这个是一个典型的航空订票流程,就是那种每次你通过在线方式参与订票的过程。

为什么出现Spring Web Flow?

在传统的Web应用中,页面流程就像上面所展示的,不是很明确 — 他们不是一等公民。就拿一个使用Struts的Web应用举个例子,为了在Struts里面实现页面流,大多数开发人员利用了框架提供的Action和视 图。在这种情况下,一个单独的Action就和一个指定的请求URL产生了联系。只有当请求从那个URL过来的时候,Action才会被执行。在执行过程 中,Action运行一些处理并且选择一个合适的视图显示结果。这非常简单。

所以要在Struts中实现多步控制的页面流,你需要通过不同的视图将独立的Action形成链。用来处理不同事件,例如“后退”或“提交”的 Action URL都是硬编码在视图中的。一些ad-hoc形式的会话存储被用来管理流程状态。提交后重定向被用来阻止重复提交,等等。

虽然这是一个简单并且有效的方法,但是它具有一个很大的缺陷:从struts-config.xml文件的Action定义中不能清晰的看到页面流 程。你无法从几棵树看到一片森林,就像你无法从Action和视图的定义看到页面流程一样。灵活性也因为Action和视图不能被重用而大打折扣。最后, 你仍然需要做很多工作,这并不见得容易。

Spring MVC提供了一个轻便的高层次的功能:表单控制器实现了一个与定义的页面流程。它提供了两个这样的控制器:SimpleFormController和AbstractWizardController。尽管如此,这些仍然是大多数页面流程控制概念的例子。

Tapestry和JSP在页面的基础上而不是请求的基础上使用事件驱动的方法,使得每个页面和它的后退控制器逻辑保持一致。然而,仍然需要提供一个优秀的类根据一个定义良好的能跨越多个页面和不同路径的生存周期去支持一个逻辑页面流程。就如你所看到的,这个页面流程的生存周期要比单一的请求长,但是却比一个会话要短。

这就是Spring Web Flow的切入点,允许你使用一个简单清晰的方法体现你的页面流程,并且随时重用,包括像Struts、Spring MVC、Tapestry、JSP甚至Portlets这些环境下。

优点

正如你所看到的,Spring Web Flow提供以下优点:

  • Web应用中的页面流程可以通过Web流程的定义(XML文件或者Java类)清晰的展现出来。
  • Web流程被设计成自包含的。这就允许你把你的应用中的一部分看作是一个模块,这样就你可以在多种场合重用它。
  • Web流程捕获任何合理的页面流程总是使用同种技术。你不必被迫在特定的场合使用特定的控制器。
  • 最后,Web流程是一等公民并且可以通过一个良好定义的契约使用。它具有一个清晰的,可观察的生存周期为你自动管理。通过简单配置,系统便会为你管理复杂的逻辑,总而言之,这非常容易使用。

Spring Web Flow是如何工作的?

现在已经有能力说Web流程是一组状态(states)的集合。一个状态是流程中发生某事的一个点。举个例子,譬如显示一个视图或者执行一个Action。每个状态都有一个或更多的转变(transitions)用来移动到下一个状态。

一个转变是由一个事件(event)触发的。

航空订票Web流程示例

为了展示一个Web流程的定义,下面的XML片段展示了上面UML状态图定义的航空订票处理:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE webflow PUBLIC "-//SPRING//DTD WEBFLOW//EN"
"http://www.springframework.org/dtd/spring-webflow.dtd">

 
<webflow id="bookflight" start-state="obtainTripInfo">
 
<action-state id="obtainTripInfo">
<action bean="bookingActions" method="bindAndValidate"/>
<transition on="success" to="suggestItineraries"/>
<transition on="error" to="tryAgain"/>
</action-state>
 
<action-state id="suggestItineraries">
<action bean="bookingActions"/>
<transition on="success" to="displaySuggestedItineraries"/>
</action-state>
 
<view-state id="displaySuggestedItineraries" view="suggestedItenaries">
<transition on="startOver" to="cancel"/>
<transition on="select" to="selectItinerary"/>
</view-state>
 
<action-state id="selectItinerary">
<action bean="bookingActions"/>
<transition on="success" to="isPassengerInfoRequired"/>
</action-state>
 
<decision-state id="isPassengerInfoRequired">
<if test="${requestScope.passenger == null}" then="enterPassengerInformation"/>
<if test="${requestScope.passenger.preferences.alwaysConfirmPassengerInfo}"
then="enterPassengerInformation" else="displayReservationVerification"/>

</decision-state>
 
<subflow-state id="enterPassengerInformation" flow="passenger">
<attribute-mapper>
<input value="${requestScope.passenger.id}" as="passengerId"/>
</attribute-mapper>
<transition on="finish" to="displayReservationVerification"/>
</subflow-state>
 
<view-state id="displayReservationVerification" view="reservationVerification">
<transition on="startOver" to="cancel"/>
<transition on="assignSeats" to="chooseSeatAssignments"/>
<transition on="book" to="book"/>
</view-state>
 
<subflow-state id="chooseSeatAssignments" flow="seatAssignments">
<attribute-mapper>
<input value="${requestScope.passenger.id}" as="passengerId"/>
<input name="itinerary"/>
</attribute-mapper>
<transition on="finish" to="displayReservationVerification"/>
</subflow-state>
 
<action-state id="book">
<action bean="bookingActions"/>
<transition on="success" to="displayConfirmation"/>
</action-state>
 
<end-state id="displayConfirmation" view="reservationConfirmation"/>
 
<end-state id="tryAgain" view="tryAgain"/>
 
<end-state id="cancel" view="home"/>
 
</webflow>

图 2 - 基于XML的航空订票流程定义

就像你所看到的,仅仅是扫过XML定义,逻辑流程驱动的订票流程处理就已经可以清晰地辨认出来了,即使你都不了解Spring Web Flow实现细节。

如果你看得仔细点,你将会发现两个子流程产生了订票流程的子过程。第一个子流程指导用户输入乘客信息。第二个让用户分配他的座位。这个内嵌的流程扮演了“迷你应用程序模块”的角色,这是Spring Web Flow强大的功能之一。

你可以将这份定义上交给一位业务分析人员,并且她估计能看懂。更好的是你可以根据这个定义绘制一个可视化图表将其提交给业务分析人员。做这个的工具已经诞生了。

航空订票流程祥解

这篇文章的下一部分将逐块分解上面的航空订票流程定义,并且提供对话框演示Spring Web Flow是如何工作的。

流程定义

这是第一行的XML流程定义:

<webflow id="bookflight" start-state="obtainTripInfo">
...
</webflow>

webflow 元素定义了流程,指定它的idstart-date。id是一个简单的唯一的标识符,start-state是一个转变的初始状态,这发生在当一个新的流程会话在运行时被激活的时候。

所以,在业务案例上,当订票会话被激活的时候,它将转变为obtainTripInfo状态。

获得行程信息行为状态(Action State)

下面是obtainTripInfo状态定义。

<action-state id="obtainTripInfo">
<action bean="bookingActions" method="bindAndValidate"/>
<transition on="success" to="suggestItineraries"/>
<transition on="error" to="tryAgain"/>
</action-state>

记得当状态被进入,针对该状态的行为就发生了。正如你将看到的,不同的状态类型有不同的执行动作。action state,正如obtainTripInfo,在进入的时候执行一个Action。该Action返回执行的逻辑结果,并且这个结果被映射到状态转变上。一切就是这么简单。

所以,在这个业务案例上,obtainTripInfo,当进入的时候执行bookingActions这个Action的 bindAndValidate方法。这个方法从浏览器绑定表单输入到一个Trip领域对象并且检验它。如果处理成功,就进入 suggestItineraries状态。如果错误发生,进入tryAgain状态。

订票Action

当在Spring IoC中使用Spring Web Flow的时候,action元素的bean属性涉及到Spring Application Context中定义的一个相同名称的Action实现。下面是bookingActions的定义:

web-context.xml
 
<bean id="bookingActions"
class="org.springframework.samples.bookflight.BookingActions">

<property name="bookingAgent" ref="myBookingAgent"/>
</bean>

这就允许我们的Action实现被Spring管理并且通过依赖注入进行配置。

建议路线行为状态

现在我们看一下下一个Action State,给定一个绑定的并且通过检验的Trip对象作为输入,返回一个建议的路线集合:

<action-state id="suggestItineraries">
<action bean="bookingActions"/>
<transition on="success" to="displaySuggestedItineraries"/>
</action-state>

下面是Action实现代码:

public class BookingActions extends FormAction {
...
public Event suggestItineraries(RequestContext context) {
Trip trip = (Trip)context.getRequestScope().getAttribute("trip");
Collection<Itinerary> itineraries = bookingAgent.suggestItineraries(trip);
context.getRequestScope().setAttribute("itineraries", itineraries);
return success();
}
}

当进入suggestItineraries状态的时候,suggestItineraries就被调用了。其他的Action State也是同样的工作方式:进入状态并调用指定的方法。

显示建议路线视图状态(View State)

一旦返回了一个建议的路线集合,下一步就是让用户看到它们并且让用户选择他最喜欢的。这可以通过以下的状态定义完成:

<view-state id="displaySuggestedItineraries" view="suggestedItenaries">
<transition on="startOver" to="cancel"/>
<transition on="select" to="selectItinerary"/>
</view-state>

就如你所看到的,displaySuggestedItineraries是一个view state - 一个我们还未讨论过的状态类型。一个视图状态,当进入的时候,导致执行流程暂停,并将控制返回给客户端同时根据配置的视图同时返回。随后,在用户思考过后,客户端发出一个事件描述用户执行的Action。继续流程,事件的发生已经映射到了一个状态的转变,这个转变把用户带到了流程的下一步。

所以,在这个业务案例上,当进入displaySuggestedItineraries的时候suggestedIteneraries视图被渲染并且将控制返回给浏览器。然后用户选择路线之后点击“选择”按钮。这就出发了select事件,传递选择的路线id作为事件参数。

用户也可能选择startOver,这时候流程转变到了取消状态。

对于view属性,Spring MVC中,FlowControoler使用熟悉的 ModelAndView和ViewResolver构造,在Struts中,FlowAction用ActionForward。

客户端状态

在这个问题上你可能会问:

“...自从进入ViewState之后,执行流程暂停了,控制返回给了浏览器,那么流程如何重新拾起并且继续运行呢?”

答案就是客户端跟踪一个唯一的id用户标示流程执行点,并且将这个id放在input标签内,以便引起下一个事件。典型的做法是放在一个隐藏域内。

举个例子,在一个JSP文件里:

<input type="hidden" value="<c:out value="${flowExecution.id}"/>">

“是否需要乘客信息?” 决策状态(Decision State)

用户选择了她需要的路线之后,流程需要做一个上下文关系(contextual)的决策关于下一步执行什么。

需要特别指出的是,如果用户没有登录,或者她已经登录但是希望确认她的信息 - 例如她所使用的信用卡 - 流程控制需要允许她确定这些信息。另一方面,如果她已经登录并且希望直接进入预定页面,流程控制应该跳个这个可选步骤。

基本上需要做一个动态的决策重新考虑她的信息和偏好的。

决策状态最适合这个,看下面的定义:

<decision-state id="isPassengerInfoRequired">
<if test="${requestScope.passenger == null}" then="enterPassengerInformation"/>
<if test="${requestScope.passenger.preferences.alwaysConfirmPassengerInfo}"
then="enterPassengerInformation" else="displayReservationVerification"/>

</decision-state>
输入乘客信息子流程状态(SubFlow State)

处理乘客信息的过程逻辑上独立的。这是处理的一部分,但是在机票预定这个上下文环境之外它也可以独立存在。

子流程(Subflow)状态机制就是针对这个实现的。当进入一个子流程状态,这个子流程就被产生了。父流程挂起知道子流程结束。这让你可以把你的应用作为一系列自包含的模块看待,至于流程,你可以很容易的把多种情况统一处理。

看一下enterPassengerInformation子流程状态:

<subflow-state id="enterPassengerInformation" flow="passenger">
<attribute-mapper>
<input value="${requestScope.passenger.id}" as="passengerId"/>
</attribute-mapper>
<transition on="finish" to="displayReservationVerification"/>
</subflow-state>

flow 属性是这个进入这个流程的id,attribute-mapper 元素从子流程映射属性。输入映射将属性向下映射到子流程。输出映射将属性倒退会父流程当子流程结束的时候。你可以从这里看到表达式也是支持的。

所以,在这个业务用例上,当进入enterPassengerInformation状态,乘客流程就产生了。passengerId属性传递给这 个流程作为输入。从这里,自流程作它需要做的。对于父流程来说这是一个黑箱。当子流程结束,父流程继续,应答最后结果并决定去哪执行下一步 — 在这里,去确认预定。

显示确认结束状态(End State)

最有一个状态类型在这里讨论:结束状态。当进入结束状态,活动的流程会话就结束了。在结束上面,所有与之相关的资源都被自动清理。

displayConfirmation结束状态在一条路线被被成功预定后显示确认信息:

<end-state id="displayConfirmation" view="reservationConfirmation"/>

当进入这个状态的时候,订票流程结束了并且显示reservationConfirmation视图。因为订票流程是根流程,并非子流程,所以任何分配的资源都会被自动清理。

注意:结束流程如果是一个子流程,进入这个状态就会被认为是一个子流程结果并继续父流程。更特别的是,这个状态的ID在继续父流程的子流程的状态上被用作一个状态的转变。你可以从enterPassengerInformation子流程状态定义上看出来。注意它如何响应子流程的“完成”结果,是通过一个“完成”结束状态。

流程部署

到这里,你了解了Spring Web Flow是关于什么的,并且你也看到了一个现实的例子。现在你要看到的就是如何部署这个流程定义到特定的环境中去执行,就行Spring MVC在一个Servlet环境下一样:

做这事是很容易的,这里你需要和Spring MVC一起使用:

<bean name="/booking.htm" class="org.springframework.web.flow.mvc.FlowController">
<property name="flow">
<ref bean="bookingFlow"/>
</property>
</bean>
 
<bean id="bookingFlow" class="org.springframework.web.flow.config.XmlFlowFactoryBean">
<property name="location" value="classpath:bookflight-flow.xml"/>
</bean>

这就自动将bookingFlow导出至/booking.htm这个URL在一个Servlet环境里。

高级主题

下面的部分介绍了一些SWF更高级的特性。

流程执行监听器(Flow Execution Listeners

FlowExecutionListener 构造了一个观察者允许你监听并且对一个执行着的流程的生存周期作出反应。你可以使用这个特性作任何事,从一个状态的预处理到后期条件的检测,或则审计、安全处理。

流程执行存储策略(Flow Execution Storage Strategies

一个执行着的流程的状态的存储机制是完全可插拔的。基于HttpSession的存储是默认的,但是SWF提供两种其他的存储方式:一个是使用服务器端连续的会话储存,另一种是使用完全的客户端序列化。定义你自己的存储方式,举个例子,譬如使用数据库存储,是不推荐的。

什么时候使用Spring Web Flow才适合你?

你应该注意到Spring Web Flow并不是一揽子全包的解决方案。正如你所看到的,这是一个有状态的系统能够自动管理这些由业务处理过程驱动的页面流程。它不能被当作简单的、无状态的解决方案。举个例子,它不能被用在一些需要自由导航的站点,一些可以让用户自由“点击周围任意链接”的站点。Spring Web Flow被设计为强大的受控制导航,可以指导用户按照一个清晰的业务目的和生存周期进行处理。

为了使得用例更具体,这里有一些“不错的流程”的例子,这些流程就适合使用SWF系统:

  • 航空订票
  • 税收管理
  • 申请贷款

下面的一些例子是不适合使用SWF的:

  • 索引页
  • 欢迎页
  • 菜单
  • 简单表单流(一个页面)

Spring Web Flow打算要作为一个优秀的传统的控制器在任何Web环境下,就像Spring MVC、Struts、Tapestry、Web Work、JSP或者Portlets一样。一个单一的站点可以适当的组合使用简单的控制器管理Web流程。

路线图

Spring Web Flow 1.0 final 版本将随着Spring 1.3正式版发布,时间定在JavaOne大会前大概六月份的时候。就现在而言,只能期待正式、稳定的预览版。这个产品目前已经在特性集合和示例程序方面相当成熟了。

当开发小组给最终发布版砌上最后一块砖时,下面是一些最重要的特性我们长在着手完成:

整合

作为独立的库,Spring Web Flow很好与其他框架整合了。除了Spring MVC以外,已经提供了和Struts、Portlet MVC的整合,JSP和Tapestry的整合在最终版中也会见到。

流程管理

在Spring 1.2中,在MBeanServer中输出用于管理和监视的bean是很容易的。一个FlowExecutionMBean管理接口已经存在了,我们计划扩展以便可以从JMX控制台集中监控在服务器执行的所有流程的全局统计数据。

可插拔性

系统中的每个结构都可以做成可插拔的以获得简单的扩展或定制,甚至是从xml定义中。这包括状态和转变,以及其中的其他概念。

事务补偿

提供的特性和例子程序展示了在执行过程中使用事务补偿来回滚先前提交的事务,我们对这点很感兴趣。

总结

Spring Web Flow 是控制业务处理流程的有效解决方案。并且用起来也很有意思,如果你还没试过,那么你还等什么呢?

来源: nirvanastudio.org

Posted on August 30, 2007 8:53 PM | | Comments (0) | TrackBacks (0)

关键词: Spring AOP

一.为什么要用框架和模式

1.为什么要用模式?

因为模式是一种指导,在一个良好的指导下,有助于你完成任务,有助于你作出一个优良的设计方案,达到事半功倍的效果。而且会得到解决问题的最佳办法。

2.为什么要用框架?

因为软件系统发展到今天已经很复杂了,特别是服务器端软件,设计到的知识,内容,问题太多。在某些方面使用别人成熟的框架,就相当于让别人帮你完成一些基础工作,你只需要集中精力完成系统的业务逻辑设计。而且框架一般是成熟,稳健的,他可以处理系统很多细节问题,比如,事物处理,安全性,数据流控制等问题。还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。

总之:就是让开发更简单,让我们成功。

Continue reading "AOP和Spring事务处理"
Posted on June 28, 2007 2:34 PM | | Comments (0) | TrackBacks (0)

今天对 spring AOP 事务有了一个新的认识,所以赶紧把今天的学习记下来,希望在今后的学习中能够起到一些作用,也能对今天的认识做一次总结。

Continue reading "Spring事务的学习"
Posted on June 28, 2007 2:31 PM | | Comments (1) | TrackBacks (0)

Spring1.2.8

Spring以前对一个事务拦截要通过代理实现下面的配置文件是从不同的文件中找来的,不是单独的一个Spring配置文件。

<!-- Transaction manager for a single JDBC DataSource -->
<!-- 声明一个事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<!-- 声明一个抽象Bean,这个Bean是不能实例化的,提供给其它需要AOP事务的Bean用,其它需要AOP事务的只要继承这个Bean就会被AOP接管-->
<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

<!-- 继承之前实现的抽象Bean,让这个Bean通过代理工厂生成,交给AOP托管。至于哪些方法被接管在控制Bean中已经配置了-->

Continue reading "Spring的事务管理2.0与1.2.8的区别"
Posted on June 28, 2007 2:21 PM | | Comments (0) | TrackBacks (0)

扩展Spring系列(1)--Spring 的微内核与FactoryBean扩展机制

DreamHead在《思考微内核》十分激赏 Spring的微内核与扩展机制:
“Spring 的微内核在哪里呢?便是DI容器。而通过FactoryBean,我们可以定制自己的组件组装过程,对一个普通的JavaBean做手脚,像Spring AOP中常用的ProxyFactoryBean做的那样。如此,我们就不必把所有功能都做到Spring的DI容器中去,而是以一个 FactoryBean来对DI容器的功能进行扩展。除了Spring自身之外,现在已经有一些项目开始利用这个特性扩展Spring,比如,Acegi Security和Spring Modules。”

这确是框架容器界应该贯彻的范式,微内核提供最少的功能,而由扩展接口去增强框架的能力。下面看看Spring怎么设计,明白之后就可以开始为Spring捐献精力了:)

1、微内核的功能
1.1 DI(依赖注入)与Singleton管理
利用POJO setter的DI机制,估计每位同学随手都能写一个简单版本,不多说了。
Singleton管理说白了就是先到一个map中按id找找看有没有已存在的实例。

1.2 BeanName与BeanFactory注入
除了DI注入的属性,微内核还有什么能卖给POJO呢?就是Bean在xml 定义里的id和BeanFactory自己了。
卖的机制是让POJO 实现 BeanNameAware和BeanFactoryAware接口。BeanFactory用 if(pojo instance of BeanFactoryAware)判断到POJO需要注入BeanFactory,就调用setBeanFactory(this)将自己注入。

这种框架中基于接口的注入和调用机制在Java下挺标准的,Spring的功能多是基于这种模式提供。遗憾就是Java不支持多重继承,作为替代的接口里不能提供默认的实现,导致每一个Pojo都要很无聊的实现一遍setBeanFactory()。

Continue reading "Spring 的微内核与FactoryBean扩展机制"
Posted on June 28, 2007 2:19 PM | | Comments (0) | TrackBacks (0)

想起来Spring的JDBC还不错,就拿起来研究了一下,顺便也写了个小例子,希望对初学者有点用处

主要注意以下几点:
1. 对于一些特定的小项目中,很多的工作量都在增删改上,而这些代码当然是惊人地相似,所以对5个简单的
方法抽象了一下,以达到共用的目的
2. 主要文件:applicationContext.xml, BaseDAO.java, BaseDAOImp.java, DAORowMapper.java
3. 在BaseDAOImp.java里有一个Main方法可以做测试
4. DB中只要一个表,表名:users, 三个列:id(int,主键),name(varchar), email(varchar)

这只是一个初稿,后面再做修改再与大家分享

Continue reading "使用Spring JDBC包装基本的CRUD操作"
Posted on April 13, 2007 3:40 PM | | Comments (0) | TrackBacks (0)

/**分页包装类

很简单 :)

*/

package com.xing.cms.model.util;

public class Pager {
 private int totalPages = 0;
 private int totalObjects = 0;
 private int pageNumber = 1;
 private int pageSize = 3;
 private boolean pageAble = true;
 
 private int firstResult;
 

 public void calc(){
  totalPages =  totalObjects % pageSize == 0 ? totalObjects
    / pageSize : totalObjects / pageSize + 1;
 
  firstResult = (pageNumber - 1) * pageSize;
 }
 
 public boolean isPageAble() {
  return pageAble;
 }

 public void setPageAble(boolean pageAble) {
  this.pageAble = pageAble;
 }

 public int getTotalObjects() {
  return totalObjects;
 }

 public void setTotalObjects(int param) {
  this.totalObjects = param;
 }

 public int getTotalPages() {
  return totalPages;
 }

 public void setTotalPages(int param) {
  this.totalPages = param;
 }

 public int getPageNumber() {
  return pageNumber;
 }

 public void setPageNumber(int pageNumber) {
  this.pageNumber = pageNumber;
 }

 public int getPageSize() {
  return pageSize;
 }

 public void setPageSize(int pageSize) {
  this.pageSize = pageSize;
 }
 
 public String toString(){
  return("\ntotalPages:" + totalPages +
  "\ntotalObjects:" + totalObjects +
  "\npageNumber:" + pageNumber +
  "\npageSize:" + pageSize +
  "\npageAble:" + pageAble +
  "\nfirstResult:" + firstResult);
 }

 public int getFirstResult() {
  return firstResult;
 }

 public void setFirstResult(int firstResult) {
  this.firstResult = firstResult;
 }
}

/*UserDao 接口*/

package com.xing.cms.model.dao;

import java.util.List;

import com.xing.cms.model.businessobject.User;
import com.xing.cms.model.util.Pager;

public interface UserDao {
 public abstract boolean save(User model);
 public abstract boolean update(User model);
 public abstract User get(int id);
 public abstract boolean delete(int id);
 public abstract void truncate();
 public abstract boolean delete(int[] id);
 public abstract List<User> find(String[][] searchPara,Pager pager);
 public abstract List<User> find(String[][] searchPara);
}

/*UserDao实现*/

package com.xing.cms.model.dao.impl;

import java.sql.SQLException;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
//import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.xing.cms.model.businessobject.User;
import com.xing.cms.model.dao.UserDao;
import com.xing.cms.model.util.GeneralTool;
import com.xing.cms.model.util.Pager;

public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

 private JdbcTemplate jdbcTemplate;// Spring Injection

 public UserDaoImpl() {
  super();
 }

 public JdbcTemplate getJdbcTemplate() {
  return jdbcTemplate;
 }

 public void setJdbcTemplate(JdbcTemplate param) {
  this.jdbcTemplate = param;
 }

 @SuppressWarnings("unchecked")
 public List<User> find(final String[][] searchPara, final Pager pager) {

  return (List<User>) this.getHibernateTemplate().execute(
    new HibernateCallback() {
     public Object doInHibernate(Session session)
       throws SQLException, HibernateException {
      Criteria ca_count = null;
    
      if(pager.isPageAble())
       ca_count = session.createCriteria(User.class);//不能避免,需要查询两次数据库,这个获取总数
    
      Criteria ca = session.createCriteria(User.class);

      if(searchPara != null){
       ca.setProjection(null);
      for (int i = 0; i < searchPara.length; i++) {

       if (searchPara[i][1].equalsIgnoreCase("=")) {
        if(pager.isPageAble())
         ca_count.add(Restrictions.eq(searchPara[i][0], searchPara[i][2]));
        ca.add(Restrictions.eq(searchPara[i][0], searchPara[i][2]));
       } else if (searchPara[i][1].equalsIgnoreCase("like")) {      
        if(pager.isPageAble())
         ca_count.add(Restrictions.like(searchPara[i][0], searchPara[i][2],MatchMode.ANYWHERE));
        ca.add(Restrictions.like(searchPara[i][0], searchPara[i][2],MatchMode.ANYWHERE));
      
       }

      }
      }
 
      List ret_list;

      if (pager.isPageAble()) {

       pager.setTotalObjects(((Integer) (ca_count
         .setProjection(Projections.rowCount())
         .uniqueResult())).intValue()); //获取Count

 
       pager.calc();
     
       ca.addOrder(Order.desc("UID"));
       ca.setFirstResult(pager.getFirstResult());
       ca.setMaxResults(pager.getPageSize());
       ret_list = ca.list();
 
      } else {
       ca.addOrder(Order.desc("UID"));
       ret_list = ca.list();
     
      }

      return ret_list;
     }
    });
 }

// ......其它方法略去

/*测试类*/

package test;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
import com.xing.cms.model.businessobject.User;
import com.xing.cms.model.dao.DocumentDao;
import com.xing.cms.model.dao.UserDao;
import com.xing.cms.model.util.HtmlGenerator;
import com.xing.cms.model.util.Pager;
import com.xing.cms.model.util.UtilXml;
import com.xing.cms.action.UserAction;

public class TestSpring extends AbstractTransactionalDataSourceSpringContextTests
{
 private Log logger = LogFactory.getLog(TestSpring.class);

 public String[] getConfigLocations() {
  String[] config = new String[]{"applicationContext.xml"};
  return config;
 }


 public void testUserAction() throws Exception{
 
  UserDao dao = (UserDao)applicationContext.getBean("userDao");
  assertTrue(dao!=null);
  User user = (User)applicationContext.getBean("user");
  assertTrue(user!=null);
  Pager pager = (Pager)applicationContext.getBean("pager");
  assertTrue(pager!=null);
  UserAction action = (UserAction)applicationContext.getBean("userAction");
  assertTrue(action!=null);
 
  //clear all first
  dao.truncate();
 
  //super.setComplete();
 
  //add
  for(int i=0;i<20;i++)
  {
   User u = new User();
   u.setGID(1);
   u.setUsername("xzw_"+i);
   u.setPassword("abcd");
   dao.save(u);
  }
 
 
 
  //no pager
 
  /*
  assertEquals(0,dao.find(
    new String[][]{
      new String[]{"username","=","xzw"}
    }
  ).size());
  assertEquals(20,dao.find(null).size());
  */
 
  //with pager
  //action.getPager().setPageSize(9);
  //10 11 12 ... 19 + 1 = 11
 
  /*分页,取第一页,前4 条,应该返回4*/
  action.getPager().setPageSize(4);
  assertEquals(4,dao.find(new String[][]{
    new String[]{"username","like","xzw_1"}
  },action.getPager()).size());

  /*不分页,则取全部的,应该返回20*/
  action.getPager().setPageAble(false);
  action.getPager().setPageSize(4);
  assertEquals(11,dao.find(new String[][]{
    new String[]{"username","like","xzw_1"}
  },action.getPager()).size());

 }
}

Posted on April 12, 2007 11:33 PM | | Comments (0) | TrackBacks (0)

/**
     * TOP查询
     *  @param  sql String
     *  @param  top int
     *  @return  List
      */
     public  List findTop(String sql,  int  top) {
      HibernateTemplate ht  =   this .getHibernateTemplate();
      ht.setMaxResults(top);
       return  ht.find(sql);
    }

     /**
     * 分页查询
     *  @param  sql String
     *  @param  firstRow int
     *  @param  maxRow int
     *  @return  List
      */
     public  List findPage( final  String sql, final   int  firstRow, final   int  maxRow) {
       return   this .getHibernateTemplate().executeFind( new  HibernateCallback(){
             public  Object doInHibernate(Session session)  throws  SQLException,
                    HibernateException {
               Query q  =  session.createQuery(sql);
               q.setFirstResult(firstRow);
               q.setMaxResults(maxRow);
                return  q.list();
               }
        });     
    }

模板实现分页:
public   List find(  final   String hsql,   final     int   firstRow,   final     int   maxRow)   throws         Exception {
   return  getHibernateTemplate().executeFind( new  HibernateCallback() {
     public  Object doInHibernate(Session s)  throws  HibernateException, SQLException {
           Query query  =  s.createQuery(hsql);
           query.setFirstResult(firstRow);
           query.setMaxResults(maxRow);
           List list  =  query.list();
           return  list;
           }
     });
}

Posted on April 12, 2007 11:31 PM | | Comments (0) | TrackBacks (0)
1 2 3 4
相关内容
广告计划
最新评论
[评论] 大可山 : 嗯,开放API对报纸等平面媒体的确是个好主意!
[评论] 鸿雁 : 默默地为他们祈祷吧
[评论] lym328 : 客源CRM非常不错-----如有需要可以了解一下! 可以帮助企业轻松获得大量目标客户来源,促使市场
[评论] kevinwu : 作用肯定是有的,Google会首先搜索站点的sitemap.xml文件,增加搜索的频率;国内的用户,
[评论] ss : 其实还真的感觉不到sitemap的作用~
[评论] kevinwu : 谢谢你的关注 :-)
[评论] h51h : 贵博客写得非常的好,界面简洁但内容却十分丰富 <a href="http://sexeden.blo
[评论] snguo : 这里很好 来这里支持下呢?
[评论] redondo : 感谢你分享知识! 这篇文章我转载到我的博客——Redono的日记本。 链接已注明来源。 欢迎光临Re
[评论] kevinwu : 就是这本;看来我买的贵了点 - 8折;我不想等太长时间,直接在一间书店买了