SpringBoot消息源碼解析:JMS基礎自動配置
SpringBoot消息源碼解析
Spring框架對消息系統(tǒng)的整合提供了廣泛的支持:從簡單使用 Jms Template 的 JMS API,到可接收異步消息的完整基礎結構。Spring AMQP 為“高級消息隊列協(xié)議”提供了類似的功能集。
同時,Spring Boot 也為 RabbitTemplate 和 Rabbit MQ 提供了自動配置選項。Spring Boot通過自動配置對 ActiveMQ、RabbitMQ 和 Apache Kafka 提供了支持。本章重點講解 SpringBoot 對 JMS 和 ActiveMQ 的自動配置操作。
JMS 基礎自動配置
JMS 的全稱是 Java Message Service,即 Java 消息服務。它主要用于在生產(chǎn)者和消費者之間進行消息傳遞。JMS 只是一個標準, 在使用的時候需要有具體實現(xiàn),比如后面要講到的ActiveMQ。
在 Spring Boot 中,通過 JmsAutoConfiguration 自動配置來完成 JMS 的基礎組件的初始化。
像其他自動配置-樣,在 ME TA-INF/spring.factories 中可以找到注冊的 JMS 自動配置類。
# Auto Configure
org. springframework . boot . autoconfigure . jms . JmsAutoConfiguration
JmsAutoConfiguration 的注解
我們先從 JmsAutoConfiguration 的注解部分看起。
@Configuration(proxyBeanMethods = false)
@Conditional0nClass({ Message . class, JmsTemplate.class })
@ConditionalOnBean(ConnectionFactory . class)
@EnableConfigurationProperties (JmsProperties .class)
@Import (JmsAnnotat ionDrivenConfiguration. class)
public class JmsAutoConfiguration {
}@ConditionalOnClass 注解指定需要滿足在 classpath 下存在javax.jms.Message 類和
org.springframework.jms.core.Jms Template 類的條件才會進行初始化。
@ConditionalOnBean 注解指定需要在容器中存在
javax.jms.ConnectionFactory 的 Bean 對象時才會被實例化。
ConnectionFactory 接口提供了用于創(chuàng)建與 JMS 代理進行交互的 javax.jms.Connection 的標準方法。Spring 需要 ConnectionFactory 來 與 JMS 一起使用,但是通常不需要編程人員直接使用它。
那么,ConnectionFactory 的 Bean 是在什么時候被初始化的呢?以 ActiveMQ 為例,Active-MQ 的自動配置會在 JmsAutoConfiguration 配置之前執(zhí)行,并在其內部創(chuàng)建Connection-Factory 實現(xiàn)類的 Bean 對象。其他消息框架也與此類似,至于是如何初始化的,后面關于 ActiveMQ 的自動配置的部分我們會進行詳解。
@
EnableConfigurationProperties引入了JMS的配置屬性類 ,對應的就 是 在application.properties 文件中配置的以“spring.jms”為前綴的屬性。
@Import 引入了
JmsAnnotationDrivenConfiguration 配置,該配置類主要用于 Spring4.1 注解驅動的 JMS 的自動配置。
下面我們先看
JmsAnnotationDrivenConfiguration 的注解部分和構造方法的源代碼。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass (EnableJms .class)class JmsAnnotationDrivenConfiguration {
private final objectProvider destinat ionResolver;
private final objectProvider transactionManager J
private final objectProvider messageConverter;
private final JmsProperties properties;
JmsAnnotationDrivenConfiguration(ObjectProvider dest
ination-
Resolver,
ObjectProvider tr
ansactionManager, 0bject-
Provider messageConver
ter,
JmsProperties properties) {
this . destinationResolver = destinationResolver;
this. transactionManager = transactionManager;
this . messageConverter = messageConverter;
this. properties = properties;
}
} @Configuration 聲 明 該 類 也 是 配 置 類 , @ConditionalOnClass 表 示 類 路 徑 下 需 存 在EnableJms 類。其中 EnableJms 為一個注解類,@EnableJms 用于開啟 JMS 注解,使@JmsListener 注解生效。
在之前章節(jié)已經(jīng)講過
JmsAnnotationDrivenConfiguration 的構造方法中 ObjectProvider 的作用,這里看其泛型部分類。其中 JmsProperties 參數(shù)為 JMS 的配置,前面已經(jīng)提到過,下 面我們重點看其他 3 個參數(shù)。
DestinationResolver 用于解決 JMS 目標的策略接口,被 Jms Template 用于將目標名稱從簡單的字符串解析為實際的 Destination 實現(xiàn)實例。JmsTemplate 實例使用的默認Destina-tionResolver 實現(xiàn)
DynamicDestinationResolver 類。
該接口只有一個方法, 通過源代碼可以更清晰地看出它的功能。
@FunctionalInterface
public interface Dest inationResolver
//將給定的目標名稱解析為定位資源或作為動態(tài)的 Destinat ion(返回 topic 或 queue)
// Session: 為當前 JMS 的 Session
// destinat ionName:目標名稱
// pubSubDomain: true 表示 pub-sub 模式,false 表示 P2P 模式
Destination resolveDestinationName (@Nullable Session session, String destina-tionName, boolean pubSubDomain) throws
JMSException;
}Jta TransactionManager 是
PlatformTransactionManager 的實現(xiàn)類,它提供了 Spring 的JTA 事務管理,也可用于分布式事務的管理。關于事務相關的內容,在此不進行展開。
MessageConverter 是一個策略接口, 用于指定 Java 對象和 JMS 消息之間的轉換器。
SimpleMessageConverter 作 為 其 簡 單 的 默 認 實 現(xiàn) , 能 夠 處 理 TextMessages 、BytesMessages、 MapMessages 和 ObjectMessages 之 間的消息轉換。
public interface MessageConverter {
//轉換 Java 對象到 JMS 消息
Message toMessage(object
object,
Session
session)
throws
JMSException,
MessageConversionException;
//轉 JMS 消息為 Java 對象
Object fromMessage (Message message) throws JMSException, MessageConversio
nException;
}通過 MessageConverter 接口方法的定義也能夠看出它的核心功能就是進行 Java 對象與JMS 消息之間的轉換。
了解完構法我們先看
DefaultJmsListenerContainerFactoryConfigurer 的初始化,代碼如下。
@Bean
@ConditionalOnMissingBean
DefaultJmsListenerContainerFactoryConfigurer jmsL istenerContainerFactoryCon
rer() {
DefaultJmsL istenerContainerF actoryConfigurer configurer = new DefaultJmsL
is -
tenerContainerFactoryConfigurer();
configurer . setDestinationResolver(this . destinationResolver . getIfUnique
());
configurer . setTransactionManager(this . transactionManager . getIfUnique());
configurer . setMessageConverter(this . messageConverter . getIfUnique());
configurer . setJmsProperties (this . properties);
return configurer;
}初始化操作就是創(chuàng)建一個
DefaultJmsListenerContainerFactoryConfigurer 對象,然后將上面構造方法傳入的參數(shù)設置到 DefaultJmsListenerContainerFactoryConfigurer 對象中。該類的作用是配置DefaultJmsListenerContainerFactory通過DefaultJmsListenerContainerFact-oryConfigurer 中的 configure 方法將構造方法中的參數(shù)項賦值給 DefaultJmsListenerContainer-Factory.而 DefaultJmsListenerContainerFactory是個 JmsListenerContainerFactory 實現(xiàn),
用于構建常規(guī)的 DefaultMessageL istenerContainer。對于大多數(shù)用戶,這是默認設置,對于用于手動構建此類容器定義的用戶,這應該是一個很好的過渡路徑。
接下來便是
DefaultJmsListenerContainerFactory 的初始化操作,代碼如下。
@Bean
@ConditionalOnSingleCandidate(Connect ionFactory. class)
@ConditionalOnMissingBean(name = "jimsListenerContainerFactory")
DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
DefaultJmsListenerContainerF actoryConfigurer configurer, ConnectionFactory
connectionFactory) {
DefaultImsListenerContainerFactory factory = new DefaultJmsL istenerContai
nerFactory();
configurer . configure(factory, connectionFactory);
return factory;
}當存在唯一候選 ConnectionFactory 的 Bean 并且當 name 為
jmsListenerContainer-Factory的 DefaultJmsListenerContainerFactory Bean 不存在時,會執(zhí)行創(chuàng)建和初始化操作。其中創(chuàng)建就是直接 new-一個對象,而關于初始化操作,上面已經(jīng)提到了,通過 DefaultJms-ListenerContainerFactoryConfigurer 的 configure 方法來對 DefaultJmsListenerContainerFacto-ry 對應的各項參數(shù)進行賦值。
JmsAnnotationDrivenConfiguration 剩余部分代碼定義了兩個內部類,代碼如下。
@Configuration(proxyBeanMethods = false)
@EnableJms
@ConditionalOnMissingBean(name = JmsL istenerConfigUtils.JMS_ LISTENER NNOTA
TION_
PROCESSOR_ BEAN NAME)
static class EnableJmsConfiguration
@Configuration(proxyBeanMethods = false)
@Conditional0nJndi
static class IndiConfiguration {
@Bean
@Conditiona lOnMissingBean(DestinationResolver. class)
JndiDestinationResolver dest inationResolver() {
JndiDestinationResolver resolver = new JndiDestinationResolver();resolver . setFallbackToDynamicDestination(true);
return resolver;
}
}其 中 , 內 部 類 EnableJmsConfiguration 的 實 現(xiàn) 為 空 , 主 要 作 用 是 根 據(jù) 條 件 來 使@EnableJms 注解生效生效條件是類
org.springframework.jms.config.internalJmsListenerAnnotation-Processor 對應的 Bean 不存在。
內部類 JndiConfiguration 主要實例化了 JndiDestinationResolver,JndiDestinationResolver 是我們上面講到的 DestinationResolver 具體實現(xiàn),用于將目標名稱解釋為 JNDI 位置。
至此,關于 JmsAutoConfiguration 注解部分, 及其注解部分的延伸內容已經(jīng)講解完畢。下一 節(jié), 我們繼續(xù)學習 JmsAutoConfiguration 內部的自動配置實現(xiàn)。
JmsAutoC onfiauration 內部實現(xiàn)
JmsAutoConfiguration 的內部代碼部分主要包含兩個內部靜態(tài)類:Jms' TemplateConfi-guration 和 Messaging TemplateConfiguration。
Jms' TemplateConfiguration 主要用來初始化 Jms Template 對象。它的構造方法主要設置了JmsProperties、ObjectProvider
下面看 Jms TemplateConfiguration 中 Jms Template 的初始化。
@Configuration(proxyBeanMethods = false)
protected static class Jms TemplateConfiguration {
.. .
@Bean
@Condit ionalOnMissingBean
@ConditionalOnSingleCandidate(ConnectionFactory. class)
public Jms Template jmsTemplate(ConnectionFactory connectionFactory) {
PropertyMapper map = PropertyMapper .get();
//基于 ConnectionFactory 創(chuàng)建 Jms Template 對象
JmsTemplate template = new Jms Template( connectionFactory);
//沒置是否為發(fā)布訂閱模式
template . setPubSubDomain(this . properties . isPubSubDomain());
map . from(this . dest inat ionResolver: :getIfUnique ) . whenNonNull()
. to(template: :setDestinationResolver);
map . from(this . messageConverter :: getIfUnique) . whenNonNull(). to(template: :setMessageConverter);
mapTemplateProperties(this . properties . getTemplate(),template);
return template;
private void mapT emplateProperties (Template properties, JmsTemplate templ
ate)
//... Template 的其他參數(shù)設置
}
}在初始化 JmsTemplate 的過程中,明確要求必須只有一個候選 ConnectionFactory 對象存在,并且不存在 Jms Template 對象。
Jms Template 是用于簡化同步 JMS 訪問代碼的 Helper 類。以上代碼業(yè)務比較簡單,就是創(chuàng) 建 了 JmsTemplate 對 象 , 并 判 斷 DestinationResolver 、 MessageConverter 和JmsPro-perties 中的值是否為 null,如果不為 null 則對 Jms Template 進行賦值。
其中值得注意的是 Jms Template 的 pubSubDomain 的設置,默認情況下為 false,即 P2P模 式 (Point-to-Point
另外一個內部類 Messaging TemplateConfiguration 用來創(chuàng)建 JmsMessaging Template 對象。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass (JmsMessagingTemplate. class)
@Import(JmsTemplateConfiguration. class)
protected static class MessagingTemplateConfiguration {
@Bean
@ConditionalOnMiss ingBean( JmsMessageOperations. class)
@ConditionalOnSingleCandidate(Jms Template. class)
public JmsMessagingTemplate jmsMessagingTemplate(JmsProperties propertie
s,JmsTemplate
jmsTemplate) {
JmsMessagingTemplate messagingTemplate = new JmsMessagingTemplate(jms -
Templ
ate);
//沒置目標名稱
mapTemplateProperties(properties . getTemplate(), messagingTemplate);p
return
messagingTemplate;
private void mapTemplateProperties (Template properties, JmsMessagingTemplatemessagingTemplate) {
PropertyMapper map = PropertyMapper . get(). alwaysApplyingWhenNonNu1l();
/沒置目標名稱
map. from(properties : :getDefaultDestination) . to(messagingTemplate: : setDe
fault-
DestinationName);
}
}當類路徑下有 JmsMessagingTemplate 時才會觸發(fā)
MessagingTemplateConfiguration 的自動配置。通過@lmport 注解引入了上面井到的 Jms TemplateConfiguration 配置類, 為了確保 Jms Template 的實例化,創(chuàng)建、JmsMessagingTemplate 時將 Jms Template 對象作為參數(shù), 然后設置目標名稱。
JmsMessagingTemplate 為 JmsMessageOperations 的具體實現(xiàn),也是提共 Spring 發(fā)送消息的工具類。自 Spring 4.1 起,JmsMessaging Template 構建于 JmsTemplate 之上,提供了消息抽象的集成,例如
rg.springframework.messaging.Message。
至此,JmsAuto( Confiauration 相關的自動配置講解完畢,也完成了 JMS 基礎的自動配置。
本文給大家講解的內容是SpringBoot消息源碼解析:JMS基礎自動配置
下篇文章給大家講解的是以ActiveMQ為例,講解其自動配置的實現(xiàn);
覺得文章不錯的朋友可以轉發(fā)此文關注小編;
感謝大家的支持!

本文就是愿天堂沒有BUG給大家分享的內容,大家有收獲的話可以分享下,想學習更多的話可以到微信公眾號里找我,我等你哦。
