これを行うためのカスタム スコープを作成するのは非常に簡単です...
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-batch の 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 アドバイスでそれを行うこともできます。