Spring Integration 学习笔记

Spring Integration 学习笔记
The Cafe Sample(小卖部订餐例子)

    小卖部有一个订饮料服务,客户可以通过订单来订购所需要饮料。小卖部提供两种咖啡饮料
        LATTE(拿铁咖啡)和MOCHA(摩卡咖啡)。每种又都分冷饮和热饮
    整个流程如下:
        1.有一个下订单模块,用户可以按要求下一个或多个订单。
        2.有一个订单处理模块,处理订单中那些是关于订购饮料的。
        3.有一个饮料订购处理模块,处理拆分订购的具体是那些种类的饮料,把具体需要生产的饮料要求发给生产模块
        4.有一个生产模块
    Spring Integration 学习笔记_第1张图片
       
    这个例子利用Spring Integration实现了灵活的,可配置化的模式集成了上述这些服务模块。
   
    先来看一下配置文件
  
< beans:beans  xmlns ="http://www.springframework.org/schema/integration"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans
="http://www.springframework.org/schema/beans"
    xmlns:context
="http://www.springframework.org/schema/context"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/integration
        http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-2.5.xsd"
>

    

    
< message-bus />
    

    
< annotation-driven />
    

    
< context:component-scan  base-package ="org.springframework.integration.samples.cafe" />

        

        

    
< channel  id ="orders" />  
    
< channel  id ="drinks" />  
    
< channel  id ="coldDrinks" />  
    
< channel  id ="hotDrinks" />  

        

        

    
< endpoint  input-channel ="coldDrinks"  handler-ref ="barista"
                                         handler-method
="prepareColdDrink" />

        

    
< endpoint  input-channel ="hotDrinks"  handler-ref ="barista"
                                        handler-method
="prepareHotDrink" />

        

    
< beans:bean  id ="cafe"  class ="org.springframework.integration.samples.cafe.Cafe" >
        
< beans:property  name ="orderChannel"  ref ="orders" />
    
