記一次工作中使用spring-boot-activemq的排錯經歷

染墨涼聲絕發表於2018-12-12

一. 問題描述

最近在使用新版本的spring boot連線activeMQ時(2.1.1.RELEASE)遇到了一個問題:引入依賴後,

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
    <relativePath/>
</parent>

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

如果開啟activemq的連線池,則 JmsTemplate 就無法自動注入進來,如下所示:

但是如果用老版本的spring boot(1.5.13.RELEASE),同樣的配置下則沒有這個問題。

經過分析原始碼,終於找到了這個問題的答案,原因如下所示:

二. 原因

對spring boot activemq的配置:

spring.activemq.broker-url=failover:(tcp://ip1:port1,tcp://ip2:port2)?nested.wireFormat.maxInactivityDuration=1000
spring.activemq.in-memory=false
# true表示使用連線池
spring.activemq.pool.enabled=true
# 連線池的最大連線數
spring.activemq.pool.max-connections=5
# 空閒的連線過期時間,預設為30s
spring.activemq.pool.idle-timeout=30000

在2.1.1版本中:

package org.springframework.boot.autoconfigure.jms.activemq;

import org.messaginghub.pooled.jms.JmsPoolConnectionFactory;


/**
 * Configuration for ActiveMQ {@link ConnectionFactory}.
 *
 * @author Greg Turnquist
 * @author Stephane Nicoll
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @author Aurélien Leboulanger
 * @since 1.1.0
 */
@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.activemq.pool.enabled=true" 時,會使用 JmsPoolConnectionFactory,但是這個類(org.messaginghub.pooled.jms.JmsPoolConnectionFactory)並不在activemq-pool這個依賴中,所以導致ConnectionFactory無法注入,因此 JmsTemplate也就無法由Spring容器來管理。因此需要引入別的依賴,如下所示:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
    <groupId>org.messaginghub</groupId>
    <artifactId>pooled-jms</artifactId>
</dependency>

而在1.5.13版本中

package org.springframework.boot.autoconfigure.jms.activemq;


import org.apache.activemq.pool.PooledConnectionFactory;


/**
 * Configuration for ActiveMQ {@link ConnectionFactory}.
 *
 * @author Greg Turnquist
 * @author Stephane Nicoll
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @author Aurélien Leboulanger
 * @since 1.1.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)
		@ConfigurationProperties(prefix = "spring.activemq.pool.configuration")
		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());
			pooledConnectionFactory
					.setBlockIfSessionPoolIsFullTimeout(pool.getBlockIfFullTimeout());
			pooledConnectionFactory
					.setCreateConnectionOnStartup(pool.isCreateConnectionOnStartup());
			pooledConnectionFactory.setExpiryTimeout(pool.getExpiryTimeout());
			pooledConnectionFactory.setIdleTimeout(pool.getIdleTimeout());
			pooledConnectionFactory.setMaxConnections(pool.getMaxConnections());
			pooledConnectionFactory.setMaximumActiveSessionPerConnection(
					pool.getMaximumActiveSessionPerConnection());
			pooledConnectionFactory
					.setReconnectOnException(pool.isReconnectOnException());
			pooledConnectionFactory.setTimeBetweenExpirationCheckMillis(
					pool.getTimeBetweenExpirationCheck());
			pooledConnectionFactory
					.setUseAnonymousProducers(pool.isUseAnonymousProducers());
			return pooledConnectionFactory;
		}

	}

}

當 "spring.activemq.pool.enabled=true" 這個配置項存在時,往spring容器中注入的是 PooledConnectionFactory 這個類,而這個類,在activemq-pool這個依賴中,因此,在1.5.13的版本中,可以依賴activemq-pool:


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

 

相關文章