spring boot2.* 整合ActiveMQ時(shí)版本引起的問(wèn)題簡(jiǎn)錄

之前項(xiàng)目使用的是一直是spring boot 2.0.*的版本整合ActiveMQ。本來(lái)以為spring boot 2的版本之間改動(dòng)應(yīng)該不會(huì)太大,所以閑來(lái)無(wú)事試著更改成spring boot 2.1.*的版本,結(jié)果出現(xiàn)了一些版本上的問(wèn)題?,F(xiàn)在版本上出現(xiàn)的問(wèn)題及解決辦法記錄了下來(lái)。
首先添加ActiveMQ相關(guān)的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-pool</artifactId>
</dependency>

將版本更改為spring boot 2.1.1

<!-- pom文件部分代碼 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

添加測(cè)試用相關(guān)業(yè)務(wù)代碼,為了測(cè)試方便直接將發(fā)送、接收整合在了一個(gè)service業(yè)務(wù)中了。

測(cè)試用service層接口
public interface MessageService {
    //發(fā)送消息
    void sendMessage(Object message);
    //發(fā)送消息對(duì)象
    void sendMessageObj(Object message);
    //接收消息
    void receiveMessage(String message);
}
測(cè)試用service層實(shí)現(xiàn)

@Service
public class MessageServiceImpl implements MessageService
{
    /**
     * 依賴注入jmsTemplate
     */
    @Resource
    private JmsTemplate jmsTemplate = null;

    /**
     * 發(fā)送消息
     */
    @Override
    public void sendMessage(Object message) {
        jmsTemplate.convertAndSend(message);
    }
    
    /**
     * 發(fā)送消息對(duì)象
     */
    @Override
    public void sendMessageObj(Object message) {
        jmsTemplate.convertAndSend(message);
    }
        
        /**
         * 接收消息
         */
    @Override
    @JmsListener(destination="${spring.jms.template.default-destination}")
    public void receiveMessage(String message) {
        /*if(message instanceof ActiveMQTextMessage) {
            ActiveMQTextMessage ac = (ActiveMQTextMessage)message;
            try {
                System.err.println("------->"+ac.getText());
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }else if(message instanceof ActiveMQObjectMessage) {
            ActiveMQObjectMessage s = (ActiveMQObjectMessage) message;
            try {
                MessageCustom object = (MessageCustom)s.getObject();
                System.err.println(object);
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }*/
        System.err.println(message);
    }

}
測(cè)試用controller層
        /**
     * 發(fā)送字符串消息
     */
    @RequestMapping("/sendMessage")
    public void sendMessage(String message) {
        messageService.sendMessage(message);
    }
測(cè)試用application.properties配置文件
spring.activemq.broker-url=tcp://localhost:61616
spring.activemq.user=admin
spring.activemq.password=admin
spring.activemq.packages.trusted=com.activemq.bean,java.lang
spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=50
spring.jms.pub-sub-domain=true
spring.jms.template.default-destination=active.default.destination

# 將spring boot日志級(jí)別調(diào)整為debug級(jí)別,以便更方便查看打印出來(lái)的日志信息(只針對(duì)org.springframework及其子包)
logging.level.org.springframework=debug

測(cè)試用例的代碼解釋不在此記錄的討論范圍內(nèi),所以相關(guān)配置等詳細(xì)解釋看官方給出的文檔
此時(shí)啟動(dòng)spring boot 應(yīng)用后,會(huì)出現(xiàn)如下日志信息。只截取重點(diǎn)的幾個(gè)部分:

....
Negative matches:
-----------------
   ActiveMQConnectionFactoryConfiguration.PooledConnectionFactoryConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'org.messaginghub.pooled.jms.JmsPoolConnectionFactory' (OnClassCondition)
   ActiveMQConnectionFactoryConfiguration.SimpleConnectionFactoryConfiguration:
      Did not match:
         - @ConditionalOnProperty (spring.activemq.pool.enabled=false) found different value in property 'enabled' (OnPropertyCondition)
      Matched:
         - @ConditionalOnClass found required class 'org.springframework.jms.connection.CachingConnectionFactory' (OnClassCondition)
   ActiveMQXAConnectionFactoryConfiguration:
      Did not match:
         - @ConditionalOnBean (types: org.springframework.boot.jms.XAConnectionFactoryWrapper; SearchStrategy: all) did not find any beans of type org.springframework.boot.jms.XAConnectionFactoryWrapper (OnBeanCondition)
      Matched:
         - @ConditionalOnClass found required class 'javax.transaction.TransactionManager' (OnClassCondition)