beans:bean >
beans:beans >
   
    下面我们来看一下源代码目录:
    Spring Integration 学习笔记_第2张图片
   
    我们来看一下整体服务是怎么启动的
   
    首先我们来看一下CafeDemo这个类,它触发下定单操作、
   
  
 1  public   class  CafeDemo {
 2 
 3           public   static   void  main(String[] args) {
 4               // 加载Spring 配置文件
 5              AbstractApplicationContext context  =   null ;
 6               if (args.length  >   0 ) {
 7                  context  =   new  FileSystemXmlApplicationContext(args);
 8              }
 9               else  {
10                  context  =   new  ClassPathXmlApplicationContext( " cafeDemo.xml " , CafeDemo. class );
11              }
12               // 启动 Spring容器(启动所有实现 org.springframework.context.Lifecycle接口的实现类的start方法)
13              context.start();
14               // 从Spring容器 取得cafe实例
15              Cafe cafe  =  (Cafe) context.getBean( " cafe " );
16              DrinkOrder order  =   new  DrinkOrder();
17               // 一杯热饮               参数说明1.饮料类型 2.数量 3.是否是冷饮(true表示冷饮)
18              Drink hotDoubleLatte  =   new  Drink(DrinkType.LATTE,  2 false );
19              Drink icedTripleMocha  =   new  Drink(DrinkType.MOCHA,  3 true );
20              order.addDrink(hotDoubleLatte);
21              order.addDrink(icedTripleMocha);
22               // 下100个订单
23               for  ( int  i  =   0 ; i  <   100 ; i ++ ) {
24                   // 调用cafe的placeOrder下订单
25                  cafe.placeOrder(order);
26              }
27          }
28      }

   
    下面是Cafe的源代码
   
 1  public   class  Cafe {
 2 
 3           private  MessageChannel orderChannel;
 4     
 5     
 6           public   void  setOrderChannel(MessageChannel orderChannel) {
 7               this .orderChannel  =  orderChannel;
 8          }
 9         
10           // 其实下订单操作,调用的是orderChannel(orders channel)的send方法,把消息发出去
11           public   void  placeOrder(DrinkOrder order) {
12               this .orderChannel.send( new  GenericMessage < DrinkOrder > (order));
13                   // GenericMessage有三个构建方法,参考如下
14                   // new GenericMessage(Object id, T payload);
15                   // new GenericMessage(T payload);
16                   // new GenericMessage(T payload, MessageHeader headerToCopy)
17          }
18      }

   
     
    下面我们来看一下哪个类标记有@MessageEndpoint(input="orders") 表示它会消费orders Channel的消息
    我们发现OrderSplitter类标记这个元数据,下面是源代码,我们来分析
   
       
 1  // 标记 MessageEndpoint 元数据, input表示 设置后所有 orders Channel消息都会被OrderSplitter收到
 2          @MessageEndpoint(input = " orders " )
 3           public   class  OrderSplitter {
 4         
 5               // @Splitter表示,接收消息后,调用这个类的该方法. 其的参数类型必须与message的 payload属性一致。
 6               // 即在new GenericMessage的泛型中指定
 7               // 元数据设置的 channel属性表示,方法执行完成后,会把方法返回的结果保存到message的payload属性后,发送到指定的channel中去
 8               // 这里指定发送到 drinks channel
 9              @Splitter(channel = " drinks " )
10               public  List < Drink >  split(DrinkOrder order) {
11                   return  order.getDrinks();  // 方法中,是把订单中的饮料订单取出来
12              }
13          }

   
    接下来,与找OrderSplitter方法相同,我们要找哪个类标记有@MessageEndpoint(input="drinks") 表示它会消费drinks Channel的消息
    找到DrinkRouter这个类
   
      
 1  @MessageEndpoint(input = " drinks " )
 2           public   class  DrinkRouter {
 3         
 4               // @Router表示,接收消息后,调用这个类的该方法. 其的参数类型必须与message的 payload属性一致。
 5               // 方法执行完毕后,其返回值为 在容器中定义的channel名称。channel名称必须存在
 6              @Router
 7               public  String resolveDrinkChannel(Drink drink) {
 8                   return  (drink.isIced())  ?   " coldDrinks "  :  " hotDrinks " // 方法中,是根据处理饮料是否是冷饮,送不同的channel处理
 9              }
10          }

       
       
备注:@Router可以把消息路由到多个channel,实现方式如下
            @Router
            
public  MessageChannel route(Message message) {}
           
            @Router
            
public  List < MessageChannel >  route(Message message) {}
           
            @Router
            
public  String route(Foo payload) {}
           
            @Router
            
public  List < String >  route(Foo payload) {}

       
       
        接下来,我们就要找 MessageEndpoint 标记为处理 "coldDrinks" 和 "hotDrinks" 的类,我们发现
        这个两个类并不是通过元数据@MessageEndpoint来实现的,而是通过容器配置
        (下面会演示如何用元数据配置,但元数据配置有局限性。这两种配置方式看大家喜好,系统中都是可以使用)

        下面是容器配置信息:
       

    
< endpoint  input-channel ="coldDrinks"  handler-ref ="barista"
                                         handler-method
="prepareColdDrink" />

        

    
< endpoint  input-channel ="hotDrinks"  handler-ref ="barista"
                                        handler-method
