一. 問題描述
最近在使用新版本的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>