SpringBoot2.0原始碼分析(二):整合ActiveMQ分析

貳級天災發表於2018-10-21

SpringBoot具體整合ActiveMQ可參考:SpringBoot2.0應用(二):SpringBoot2.0整合ActiveMQ

ActiveMQ自動注入

當專案中存在javax.jms.Messageorg.springframework.jms.core.JmsTemplate著兩個類時,SpringBoot將ActiveMQ需要使用到的物件註冊為Bean,供專案注入使用。一起看一下JmsAutoConfiguration類。

@Configuration
@ConditionalOnClass({ Message.class, JmsTemplate.class })
@ConditionalOnBean(ConnectionFactory.class)
@EnableConfigurationProperties(JmsProperties.class)
@Import(JmsAnnotationDrivenConfiguration.class)
public class JmsAutoConfiguration {

	@Configuration
	protected static class JmsTemplateConfiguration {
	    ......
		@Bean
		@ConditionalOnMissingBean
		@ConditionalOnSingleCandidate(ConnectionFactory.class)
		public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) {
			PropertyMapper map = PropertyMapper.get();
			JmsTemplate template = new JmsTemplate(connectionFactory);
			template.setPubSubDomain(this.properties.isPubSubDomain());
			map.from(this.destinationResolver::getIfUnique).whenNonNull()
					.to(template::setDestinationResolver);
			map.from(this.messageConverter::getIfUnique).whenNonNull()
					.to(template::setMessageConverter);
			mapTemplateProperties(this.properties.getTemplate(), template);
			return template;
		}
            ......
	}

	@Configuration
	@ConditionalOnClass(JmsMessagingTemplate.class)
	@Import(JmsTemplateConfiguration.class)
	protected static class MessagingTemplateConfiguration {

		@Bean
		@ConditionalOnMissingBean
		@ConditionalOnSingleCandidate(JmsTemplate.class)
		public JmsMessagingTemplate jmsMessagingTemplate(JmsTemplate jmsTemplate) {
			return new JmsMessagingTemplate(jmsTemplate);
		}

	}

}
複製程式碼

RabbitAutoConfiguration主要注入了jmsMessagingTemplatejmsTemplateRabbitAutoConfiguration同時匯入了RabbitAnnotationDrivenConfiguration,注入了jmsListenerContainerFactory

訊息傳送

以下面的傳送為例:

    jmsMessagingTemplate.convertAndSend(this.queue, msg);
複製程式碼

這個方法會先對訊息進行轉換,預處理,最終通過呼叫JmsTemplate的doSend實現訊息傳送的。

	protected void doSend(Session session, Destination destination, MessageCreator messageCreator)
			throws JMSException {
		Assert.notNull(messageCreator, "MessageCreator must not be null");
		MessageProducer producer = createProducer(session, destination);
		try {
			Message message = messageCreator.createMessage(session);
			doSend(producer, message);
			if (session.getTransacted() && isSessionLocallyTransacted(session)) {
				JmsUtils.commitIfNecessary(session);
			}
		}
		finally {
			JmsUtils.closeMessageProducer(producer);
		}
	}
複製程式碼

首先建立一個MessageProducer的例項,接著將最初的org.springframework.messaging.Message轉換成javax.jms.Message,再將訊息委託給producer進行傳送。

訊息接收

先看一個消費的事例:

@Component
public class Consumer {
	@JmsListener(destination = "sample.queue")
	public void receiveQueue(String text) {
		System.out.println(text);
	}
}
複製程式碼

SpringBoot會去解析@JmsListener,具體實現在JmsListenerAnnotationBeanPostProcessorpostProcessAfterInitialization方法。

	public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
		if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
			Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);
			Map<Method, Set<JmsListener>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
					(MethodIntrospector.MetadataLookup<Set<JmsListener>>) method -> {
						Set<JmsListener> listenerMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
								method, JmsListener.class, JmsListeners.class);
						return (!listenerMethods.isEmpty() ? listenerMethods : null);
					});
			if (annotatedMethods.isEmpty()) {
				this.nonAnnotatedClasses.add(bean.getClass());
			}
			else {
				annotatedMethods.forEach((method, listeners) ->
						listeners.forEach(listener ->
								processJmsListener(listener, method, bean)));
			}
		}
		return bean;
	}
複製程式碼

SpringBoot根據註解找到了使用了@JmsListener註解的方法,當監聽到ActiveMQ收到的訊息時,會呼叫對應的方法。來看一下具體怎麼進行listener和method的繫結的。

	protected void processJmsListener(JmsListener jmsListener, Method mostSpecificMethod, Object bean) {
		Method invocableMethod = AopUtils.selectInvocableMethod(mostSpecificMethod, bean.getClass());
		MethodJmsListenerEndpoint endpoint = createMethodJmsListenerEndpoint();
		endpoint.setBean(bean);
		endpoint.setMethod(invocableMethod);
		endpoint.setMostSpecificMethod(mostSpecificMethod);
		endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
		endpoint.setEmbeddedValueResolver(this.embeddedValueResolver);
		endpoint.setBeanFactory(this.beanFactory);
		endpoint.setId(getEndpointId(jmsListener));
		endpoint.setDestination(resolve(jmsListener.destination()));
		if (StringUtils.hasText(jmsListener.selector())) {
			endpoint.setSelector(resolve(jmsListener.selector()));
		}
		if (StringUtils.hasText(jmsListener.subscription())) {
			endpoint.setSubscription(resolve(jmsListener.subscription()));
		}
		if (StringUtils.hasText(jmsListener.concurrency())) {
			endpoint.setConcurrency(resolve(jmsListener.concurrency()));
		}

		JmsListenerContainerFactory<?> factory = null;
		String containerFactoryBeanName = resolve(jmsListener.containerFactory());
		if (StringUtils.hasText(containerFactoryBeanName)) {
			Assert.state(this.beanFactory != null, "BeanFactory must be set to obtain container factory by bean name");
			try {
				factory = this.beanFactory.getBean(containerFactoryBeanName, JmsListenerContainerFactory.class);
			}
			catch (NoSuchBeanDefinitionException ex) {
				throw new BeanInitializationException("Could not register JMS listener endpoint on [" +
						mostSpecificMethod + "], no " + JmsListenerContainerFactory.class.getSimpleName() +
						" with id '" + containerFactoryBeanName + "' was found in the application context", ex);
			}
		}

		this.registrar.registerEndpoint(endpoint, factory);
	}
複製程式碼

先設定endpoint的相關屬性,再獲取jmsListenerContainerFactory,最後將endpoint註冊到jmsListenerContainerFactory


本篇到此結束,如果讀完覺得有收穫的話,歡迎點贊、關注、加公眾號【貳級天災】,查閱更多精彩歷史!!!

SpringBoot2.0原始碼分析(二):整合ActiveMQ分析

相關文章