="prepareHotDrink" />

                                       
        我们来看一下源代码:
       
 1  @Component  // 这个必须要有,表示是一个消息处理组件
 2           public   class  Barista {
 3         
 4               private   long  hotDrinkDelay  =   1000 ;
 5         
 6               private   long  coldDrinkDelay  =   700 ;
 7         
 8               private  AtomicInteger hotDrinkCounter  =   new  AtomicInteger();
 9             
10               private  AtomicInteger coldDrinkCounter  =   new  AtomicInteger();
11         
12         
13               public   void  setHotDrinkDelay( long  hotDrinkDelay) {
14                   this .hotDrinkDelay  =  hotDrinkDelay;
15              }
16         
17               public   void  setColdDrinkDelay( long  coldDrinkDelay) {
18                   this .coldDrinkDelay  =  coldDrinkDelay;
19              }
20         
21               public   void  prepareHotDrink(Drink drink) {
22                   try  {
23                      Thread.sleep( this .hotDrinkDelay);
24                  }  catch  (InterruptedException e) {
25                      Thread.currentThread().interrupt();
26                  }
27                  System.out.println( " prepared hot drink # "   +  hotDrinkCounter.incrementAndGet()  +   " "   +  drink);
28              }
29         
30               public   void  prepareColdDrink(Drink drink) {
31                   try  {
32                      Thread.sleep( this .coldDrinkDelay);
33                  }  catch  (InterruptedException e) {
34                      Thread.currentThread().interrupt();
35                  }
36                  System.out.println( " prepared cold drink # "   +  coldDrinkCounter.incrementAndGet()  +   " "   +  drink);
37              }
38         
39          }

       
        如果要用元数据标识实现上述方法:
        要用元数据配置,它不像容器配置,可以在一个类中,支持多个不同的Handler方法。以处理prepareColdDrink方法为例
       
 1  @MessageEndpoint(input = " coldDrinks " // 加了该元数据,它会自动扫描,并作为@Componet标记处理
 2           public   class  Barista {
 3         
 4               private   long  hotDrinkDelay  =   1000 ;
 5         
 6               private   long  coldDrinkDelay  =   700 ;
 7         
 8               private  AtomicInteger hotDrinkCounter  =   new  AtomicInteger();
 9             
10               private  AtomicInteger coldDrinkCounter  =   new  AtomicInteger();
11         
12         
13               public   void  setHotDrinkDelay( long  hotDrinkDelay) {
14                   this .hotDrinkDelay  =  hotDrinkDelay;
15              }
16         
17               public   void  setColdDrinkDelay( long  coldDrinkDelay) {
18                   this .coldDrinkDelay  =  coldDrinkDelay;
19              }
20         
21               public   void  prepareHotDrink(Drink drink) {
22                   try  {
23                      Thread.sleep( this .hotDrinkDelay);
24                  }  catch  (InterruptedException e) {
25                      Thread.currentThread().interrupt();
26                  }
27                  System.out.println( " prepared hot drink # "   +  hotDrinkCounter.incrementAndGet()  +   " "   +  drink);
28              }
29             
30              @Handler//回调处理的方法
31               public   void  prepareColdDrink(Drink drink) {
32                   try  {
33                      Thread.sleep( this .coldDrinkDelay);
34                  }  catch  (InterruptedException e) {
35                      Thread.currentThread().interrupt();
36                  }
37                  System.out.println( " prepared cold drink # "   +  coldDrinkCounter.incrementAndGet()  +   " "   +  drink);
38              }
39          }   


       
        这样整个流程就执行完了,最终我们的饮料产品就按照订单生产出来了。累了吧,喝咖啡提神着呢!!!


初充:
下面是针对 Spring Integration adapter扩展的学习笔记

JMS Adapters
jms adapters 目前有两种实现
JmsPollingSourceAdapter 和 JmsMessageDrivenSourceAdapter. 前者是使用Srping的JmsTemplate模板类通过轮循的方式接收消息
后者是使用则通过代理Spring的DefaultMessageListenerContainer实例,实现消息驱动的方式。

xml配置如下:
JmsPollingSourceAdapter

   
   
   
   




JmsMessageDrivenSourceAdapter

   
   
   
   
   
   
   


另外还有一个比较有用的类JmsTargetAdapter 它实现了MessageHandler接口。它提把Spring Integration Message对象转换成
JMS消息并发送到指定的消息队列。与JMS服务连接的实现可以通过设定 jmsTemplate属性引用或是connectionFactory和destination
或destinationName属性。


       
       





Good Luck!
Yours Matthew!

 

你可能感兴趣的:(Spring Integration 学习笔记)