복붙노트

[SPRING] Spring Bean 사용자 정의 범위 JMS

SPRING

Spring Bean 사용자 정의 범위 JMS

Spring Framework를 사용하여 DefaultMessageListenerContainer를 사용하여 JMS 큐에서 메시지를 동시에 소비한다. 들어오는 각 메시지에 대해 자동으로 실행되는 bean의 새로운 인스턴스를 생성 할 수있는 능력을 원합니다. scope = "prototype"을 설정하는 것이 효과가 있지만 작업을 수행하지 않는 것 같습니다. JMS 메시지 당 새로운 인스턴스를 생성하는 커스텀 빈 범위를 아는 사람이 있습니까? HTTP 요청에 대한 "요청"범위와 비슷합니까?

필자는 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>

해결법

  1. ==============================

    1.이렇게하는 사용자 정의 범위를 작성하는 것은 꽤 쉽습니다 ...

    이렇게하는 사용자 정의 범위를 작성하는 것은 꽤 쉽습니다 ...

    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이 범위를 상속 받도록 만들 수는 있지만 약간의 작업이 필요합니다. 즉, 스프링 배치의 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 조언을 통해서도 청취자를 완전히 깨끗하게 유지할 수 있습니다.

  2. ==============================

    2.Spring과 함께 제공되는 SimpleThreadScope 구현 사용

    Spring과 함께 제공되는 SimpleThreadScope 구현 사용

    http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/context/support/SimpleThreadScope.html

  3. from https://stackoverflow.com/questions/15415688/spring-bean-custom-scope-jms by cc-by-sa and MIT license