简体   繁体   English

Spring Bean自定义范围JMS

[英]Spring Bean Custom Scope JMS

I am using Spring Framework to concurrently consume messages off of a JMS queue using a DefaultMessageListenerContainer . 我正在使用Spring Framework使用DefaultMessageListenerContainer 并发消耗JMS队列中的消息 I want the ability to create new instances of the beans that are autowired for each message that comes in. I thought setting the scope="prototype" would work but it doesn't seem to do the job. 我希望能够为每个进入的消息创建自动装配的bean的新实例。我认为设置scope =“prototype”会起作用,但它似乎没有完成这项工作。 Does anybody know of a custom bean scope that would create new instances per JMS message ? 有没有人知道自定义bean范围会为每个JMS消息创建新实例 Much like the "request" scope does for HTTP Requests? 就像“请求”范围对HTTP请求一样?

I realize that I could make com.sample.TestListener "BeanFactoryAware" and then just do a getBean("foo") in my onMessage but I wanted to avoid putting the Spring dependency into my code. 我意识到我可以创建com.sample.TestListener“BeanFactoryAware”,然后在我的onMessage中执行getBean(“foo”),但我想避免将Spring依赖项放入我的代码中。

Thanks in advance for any help! 在此先感谢您的帮助!

Example below, I want a new instances of "com.sample.Foo" and all beans injected into it each time a message comes in. 下面的示例,我想要一个新的“com.sample.Foo”实例,并在每次收到消息时注入所有bean。

<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>

It's pretty easy to write a custom scope to do this... 编写自定义范围很容易做到这一点......

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);
    }

}

and a sample context... 和一个样本背景......

<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>

Note: with this simple scope, you have to put all the referenced beans in the scope as well (bar and baz above). 注意:使用这个简单的作用域,您必须将所有引用的bean放在作用域中(上面的bar baz)。 You can make all the referenced beans inherit the scope, but it takes some work. 您可以使所有引用的bean继承范围,但需要一些工作。 That said - there's an example of how to do it in spring-batch's StepScope. 这就是说 - 有一个例子说明如何在春季批次的StepScope中做到这一点。

Note#2 this will get a new instance for every method call. 注意#2这将为每个方法调用获取一个新实例。 If you call multiple methods you'll get a new bean for each call. 如果你调用多个方法,你将为每个调用获得一个新的bean。 If you want to scope it to allow all calls within onMessage to use the same instance, you'll need to add some more tricks. 如果要将其范围限制为允许onMessage中的所有调用使用相同的实例,则需要添加更多技巧。

EDIT: Here are some updates to support multiple calls to an instance within the 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();
}

Now, you do have to clear the cache... 现在, 必须清除缓存...

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

But even that could be done in an AOP @After advice, to keep the listener totally clean. 但即使这样也可以在AOP @After建议中完成,以保持听众完全清洁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM