Java Springbean自定义范围JMS
我正在使用Spring框架使用DefaultMessageListenerContainer并发地使用JMS队列中的消息。我希望能够为传入的每条消息创建自动连接的bean的新实例。我认为设置scope=“prototype”会起作用,但它似乎不起作用。有人知道一个自定义bean作用域,它会根据JMS消息创建新实例吗?很像HTTP请求的“请求”范围 我意识到我可以将com.sample.TestListener设置为“BeanFactoryAware”,然后在我的onMessage中执行一个getBean(“foo”),但我希望避免将Spring依赖项放入代码中 提前感谢您的帮助 在下面的示例中,我需要一个新的“com.sample.Foo”实例,以及每次收到消息时注入的所有beanJava Springbean自定义范围JMS,java,spring,scope,spring-jms,Java,Spring,Scope,Spring Jms,我正在使用Spring框架使用DefaultMessageListenerContainer并发地使用JMS队列中的消息。我希望能够为传入的每条消息创建自动连接的bean的新实例。我认为设置scope=“prototype”会起作用,但它似乎不起作用。有人知道一个自定义bean作用域,它会根据JMS消息创建新实例吗?很像HTTP请求的“请求”范围 我意识到我可以将com.sample.TestListener设置为“BeanFactoryAware”,然后在我的onMessage中执行一个get
<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>
编写自定义作用域来实现这一点非常容易
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);
}
}
但即使这样,也可以在AOP@After建议中完成,以保持侦听器完全干净。使用Spring附带的SimpleThreadScope实现
当然,最好使用无状态bean来避免这样做。感谢您的回答和示例代码!非常感谢。@GaryRussell如果创建的bean具有
@Autowired
依赖项,您的解决方案是如何工作的?这就是我发现所有定制范围解决方案都有不足之处的地方,因为它们演示了在生产中几乎不存在创建基本POJO的情况。您的解决方案比大多数解决方案更好,因为您实现了BeanFactoryPostProcessor
,尽管我认为对于职责分离来说,最好有单独的类。请参阅。@GaryRussell您指的是这一部分吗?是我的用例。我发现你为我发布了hrlpfull。请解释为什么prototype
不起作用,以及为什么您希望每个jms消息都有一个独立的bean。
<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>
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();
}
}