1 DestinationResolver
DestinationResolver接口的作用是将指定的目的地名解析为目的地实例。其定义如下:
- public interface DestinationResolver {
- Destination resolveDestinationName(Session session, String destinationName,
- boolean pubSubDomain) throws JMSException;
- }
public interface DestinationResolver {
Destination resolveDestinationName(Session session, String destinationName,
boolean pubSubDomain) throws JMSException;
}
参数pubSubDomain用于指定是使用“发布/订阅”模式(解析后的目的地是Topic),还是使用“点对点”模式(解析后的目的地是Queue)。
CachingDestinationResolver接口继承了DestinationResolver,增加了缓存的功能,其接口定义如下:
- public interface CachingDestinationResolver extends DestinationResolver {
- void removeFromCache(String destinationName);
- void clearCache();
- }
public interface CachingDestinationResolver extends DestinationResolver {
void removeFromCache(String destinationName);
void clearCache();
}
在目的地失效的时候,removeFromCache方法会被调用;在JMS provider失效的时候,clearCache方法会被调用。
1.1 DynamicDestinationResolver
DynamicDestinationResolver实现了DestinationResolver接口。根据指定的目的地名,DynamicDestinationResolver会动态创建目的地实例。针对JMS1.1规范,它采用如下方法创建目的地:
- session.createTopic(topicName)
- session.createQueue(queueName);
session.createTopic(topicName)
session.createQueue(queueName);
1.2 JndiDestinationResolver
JndiDestinationResolver继承自JndiLocatorSupport, 同时实现了CachingDestinationResolver接口。如果在JMS provider中配置了静态目的地,那么JndiDestinationResolver通过JNDI查找的方式获得目的地实例。
JndiDestinationResolver的fallbackToDynamicDestination属性用于指定在JNDI查找失败后,是否使 用动态目的地,默认值是false。JndiDestinationResolver的cache属性用于指定是否对目的地实例进行缓存,默认值是 true。
1.3 BeanFactoryDestinationResolver
BeanFactoryDestinationResolver实现了DestinationResolver接口和BeanFactoryAware接口。它会根据指定的目的地名从BeanFactory中查找目的地实例。以下是相关的代码:
- public Destination resolveDestinationName(Session session, String destinationName,
- boolean pubSubDomain) throws JMSException {
- Assert.state(this .beanFactory != null , "BeanFactory is required" );
- try {
- return (Destination) this .beanFactory.getBean(destinationName, Destination. class );
- }
- catch (BeansException ex) {
- throw new DestinationResolutionException(
- "Failed to look up Destinaton bean with name '" + destinationName + "'" , ex);
- }
- }
public Destination resolveDestinationName(Session session, String destinationName,
boolean pubSubDomain) throws JMSException {
Assert.state(this.beanFactory != null, "BeanFactory is required");
try {
return (Destination) this.beanFactory.getBean(destinationName, Destination.class);
}
catch (BeansException ex) {
throw new DestinationResolutionException(
"Failed to look up Destinaton bean with name '" + destinationName + "'", ex);
}
}
2 JmsAccessor
抽象类JmsAccessor是JmsTemplate、SimpleMessageListenerContainer和DefaultMessageListenerContainer 等concrete class的基类。JmsAccessor定义了如下几个用于访问JMS服务的共通属性。
- private ConnectionFactory connectionFactory;
- private boolean sessionTransacted = false ;
- private int sessionAcknowledgeMode = Session.AUTO_ACKNOWLEDGE;
private ConnectionFactory connectionFactory;
private boolean sessionTransacted = false;
private int sessionAcknowledgeMode = Session.AUTO_ACKNOWLEDGE;
JmsAccessor提供了创建Connection和Session的方法,如下:
- protected Connection createConnection() throws JMSException {
- return getConnectionFactory().createConnection();
- }
- protected Session createSession(Connection con) throws JMSException {
- return con.createSession(isSessionTransacted(), getSessionAcknowledgeMode());
- }
protected Connection createConnection() throws JMSException {
return getConnectionFactory().createConnection();
}
protected Session createSession(Connection con) throws JMSException {
return con.createSession(isSessionTransacted(), getSessionAcknowledgeMode());
}
2.1 JmsDestinationAccessor
抽象类JmsDestinationAccessor继承自JmsAccessor,增加了destinationResolver和 pubSubDomain属性。destinationResolver的默认值是DynamicDestinationResolver的实例,也就是 说默认采用动态目的地解析的方式;pubSubDomain用于指定是使用“发布/订阅”模式还是使用“点对点”模式,默认值是false。
JmsDestinationAccessor提供了用于解析目的地的方法,如下:
- protected Destination resolveDestinationName(Session session, String destinationName)
- throws JMSException {
- return getDestinationResolver().resolveDestinationName(session, destinationName,
- isPubSubDomain());
- }
protected Destination resolveDestinationName(Session session, String destinationName)
throws JMSException {
return getDestinationResolver().resolveDestinationName(session, destinationName,
isPubSubDomain());
}
2.2 AbstractJmsListeningContainer
AbstractJmsListeningContainer继承自JmsDestinationAccessor,作为所有Message Listener Container的公共基类。它主要提供了JMS connection的生命周期管理的功能,但是没有对消息接收的方式(主动接收方式或者异步接收方式)等做任何假定。该类主要的属性如下:
- private String clientId;
- private Connection sharedConnection;
private String clientId;
private Connection sharedConnection;
clientId通常用于持久订阅;sharedConnection保存了被共享的JMS connection。
该类定义了如下的抽象方法,以便子类可以决定是否使用共享的JMS connection。
- protected abstract boolean sharedConnectionEnabled();
protected abstract boolean sharedConnectionEnabled();
2.3 AbstractMessageListenerContainer
AbstractMessageListenerContainer继承自AbstractJmsListeningContainer,也是作为所有Message Listener Container的公共基类。该类主要的属性如下:
- private volatile Object destination;
- private volatile Object messageListener;
- private boolean exposeListenerSession = true ;
private volatile Object destination;
private volatile Object messageListener;
private boolean exposeListenerSession = true;
destination用于指定接收消息的目的地。
messageListener用于指定处理消息的listener。对于messageListener,它既可以是符合JMS规范的javax.jms.MessageListener,也可以是Spring 特有的org.springframework.jms.listener.SessionAwareMessageListener。SessionAwareMessageListener的定义如下:
- public interface SessionAwareMessageListener {
- void onMessage(Message message, Session session) throws JMSException;
- }
public interface SessionAwareMessageListener {
void onMessage(Message message, Session session) throws JMSException;
}
跟javax.jms.MessageListener相比,这个接口的onMessage方法增加了一个Session 类型的参数,可以通过这个session发送回复消息(reply message)。
如果使用了SessionAwareMessageListener 类型的message listener,那么exposeListenerSession参数指定了传入onMessage方法的session参数是否是创建了 MessageConsumer的session,默认值是true。如果是false,那么 AbstractMessageListenerContainer会在connection上新建一个session,并传入onMessage方法。
2.4 AbstractPollingMessageListenerContainer
AbstractPollingMessageListenerContainer继承自AbstractMessageListenerContainer,它提供了对于主动接收消息(polling)的支持,以及支持外部的事务管理。
- private boolean pubSubNoLocal = false ;
- private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
- private PlatformTransactionManager transactionManager;
private boolean pubSubNoLocal = false;
private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
private PlatformTransactionManager transactionManager;
如果使用“发布/订阅”模式,那么pubSubNoLocal 属性指定通过某个连接发送到某个Topic的消息,是否应该被投递回这个连接。
receiveTimeout属性用于指定调用MessageConsumer的receive方法时的超时时间,默认值是1秒。需要注意的是,这个值应该比transactionManager 中指定的事务超时时间略小。
通常情况下,应该为transactionManager设置一个 org.springframework.transaction.jta.JtaTransactionManager的实例,此外也要设置一个支持 XA的ConnectionFactory。需要注意的是,XA 事务对性能有较大的影响。
如果只是希望使用local JMS transaction,那么只要设置sessionTransacted为true或者使用JmsTransactionManager即可。实际上, 如果设置了非JTA的transactionManager,那么sessionTransacted属性会自动被设置成true。
由于local JMS transaction无法同其它local transaction(例如local database transaction)进行协调,因此客户端程序可能需要对重发的消息进行检查。JMS规范要求:JMS provider应该将重发消息的JMSRedelivered属性设置为true。
2.5 SimpleMessageListenerContainer
SimpleMessageListenerContainer继承自AbstractMessageListenerContainer,使用异步方式 接收消息(也就是通过MessageConsumer上注册MessageListener的方式接收消息)。该类主要的属性如下:
- private boolean pubSubNoLocal = false ;
- private int concurrentConsumers = 1 ;
- private Set sessions;
- private Set consumers;
- private TaskExecutor taskExecutor;
private boolean pubSubNoLocal = false;
private int concurrentConsumers = 1;
private Set sessions;
private Set consumers;
private TaskExecutor taskExecutor;
如果使用“发布/订阅”模式,那么pubSubNoLocal 属性指定通过某个连接发送到某个Topic的消息,是否应该被投递回这个连接。
SimpleMessageListenerContainer允许创建多个Session和MessageConsumer来接收消息。具体的个数由 concurrentConsumers属性指定。需要注意的是,应该只是在Destination为Queue的时候才使用多个 MessageConsumer(Queue中的一个消息只能被一个Consumer接收),虽然使用多个MessageConsumer会提高消息处理 的性能,但是消息处理的顺序却得不到保证:消息被接收的顺序仍然是消息发送时的顺序,但是由于消息可能会被并发处理,因此消息处理的顺序可能和消息发送的 顺序不同。此外,不应该在Destination为Topic的时候使用多个MessageConsumer,这是因为多个 MessageConsumer会接收到同样的消息。
SimpleMessageListenerContainer创建的Session和MessageConsumer分别保存在sessions和consumers属性中。
taskExecutor属性的默认值是null,也就是说,对MessageListener(或者 SessionAwareMessageListener)的回调是在MessageConsumer的内部线程中执行。如果指定了 taskExecutor,那么回调是在TaskExecutor内部的线程中执行。以下是相关的代码:
- protected MessageConsumer createListenerConsumer( final Session session)
- throws JMSException {
- Destination destination = getDestination();
- if (destination == null ) {
- destination = resolveDestinationName(session, getDestinationName());
- }
- MessageConsumer consumer = createConsumer(session, destination);
- if ( this .taskExecutor != null ) {
- consumer.setMessageListener(new MessageListener() {
- public void onMessage( final Message message) {
- taskExecutor.execute(new Runnable() {
- public void run() {
- processMessage(message, session);
- }
- });
- }
- });
- }
- else {
- consumer.setMessageListener(new MessageListener() {
- public void onMessage(Message message) {
- processMessage(message, session);
- }
- });
- }
- return consumer;
- }
protected MessageConsumer createListenerConsumer(final Session session)
throws JMSException {
Destination destination = getDestination();
if (destination == null) {
destination = resolveDestinationName(session, getDestinationName());
}
MessageConsumer consumer = createConsumer(session, destination);
if (this.taskExecutor != null) {
consumer.setMessageListener(new MessageListener() {
public void onMessage(final Message message) {
taskExecutor.execute(new Runnable() {
public void run() {
processMessage(message, session);
}
});
}
});
}
else {
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
processMessage(message, session);
}
});
}
return consumer;
}
需要注意的是,如果指定了taskExecutor,那么消息在被taskExecutor内部的线程处理前,可能已经被确认过了(外层的 onMessage方法可能已经执行结束了)。因此如果使用事务Session或者Session.CLIENT_ACKNOWLEDGE类型的确认模 式,那么可能会导致问题。
该类的sharedConnectionEnabled方法(在AbstractJmsListeningContainer中定义)总是返回true, 因此SimpleMessageListenerContainer会使用共享的JMS connection。
2.6 DefaultMessageListenerContainer
DefaultMessageListenerContainer 继承自AbstractPollingMessageListenerContainer,主要使用同步的方式接收消息(也就是通过循环调用MessageConsumer.receive的方式接收消息)。该类主要的属性如下:
- private int concurrentConsumers = 1 ;
- private int maxConcurrentConsumers = 1 ;
- private int maxMessagesPerTask = Integer.MIN_VALUE;
- private int idleTaskExecutionLimit = 1 ;
- private final Set scheduledInvokers = new HashSet();
- private TaskExecutor taskExecutor;
- private int cacheLevel = CACHE_AUTO;
private int concurrentConsumers = 1;
private int maxConcurrentConsumers = 1;
private int maxMessagesPerTask = Integer.MIN_VALUE;
private int idleTaskExecutionLimit = 1;
private final Set scheduledInvokers = new HashSet();
private TaskExecutor taskExecutor;
private int cacheLevel = CACHE_AUTO;
跟SimpleMessageListenerContainer一样,DefaultMessageListenerContainer 也支持创建多个Session和MessageConsumer来接收消息。跟SimpleMessageListenerContainer不同的是,DefaultMessageListenerContainer 创建了concurrentConsumers所指定个数的AsyncMessageListenerInvoker(实现了SchedulingAwareRunnable接口),并交给taskExecutor运行。
maxMessagesPerTask属性的默认值是Integer.MIN_VALUE,但是如果设置的taskExecutor(默认值是 SimpleAsyncTaskExecutor)实现了SchedulingTaskExecutor接口并且其 prefersShortLivedTasks方法返回true(也就是说该TaskExecutor倾向于短期任务),那么 maxMessagesPerTask属性会自动被设置为10。
如果maxMessagesPerTask属性的值小于0,那么AsyncMessageListenerInvoker.run方法会在循环中反复尝试 接收消息,并在接收到消息后调用MessageListener(或者SessionAwareMessageListener);如果 maxMessagesPerTask属性的值不小于0,那么AsyncMessageListenerInvoker.run方法里最多会尝试接收消息 maxMessagesPerTask次,每次接收消息的超时时间由其父类 AbstractPollingMessageListenerContainer的receiveTimeout属性指定。如果在这些尝试中都没有接收 到消息,那么AsyncMessageListenerInvoker的idleTaskExecutionCount属性会被累加。在run方法执行完 毕前会对idleTaskExecutionCount进行检查,如果该值超过了DefaultMessageListenerContainer .idleTaskExecutionLimit(默认值1),那么这个AsyncMessageListenerInvoker可能会被销毁。
所有AsyncMessageListenerInvoker实例都保存在scheduledInvokers中,实例的个数可以在 concurrentConsumers和maxConcurrentConsumers之间浮动。跟 SimpleMessageListenerContainer一样,应该只是在Destination为Queue的时候才使用多个 AsyncMessageListenerInvoker实例。
cacheLevel属性用于指定是否对JMS资源进行缓存,可选的值是CACHE_NONE = 0、CACHE_CONNECTION = 1、CACHE_SESSION = 2、CACHE_CONSUMER = 3和CACHE_AUTO = 4。默认情况下,如果transactionManager属性不为null,那么cacheLevel被自动设置为CACHE_NONE(不进行缓 存),否则cacheLevel被自动设置为CACHE_CONSUMER。
如果cacheLevel属性值大于等于CACHE_CONNECTION,那么sharedConnectionEnabled方法(在AbstractJmsListeningContainer中定义)返回true,也就是说使用共享的JMS连接。
3 SingleConnectionFactory
SingleConnectionFactory实现了ConnectionFactory接口,其createConnection方法总是返回相同的 Connection。可以在SingleConnectionFactory的构造函数中传入Connection对象或者 ConnectionFactory对象,用来创建被代理的连接对象。 SingleConnectionFactory.createConnection方法返回的连接是个代理,它忽略了对stop和close方法的调用 (连接会在SingleConnectionFactory.destroy方法中关闭)。
SingleConnectionFactory的reconnectOnException属性用来指定是否在连接抛出JMSException的时候,对连接进行重置,重置后如果再调用createConnection方法,那么会返回一个新的连接。
需要注意的是,AbstractJmsListeningContainer类的抽象方法sharedConnectionEnabled指定了是否在 message listener container内部使用共享的JMS连接。因此通常情况下不需要为单独的message listener container设置SingleConnectionFactory(及其子类);如果希望在不同的message listener container之间共享JMS连接,那么可以考虑使用SingleConnectionFactory。
3.1 CachingConnectionFactory
CachingConnectionFactory继承自SingleConnectionFactory,增加了对Session和MessageProducer缓存的功能。该类主要的属性如下:
- private int sessionCacheSize = 1 ;
- private boolean cacheProducers = true ;
private int sessionCacheSize = 1;
private boolean cacheProducers = true;
sessionCacheSize属性指定了被缓存的Session实例的个数(默认值是1),也就是说,如果同时请求的Session个数大于sessionCacheSize,那么这些Session不会被缓存,而是正常的被创建和销毁。
cacheProducers属性指定了是否对MessageProducer进行缓存,默认值是true。