....
 JmsAnnotationDrivenConfiguration.JndiConfiguration:
      Did not match:
         - @ConditionalOnJndi JNDI environment is not available (OnJndiCondition)
   JmsAutoConfiguration:
      Did not match:
         - @ConditionalOnBean (types: javax.jms.ConnectionFactory; SearchStrategy: all) did not find any beans of type javax.jms.ConnectionFactory (OnBeanCondition)
      Matched:
         - @ConditionalOnClass found required classes 'javax.jms.Message', 'org.springframework.jms.core.JmsTemplate' (OnClassCondition)
   JmsAutoConfiguration.MessagingTemplateConfiguration:
      Did not match:
         - Ancestor org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration did not match (ConditionEvaluationReport.AncestorsMatchedCondition)
      Matched:
         - @ConditionalOnClass found required class 'org.springframework.jms.core.JmsMessagingTemplate' (OnClassCondition)
.....
Unconditional classes:
----------------------
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
    org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
2019-01-11 10:52:44.446 DEBUG 680 --- [main] o.s.b.d.LoggingFailureAnalysisReporter   : Application failed to start due to an exception

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.jms.core.JmsTemplate' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1644) ~[spring-beans-5.1.3.RELEASE.jar:5.1.3.RELEASE]
......
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean of type 'org.springframework.jms.core.JmsTemplate' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.jms.core.JmsTemplate' in your configuration.

出現(xiàn)了沒有找到jmsTemplate組件的錯(cuò)誤。
查了網(wǎng)上各種出現(xiàn)此錯(cuò)誤的解決辦法,都是手動(dòng)創(chuàng)建connectionFactory并創(chuàng)建JmsTemplate相關(guān)Bean對(duì)象。但是這種方式治標(biāo)不治本。既然spring boot官方有整合ActiveMQ的相關(guān)方法,不可能會(huì)要求用戶自己手動(dòng)去創(chuàng)建相關(guān)的Bean對(duì)象的。此處個(gè)人進(jìn)行了相關(guān)的分析并給出了對(duì)應(yīng)解決辦法:
通過(guò)查看日志(以上截取的有這段日志):

JmsAutoConfiguration:
      Did not match:
         - @ConditionalOnBean (types: javax.jms.ConnectionFactory; SearchStrategy: all) did not find any beans of type javax.jms.ConnectionFactory (OnBeanCondition)

說(shuō)明spring 容器中并未有ConnectionFactory相關(guān)的bean對(duì)象。在往上翻日志會(huì)有如下幾段日志:

 ActiveMQConnectionFactoryConfiguration.PooledConnectionFactoryConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'org.messaginghub.pooled.jms.JmsPoolConnectionFactory' (OnClassCondition)
   ActiveMQConnectionFactoryConfiguration.SimpleConnectionFactoryConfiguration:
      Did not match:
         - @ConditionalOnProperty (spring.activemq.pool.enabled=false) found different value in property 'enabled' (OnPropertyCondition)
      Matched:
         - @ConditionalOnClass found required class 'org.springframework.jms.connection.CachingConnectionFactory' (OnClassCondition)

spring boot 要使用PooledConnectionFactoryConfiguration靜態(tài)內(nèi)部類需要類路徑下存在org.messaginghub.pooled.jms.JmsPoolConnectionFactory類。
我這里截取了PooledConnectionFactoryConfiguration相關(guān)的源碼:

@Configuration
    @ConditionalOnClass({ JmsPoolConnectionFactory.class, PooledObject.class })
    static class PooledConnectionFactoryConfiguration {

        @Bean(destroyMethod = "stop")
        @ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true", matchIfMissing = false)
        public JmsPoolConnectionFactory pooledJmsConnectionFactory(
                ActiveMQProperties properties,
                ObjectProvider<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(
                    properties,
                    factoryCustomizers.orderedStream().collect(Collectors.toList()))
                            .createConnectionFactory(ActiveMQConnectionFactory.class);
            return new JmsPoolConnectionFactoryFactory(properties.getPool())
                    .createPooledConnectionFactory(connectionFactory);
        }
    }

