自定义JmsListenerContainerFactory时,containerFactory字段的理解

序言

最近项目中利用ActiveMQ作为消息中间件,JMS进行消息传递。在对@JmsListener注解研究时对“containerFactory”字段的填值产生了一些疑问。现分享一下我的心得体会。

疑问

@Component
public class TestMessageListener implements MessageListener {

    @Override
    @JmsListener(destination = "myQueue", containerFactory = "jmsListenerContainerFactory")
    public void onMessage(Message message) {
        ...
        业务代码
        ...
    }
}
@Configuration
@EnableJms 
public class ActiveMQConfig {

    /**
     * 发布-订阅模式的ListenerContainer
     */
    @Bean
    public JmsListenerContainerFactory jmsListenerContainerTopic(ConnectionFactory factory,
                                                                    DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
        configurer.configure(jmsListenerContainerFactory, factory);
        jmsListenerContainerFactory.setConnectionFactory(factory);
        ...
        return jmsListenerContainerFactory;
    }
    
    /**
     * P2P模式的ListenerContainer
     */
    @Bean
    public JmsListenerContainerFactory jmsListenerContainerQueue(ConnectionFactory factory,
                                                                    DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory jmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
        configurer.configure(jmsListenerContainerFactory, factory);
        jmsListenerContainerFactory.setConnectionFactory(factory);
        ...
        return jmsListenerContainerFactory;
    }
}

我查找项目中,containerFactory的对应字段“jmsListenerContainerFactory”,并不存在被该字段修饰的@Bean注解,而最终项目运行起来后,却正确使用了jmsListenerContainerQueue()定义的配置属性。
按照@Bean注解的定义,在@Bean不自定义的情况下,以方法名作为标识,而若想要jmsListenerContainerQueue()被调用,containerFactory的对应字段应填写“jmsListenerContainerQueue”而不是“jmsListenerContainerFactory”。
对此我深感疑惑,由于对springboot项目也是初上手,对很多加载机制非常陌生,在翻阅了一些资料与查看了相关源码后,终于理解了其中的奥妙。

问题解析

翻找了一圈源码后,终于发现了被“jmsListenerContainerFactory”修饰的@Bean注解

    @Bean
    @ConditionalOnMissingBean(
        name = {"jmsListenerContainerFactory"}
    )
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        return factory;
    }

这是在JMS源码的JmsAnnotationDrivenConfiguration类中的一个方法,用于生成默认的JMS监听工厂。那么为何调用默认的工厂生成方法,最后返回的却是自定义的工厂?其中最关键的技术实现基础,就是springboot的scope属性即默认的singleton模式以及@Bean注解的方法参数依赖。

首先,jmsListenerContainerFactory()被@Bean注解,该方法又需要参数configurer,而该configurer由于@Bean注解的方法参数依赖,依赖调用了以下方法生成

    @Bean
    @ConditionalOnMissingBean
    public DefaultJmsListenerContainerFactoryConfigurer jmsListenerContainerFactoryConfigurer() {
        DefaultJmsListenerContainerFactoryConfigurer configurer = new DefaultJmsListenerContainerFactoryConfigurer();
        configurer.setDestinationResolver((DestinationResolver)this.destinationResolver.getIfUnique());
        configurer.setTransactionManager((JtaTransactionManager)this.transactionManager.getIfUnique());
        configurer.setMessageConverter((MessageConverter)this.messageConverter.getIfUnique());
        configurer.setJmsProperties(this.properties);
        return configurer;
    }

该方法同样被@Bean修饰,最终返回一个DefaultJmsListenerContainerFactoryConfigurer。

第一个关键点来了,项目中自定义的jmsListenerContainerQueue()中的configurer参数,同样由于@Bean的参数依赖,由jmsListenerContainerFactoryConfigurer()生成。因此,无论containerFactory字段填“jmsListenerContainerFactory”或“jmsListenerContainerQueue”,最终获得的DefaultJmsListenerContainerFactoryConfigurer是由同一个方法生成的。

但是问题又来了,jmsListenerContainerFactoryConfigurer()返回的DefaultJmsListenerContainerFactoryConfigurer对象,是每次新new出来的,为何两处拿到的对象是同一个呢。

那么引出第二个关键点,springboot中Bean加载的singleton机制,由于该机制,导致被@Bean所注解的方法,在项目启动时执行,并将该返回值放入BeanFactory中保存。后续若该方法被调用,不像普通java代码一样,重新执行一遍方法获取返回值,而是直接从BeanFactory中获取。

由此,无论containerFactory使用哪个字段,最终获得的Factory配置,均是同一个。

总结

这一次的疑问,使我对springboot的加载机制及JMS消息传递都有了深入的理解,还是那句话,实践出真知。

你可能感兴趣的:(springboot,JMS)