该文档是通过adoc文件转化而来,格式有所欠佳,请见谅。
该篇博客后期可能会继续完善,未完待续o( ̄︶ ̄)o
该文档主要从3方面来讲述spring-jms的内容:
-
资源方面,
如JMS的Connection、session、consumer、producer等,spring主要通过SingleConnectionFactory和CachingConnectionFactory实现。 -
工具类方面, spring提供了同使用数据库JdbcTemplate一样简易的工具类JmsTemplate.
-
消息驱动编程(MDBs)方法,
spring主要通过SimpleMessageListenerContainer和DefaultMessageListenerContainer来实现。
connection
SingleConnectionFactory
-
只维护了一个连接
-
CachingConneciontFactory维护的Session也是连接,从ibm的jms实现来看
-
ibm jms的不同版本的实现不一样,9.1.0.3创建Session不会创建连接,
9.1.3.0创建Session会创建连接。
-
-
AggregateExceptionListener维护了连接异常处理链
-
SingleConnectionFactory
自身就实现了ExceptionListener接口,会添加到处理链上。 -
SCF
自身也提供了setExceptionListener接口,供用户自定义异常处理,添加到处理链上。
-
-
SharedConnectionInvocationHandler 实现了对Connection的代理
-
代理createSession的时候会调用getSession方法
-
CachingConnectionFactory会实现该方法,返回缓存的Session,如果缓存到达SessionCacheSize会生成新的Session。
-
SingleConnectionFactory每次都会重新创建Session
-
-
-
实现了DisposableBean接口为优雅停机提供了支持
- resetConnection()
方法主要实现了对持有的Connection进行stop和close
- resetConnection()
CachingConnectionFactory
从Figure 1.ConnectionFactory图中可以看出CachingConnectionFactory主要对SingleConnectionFactory进行了功能的扩展。
-
CCF 支持缓存Session, Producer, Consumer
- Producer, Consumer的缓存是挂在Session下面
-
CCF实现了异常重连
-
CachedSessionInvocationHandler实现了对缓存Session的代理。
-
close方法代理:
如果缓存的Session数量没达到最大的值,就执行逻辑关闭,并添加到缓存的集合;
如果达到了,就执行物理关闭。 -
代理的创建Producer和Consumer的方法,对应的方法会缓存创建出来的对象。
-
-
Override了父类SingleConnectionFactory的 resetConnection()方法
- 在父类关闭Connection的基础上,添加了关闭Session和Consumer、Producer的功能。
JmsTemplate
从Figure 2.JmsTemplate图中可以看出,JmsTemplate主要实现了两个
分支的功能。
JmsOperations接口(JmsTemplate主要实现的功能)
- 定义了通用的jms操作,如 send、receive等方法。
JmsAccessor抽象类(没有抽象方法)
- 配置了访问jms的基础配置,如:
ConnectionFactory、isSessionTransacted、SessionAcknowledgeMode、Jms异常转化、createConnection、createSession.
JmsDestinationAccessor抽象类(没有抽象方法)
- 基于JmsAccessor类的基础上添加了对Destination的支持,如:
DestinationResolver、isPubSubDomain
JmsTemplate
-
excecute方法
-
主要实现了资源(connection,session)的获取和释放,类似JdbcTemplate.
判断事务上下文中是否有存在的Session:如果有,就直接使用;如果没有,通过保存的ConnectionFactory进行创建Session(JmsAccessor的sessionTransacted默认为false,所以不需要进行提交)。 -
使用获取到的资源执行 SessionCallback接口
-
关闭资源:如果是事物的Session不需要关闭/归还如果不是事务的Session需要归还
-
小结
-
针对JmsTemplate来说,对应的Connection、Session是在需要的时候才创建的。
-
需要的资源主要靠各个ConnectionFactory提供的资源缓存功能来支持。
MessageListenerContainer
从图Figure 3. ListenerContainer的最左边来看是和JmsTemplate是一条路线的
MessageListenerContainer(其中的方法都未实现)
-
extends生命周期接口SmartLifecycle
-
包含了设置和获取MessageConverter和DestinationResolver(JmsDestinationAccessor中也有)的途径
AbstractJmsListeningContainer
官方doc说明:
-
该类提供基本的生命周期管理,特别是共享JMS连接的管理。子类应该插入到这个生命周期中,实现sharedConnectionEnabled()以及doInitialize()和doShutdown()模板方法。
-
此基类不假设任何特定的
侦听器编程模型或侦听器调用程序机制
。它只提供在JMS
Connection/Session上操作的任何基于JMS的侦听机制所需的通用运行时生命周期管理。 -
要了解具体的
侦听器编程模型(listener programming)
,请查看
AbstractMessageListenerContainer
子类。要了解具体的侦听器调用器机制(listener invoker)
,请查看DefaultMessageListenerContainer类。
SmartLifecycle
-
start()
- 创建共享连接; 回复暂停任务; 启动共享连接
-
stop()
- 停止共享连接
-
isAutoStartup()
- 默认为true
-
getPhase()
- 默认值 ==== DisposableBean
-
destroy()
-
停止共享连接
-
停止invokers(由各子类实现,DefaulMessageListenerContainer,
SimpleMessageListenerContainer)
-
重要方法
-
afterPropertiesSet中的initialize的方法
-
在这个方法中会调用doInitialize()方法, doInitialize方法:
-
在此容器中注册所有调用程序(invokers)。
-
子类(DefaulMessageListenerContainer,
SimpleMessageListenerContainer)需要为其特定的调用者管理过程实现此方法。
-
-
-
rescheduleTaskIfNecessary(具体实现)
获取给定的任务对象并重新调度它,如果容器当前正在运行,可以立即进行调度,或者在容器重新启动后进行调度。-
这个方法会执行方法
doRescheduleTask方法(由具体需要rescheduleTaskIfNecessary的子类来实现), -
DefaultMessageListenerContainer 的 doInitiallize()-→初始化
concurrentConsumres 数量的 AsyncMessageListenerInvokers
scheduleNewInvoker()-→ rescheduleTaskIfNecessary -→
doRescheduleTask() 将invokers 交付给taskExecutor执行。
即所谓的listener invokder机制
-
-
resumePausedTasks方法,恢复暂停任务
AbstractMessageListenerContainer
Spring消息侦听器容器实现的抽象基类。可以承载标准JMS
MessageListener或Spring的SessionAwareMessageListener进行实际的消息处理。
提供了设置MessageListener的功能, 为上层接口
SimpleMessageListenerContaier和DefaultMessageListenerContainer提供了执行
MessageListener的公共方法方法。
处理了在执行Listener过程中的模板步骤,如commit/rollback
重要方法
-
doExecuteListener 执行指定的侦听器,然后提交或回滚事务(如果需要)。
-
invokeListener(Sessioin,Message)
根据容器的Listener类型(SessionAwareMessageListener/MessageListener)调用不同的方法执行。
SimpleMessageListenerContainer
消息侦听器容器,它使用普通JMS客户机API的MessageConsumer.setMessageListener()方法为指定的侦听器创建并发messageconsumer。
这是消息侦听器容器的最简单形式。它创建固定数量的JMS会话来调用侦听器,不允许动态地适应运行时需求。它的主要优点是其较低的复杂性和对JMS提供者的最低要求:甚至不需要ServerSessionPool功能。
有关确认模式和事务选项的详细信息,请参阅AbstractMessageListenerContainer
javadoc。请注意,此容器公开了默认“AUTO_ACKNOWLEDGE”模式的标准JMS行为:即,在侦听器执行后自动确认消息,在抛出用户异常的情况下不进行重新交付,但在侦听器执行期间JVM死亡的情况下可能进行重新交付。(因为沿用了JmsAccessor的默认配置,sessionTransacted=false,
sessionAcknowledgeMode=Session.AUTO_ACKNOWLEDGE)
SimpleMessageListenerContainer主要实现了AbstractMessageListenerContainer的模板办法
主要实现的模板方法
-
doInitialize
-
建立共享连接
-
初始化消费者
- 根据concurrentConsumers的配置生成对应个数的Session,
MessageConsumer对象,已提供并发消费消息;
并设置MessageConsumer的MessageListener进行消息异步消费,在消费的过程会回调到AbstractmessageListenerContainer的doExecuteListener方法。
- 根据concurrentConsumers的配置生成对应个数的Session,
-
如果给SimpleMessageListenerContainer设置了TaskExecutor,
任务将在新的线程中执行,会影响acknowledgement的使用,消息总是在侦听器执行之前得到确认,并立即重用底层会话以接收下一条消息。
AbstractPollingMessageListenerContainer
基于轮询的侦听器容器实现的基类。提供基于MessageConsumer的侦听器处理支持,可选地参与外部管理的事务。
此侦听器容器变体是为重复轮询尝试而构建的,每次都调用 receiveAndExecute
方法。使用的
MessageConsumer可能会被重新获取以进行访问尝试或在两次尝试之间缓存;这取决于具体的实施。每次尝试的接收超时可以通过“
receiveTimeout ”属性配置。
底层机制基于标准JMS MessageConsumer处理,它与Java
EE环境中的本机JMS和JMS完全兼容。也不是JMS
MessageConsumer.setMessageListener设施而不是JMS
ServerSessionPool设施是必需的。这种方法的另一个优点是完全控制侦听过程,允许自定义缩放和节流以及并发消息处理(这取决于具体的子类)。
通过将springplatformtransactionmanager传递到“transactionManager”属性中,消息接收和监听器执行可以自动封装在事务中。这通常是Java
EE环境中的org.springframework.transaction.jta.JtaTransactionManager,结合从JNDI获得的jta感知JMS
ConnectionFactory(请查看应用服务器的文档)。
此基类不假设轮询调用程序的异步执行有任何特定机制。查看DefaultMessageListenerContainer的具体实现,它基于Spring的org.springframework.core.task.TaskExecutor抽象,包括并发消费者的动态伸缩和自动自恢复。
主要实现的方法
-
覆盖@Override AbstractJmsListeningContainer的initialize()方法
-
在非jta事务管理器的情况下,设置sessiontransaction =true。
-
调用父类的 initialize方法,sup.initialize(),
为了回调父类中的doInitialize的调用(个子类实现doInitialize的模板方法进行初始化invokers)-
SimpleMessageListenerContainer根据concurrentConsumers初始化了对应的Consumer对象,并进行了MessageListener的设置,进行消息的并发消费
-
DefaultmessageListenerContainer根据concurrentConsumer初始化了对应的线程数,线程的任务是AsyncMessageListenerInvoker。
-
-
-
doReceiveAndExecute
(invokder,session,MessageConsumer,TransactionStatus)-
其中的receiveMessage(consumerToUse)方法,使用了JMS的MessageConsumer的receive(timeout)接口来接收invoker的消息。
-
【如果收到消息】
-
调用模板方法(未实现) messageReceived(invoker,session):
模板方法,该方法在接收到新消息后,在尝试处理它之前立即调用。允许子类对实际传入消息的事件做出反应,例如调整它们的消费者计数- DefaultMessageListenerContainer实现了该方法:
设置当前invoker(AsyncMessageListenerInvoker)为非空闲(idle=false);
调用scheduleNewInvokerIfAppropriate()方法进行invoker数量的调整。
- DefaultMessageListenerContainer实现了该方法:
-
并使用事务工具将Session给暴露出去,可以供Listener中
JmsTemplate使用,达到事务控制的功能。 -
执行 AbstractMessageListenerContainer的
doExecuteListener(sessionToUse,message)(包含了事务的管理,如回滚和提交)
-
-
【如果没收到消息】
- 调用模板方法(未实现)
noMessageReceived(invoker,sessionToUser)
- 调用模板方法(未实现)
-
-
重点:这个方法不会归还重CachingConnectionFactory借出来的Session和MessageConsumer
-
DefaultMessageListenerContainer
主要实现的方法
-
initialize 模板方法
-
如果没有transactionManager设置cacheLevel为3(CACHE_CONSUMER)
-
创建TaskExecutor,类型为SimpleAsyncTaskExecutor,线程前缀名为DefaultMessageListenerContainer-
-
调用父类
AbstractPollingMessageListenerContainer的initialize()方法- 调用父类的AbstractJmsListeningContainer的initiallize()方法,当前的initiallize方法会回调到子类的doInitialize()方法
-
调用DefaultMessageListenerContainer的doInitialzie()方法
- 根据 concurrentConsumers 数量的配置来创建对应个数的
AsyncMessageListenerInvoker 对象
- 根据 concurrentConsumers 数量的配置来创建对应个数的
-
AsyncMessageListenerInvoker 内部类对象
循环执行MessageConsumer.receive()的程序类。它主要分为两种情况:
maxMessagesPerTask 是否大于。
-
maxMessagePerTask小于0的情况
-
调用executeOngoingLoop方法,改方法主要通过while一直执行调用
-
调用 invokeListener()方法;
-
通过调用
iniResourceIfNecessary()进行Session和Consumer的初始化(如果还未初始化); -
通过调用
AbstractPoolingMessageListenerContainer.receiveAndExecute(self-invoker,
session,consumer) 进行消息处理,对应的回调用
doReceiveAndExecute 方法,请参考APMLC的说明。
-
-
-
spring boot(mq-jms-spring-boot-starter-2.2.0)相关
MQConnectionFactoryConfiguration 配置类
-
cachingJmsConnectionFactory 方法创建 spring CachingConnectionFactory
- 设置了 cacheConsumers=false, cacheProducers=true,
sessionCacheSize=1
- 设置了 cacheConsumers=false, cacheProducers=true,
JmsAnnotationDrivenConfiguration 配置类
-
jmsListenerContainerFactory方法创建了DefaultJmsListenerContainerFactory
- 设置了 sessionTransacted=true, autoStartup=true,
receiveTimeout=30s
- 设置了 sessionTransacted=true, autoStartup=true,
彩蛋
-
通过上面的描述,可以看出JmsTemplate的sessionTransacted是false,
DefaultMessageListenerContainer的sessionTransacted是ture。
请问他们在共用一个CachingConnectionFactory的时候为什么共享的Session不会窜用(如JmsTemplate用了DMLC创建出来的的transacted的Session,反之DMLC使用了JmsTemplate非transacted的Session).- 因为DMLC的Session是在Bean的afterPropertiesSet的声明周期创建的,而且创建出来的session不会归还到CachingConnectionFactory中。