PooledConnectionFactoryConfiguration這個(gè)靜態(tài)內(nèi)部類中pooledJmsConnectionFactory方法上標(biāo)注了@Bean,說(shuō)明此方法返回的對(duì)象會(huì)被加入到spring容器中。而返回的JmsPoolConnectionFactory對(duì)象元類實(shí)現(xiàn)了ConnectionFactory接口。所以此靜態(tài)內(nèi)部類便是出現(xiàn)問(wèn)題的源頭。
由于spring boot自動(dòng)配置該內(nèi)部類的條件是類路徑下必須存在JmsPoolConnectionFactory和PooledObject兩個(gè)類。
查看了依賴包中并未找到JmsPoolConnectionFactory該類,從日志信息上看到該類應(yīng)該存放于org.messaginghub.pooled.jms包下的。但是我們依賴中并未引入相關(guān)的依賴包。從maven中央倉(cāng)庫(kù)中查詢了該包的前綴org.messaginghub發(fā)現(xiàn)該依賴是在PooledJMS Library下的。然后嘗試引入該maven依賴并去掉原來(lái)的ActiveMQ Pool依賴:

<dependency>
    <groupId>org.messaginghub</groupId>
    <artifactId>pooled-jms</artifactId>
</dependency>
<!-- 去掉原來(lái)的連接池依賴
 <dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-pool</artifactId>
</dependency> 
-->

重新啟動(dòng)spring boot 應(yīng)用后,問(wèn)題解決了。
spring boot 2.1.*中引入了CachingConnectionFactory,所以在application.properties可以使用spring.jms.cache.**相關(guān)的配置。
查看spring boot 2.1.*和spring boot 2.0.*整合ActiveMQ時(shí)需要的重要類ActiveMQConnectionFactoryConfiguration源碼:

//spring boot 2.1.*版本的源碼如下
@Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
class ActiveMQConnectionFactoryConfiguration {

    @Configuration
    @ConditionalOnClass(CachingConnectionFactory.class)
    @ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "false", matchIfMissing = true)
    static class SimpleConnectionFactoryConfiguration {

        private final JmsProperties jmsProperties;

        private final ActiveMQProperties properties;

        private final List<ActiveMQConnectionFactoryCustomizer> connectionFactoryCustomizers;

        SimpleConnectionFactoryConfiguration(JmsProperties jmsProperties,
                ActiveMQProperties properties,
                ObjectProvider<ActiveMQConnectionFactoryCustomizer> connectionFactoryCustomizers) {
            this.jmsProperties = jmsProperties;
            this.properties = properties;
            this.connectionFactoryCustomizers = connectionFactoryCustomizers
                    .orderedStream().collect(Collectors.toList());
        }

        @Bean
        @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "true", matchIfMissing = true)
        public CachingConnectionFactory cachingJmsConnectionFactory() {
            JmsProperties.Cache cacheProperties = this.jmsProperties.getCache();
            CachingConnectionFactory connectionFactory = new CachingConnectionFactory(
                    createConnectionFactory());
            connectionFactory.setCacheConsumers(cacheProperties.isConsumers());
            connectionFactory.setCacheProducers(cacheProperties.isProducers());
            connectionFactory.setSessionCacheSize(cacheProperties.getSessionCacheSize());
            return connectionFactory;
        }

        @Bean
        @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "false")
        public ActiveMQConnectionFactory jmsConnectionFactory() {
            return createConnectionFactory();
        }

        private ActiveMQConnectionFactory createConnectionFactory() {
            return new ActiveMQConnectionFactoryFactory(this.properties,
                    this.connectionFactoryCustomizers)
                            .createConnectionFactory(ActiveMQConnectionFactory.class);
        }

    }

    @Configuration
    @ConditionalOnClass({ JmsPoolConnectionFactory.class, PooledObject.class })
    static class PooledConnectionFactoryConfiguration {

        @Bean(destroyMethod = "stop")
        @ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true", matchIfMissing = false)
        public JmsPoolConnectionFactory pooledJmsConnectionFactory(
                ActiveMQProperties properties,
                ObjectProvider<ActiveMQConnectionFactoryCustomizer> factoryCustomizers) {
            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactoryFactory(
                    properties,
                    factoryCustomizers.orderedStream().collect(Collectors.toList()))
                            .createConnectionFactory(ActiveMQConnectionFactory.class);
            return new JmsPoolConnectionFactoryFactory(properties.getPool())
                    .createPooledConnectionFactory(connectionFactory);
        }

    }

}


