2013-03-14 103 views
4

我正在使用Spring Framework同時調用使用DefaultMessageListenerContainer從JMS隊列中消費消息。我希望能夠創建自動裝配的bean的新實例,以實現每條消息的自動裝配。我認爲設置scope =「prototype」會起作用,但似乎並沒有完成這項工作。有沒有人知道自定義bean作用域,它會爲每個JMS消息創建新的實例?很像HTTP請求的「請求」範圍嗎?Spring Bean定製範圍JMS

我意識到我可以製作com.sample.TestListener「BeanFactoryAware」,然後在我的onMessage中做一個getBean(「foo」),但我想避免將Spring依賴項放入我的代碼中。

在此先感謝您的幫助!

下面的示例,我想「com.sample.Foo」的一個新的實例,每個消息到來時注入到所有豆類。

<bean id="consumer" 
    class="com.sample.TestListener"> 
    <constructor-arg ref="foo" /> 
</bean> 

<!--Configures the Spring Message Listen Container. Points to the Connection 
    Factory, Destination, and Consumer --> 
<bean id="MessageListenerContainer" 
    class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 
    <property name="connectionFactory" ref="CachedConnectionFactory" /> 
    <property name="destination" ref="Topic" /> 
    <property name="messageListener" ref="consumer" /> 
    <property name="concurrency" value="10"/> 
</bean> 

<bean id="foo" class="com.sample.Foo"> 
    <property name="x" ref="xx" /> 
    <property name="y" ref="yy" /> 
    <property name="z" ref="zz" /> 
</bean> 
+0

我找到你後hrlpfull我。請解釋爲什麼'prototype' does not work,以及爲什麼你需要每jms消息一個sepate bean。 – 2014-01-17 20:04:04

回答

3

這是很容易編寫自定義的範圍做這...

public class CustomScope implements Scope, BeanFactoryPostProcessor { 

    @Override 
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 
     String name = "myScope"; 

     beanFactory.registerScope(name, this); 

     Assert.state(beanFactory instanceof BeanDefinitionRegistry, 
       "BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used."); 
     BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; 

     for (String beanName : beanFactory.getBeanDefinitionNames()) { 
      BeanDefinition definition = beanFactory.getBeanDefinition(beanName); 
      if (name.equals(definition.getScope())) { 
       BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, false); 
       registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition()); 
      } 
     } 
    } 

    @Override 
    public Object get(String name, ObjectFactory<?> objectFactory) { 
     return objectFactory.getObject(); // a new one every time 
    } 

    @Override 
    public String getConversationId() { 
     return null; 
    } 

    @Override 
    public void registerDestructionCallback(String name, Runnable callback) { 

    } 

    @Override 
    public Object remove(String name) { 
     return null; 
    } 

    @Override 
    public Object resolveContextualObject(String arg0) { 
     return null; 
    } 

} 


public class Foo implements MessageListener { 

    private Bar bar; 

    public void setBar(Bar bar) { 
     this.bar = bar; 
    } 

    @Override 
    public void onMessage(Message message) { 
     System.out.println(bar.getId()); 
    } 

} 
@ContextConfiguration 
@RunWith(SpringJUnit4ClassRunner.class) 
public class FooTests { 

    @Autowired 
    private Foo foo; 

    @Test 
    public void test() { 
     Message message = mock(Message.class); 
     foo.onMessage(message); 
     foo.onMessage(message); 
    } 

} 

和樣本背景...

<bean class="foo.CustomScope" /> 

<bean id="baz" class="foo.BazImpl" scope="myScope" /> 

<bean id="bar" class="foo.BarImpl" scope="myScope"> 
    <property name="baz" ref="baz" /> 
</bean> 

<bean id="foo" class="foo.Foo"> 
    <property name="bar" ref="bar" /> 
</bean> 

注意:在這個簡單的範圍內,您必須將所有引用的bean放在範圍內(上面的bar baz)。您可以使所有引用的bean繼承範圍,但需要一些工作。這就是說 - 有一個如何在Spring-Step的StepScope中做到這一點的例子。

注#2這將爲每個方法調用獲取一個新實例。如果您調用多個方法,則每次調用都會得到一個新的bean。如果您想要將其範圍限制爲允許onMessage中的所有調用使用同一個實例,則需要添加更多技巧。

編輯: 這裏有一些更新,以支持在onMessage()內的一個實例多次調用...

private final ThreadLocal<Map<String, Object>> holder = new ThreadLocal<Map<String, Object>>(); 

... 

@Override 
public Object get(String name, ObjectFactory<?> objectFactory) { 
    Map<String, Object> cache = this.holder.get(); 
    if (cache == null) { 
     cache = new HashMap<String, Object>(); 
     this.holder.set(cache); 
    } 
    Object object = cache.get(name); 
    if (object == null) { 
     object = objectFactory.getObject(); 
     cache.put(name, object); 
    } 
    return object; 
} 

public void clearCache() { 
    this.holder.remove(); 
} 

現在,你必須清除緩存...

@Override 
public void onMessage(Message message) { 
    try { 
     System.out.println(bar.getId()); 
     System.out.println(bar.getId()); 
    } 
    finally { 
     this.scope.clearCache(); 
    } 
} 

但即使這樣做可以在AOP @After建議中完成,以保持聽衆完全清潔。

+1

當然,最好使用無狀態bean來避免這樣做。 – 2013-03-14 19:09:43

+0

感謝您的答案和示例代碼!非常感謝。 – Mrc0113 2013-03-15 12:49:19

+0

@GaryRussell如果創建的bean具有'@ Autowired'依賴項,您的解決方案如何工作?這是我發現所有自定義範圍解決方案不足之處的原因,因爲它們演示了創建幾乎不存在於生產中的基本POJO。你的解決方案最好,因爲你實現了'BeanFactoryPostProcessor',儘管我認爲責任分離可能更好地分離類。 – 2015-06-15 09:37:11