//spring 2.0.*版本的源碼如下
@Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
class ActiveMQConnectionFactoryConfiguration {

    @Bean
    @ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "false", matchIfMissing = true)
    public ActiveMQConnectionFactory jmsConnectionFactory(ActiveMQProperties properties,
            ObjectProvider<List<ActiveMQConnectionFactoryCustomizer>> factoryCustomizers) {
        return new ActiveMQConnectionFactoryFactory(properties,
                factoryCustomizers.getIfAvailable())
                        .createConnectionFactory(ActiveMQConnectionFactory.class);
    }

    @Configuration
    @ConditionalOnClass(PooledConnectionFactory.class)
    static class PooledConnectionFactoryConfiguration {

        @Bean(destroyMethod = "stop")
        @ConditionalOnProperty(prefix = "spring.activemq.pool", name = "enabled", havingValue = "true", matchIfMissing = false)
        public PooledConnectionFactory pooledJmsConnectionFactory(
                ActiveMQProperties properties,
                ObjectProvider<List<ActiveMQConnectionFactoryCustomizer>> factoryCustomizers) {
            PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(
                    new ActiveMQConnectionFactoryFactory(properties,
                            factoryCustomizers.getIfAvailable()).createConnectionFactory(
                                    ActiveMQConnectionFactory.class));
            ActiveMQProperties.Pool pool = properties.getPool();
            pooledConnectionFactory.setBlockIfSessionPoolIsFull(pool.isBlockIfFull());
            if (pool.getBlockIfFullTimeout() != null) {
                pooledConnectionFactory.setBlockIfSessionPoolIsFullTimeout(
                        pool.getBlockIfFullTimeout().toMillis());
            }
            pooledConnectionFactory
                    .setCreateConnectionOnStartup(pool.isCreateConnectionOnStartup());
            if (pool.getExpiryTimeout() != null) {
                pooledConnectionFactory
                        .setExpiryTimeout(pool.getExpiryTimeout().toMillis());
            }
            if (pool.getIdleTimeout() != null) {
                pooledConnectionFactory
                        .setIdleTimeout((int) pool.getIdleTimeout().toMillis());
            }
            pooledConnectionFactory.setMaxConnections(pool.getMaxConnections());
            pooledConnectionFactory.setMaximumActiveSessionPerConnection(
                    pool.getMaximumActiveSessionPerConnection());
            pooledConnectionFactory
                    .setReconnectOnException(pool.isReconnectOnException());
            if (pool.getTimeBetweenExpirationCheck() != null) {
                pooledConnectionFactory.setTimeBetweenExpirationCheckMillis(
                        pool.getTimeBetweenExpirationCheck().toMillis());
            }
            pooledConnectionFactory
                    .setUseAnonymousProducers(pool.isUseAnonymousProducers());
            return pooledConnectionFactory;
        }

    }

}

該類2.1.*版本與2.0.*版本的源碼差異較大。2.1.*需要使用JmsPoolConnectionFactory和PooledObject,而2.0.*則是直接使用PooledConnectionFactory。2.1.*增加了JMS緩存連接工廠,默認(rèn)采用的連接池不再是activeMQ的連接池,而是采用了PooledJMS Library。當(dāng)配置spring.activemq.pool.enabledtrue時(shí)使用的是JmsPoolConnectionFactoryProperties而不是Pool。相比2.0.*代碼有較大的改動(dòng)。
因個(gè)人水平有限,如有什么錯(cuò)誤,歡迎留言指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 【秦王會(huì)商學(xué)院今日討論】 王通老師在《牧場(chǎng)理論2.0之裂變》中分享了: 最棒的分享是用戶分享 最棒的營(yíng)銷是用戶裂變...
    吳建平閱讀 488評(píng)論 0 0
  • 都曾說(shuō),人一生最美的年紀(jì)應(yīng)當(dāng)是青春時(shí)期,那最美的豆蔻年華,不顧一切,竭力奔向詩(shī)和遠(yuǎn)方的當(dāng)初的自己。 在云步街頭,一...
    王鄭咩mei閱讀 273評(píng)論 1 1

友情鏈接更多精彩內(nèi)容