SpringBoot具体整合rabbitMQ可参考:SpringBoot2.0应用(三):SpringBoot2.0整合RabbitMQ

RabbitMQ自动注入

当项目中存在org.springframework.amqp.rabbit.core.RabbitTemplatecom.rabbitmq.client.Channel着两个类时,SpringBoot将RabbitMQ需要使用到的对象注册为Bean,供项目注入使用。一起看一下RabbitAutoConfiguration类。

@Configuration
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration { @Configuration
@ConditionalOnMissingBean(ConnectionFactory.class)
protected static class RabbitConnectionFactoryCreator {
@Bean
public CachingConnectionFactory rabbitConnectionFactory(
RabbitProperties properties,
ObjectProvider<ConnectionNameStrategy> connectionNameStrategy)
throws Exception {
PropertyMapper map = PropertyMapper.get();
CachingConnectionFactory factory = new CachingConnectionFactory(
getRabbitConnectionFactoryBean(properties).getObject());
......
return factory;
}
} @Configuration
@Import(RabbitConnectionFactoryCreator.class)
protected static class RabbitTemplateConfiguration { private final ObjectProvider<MessageConverter> messageConverter; private final RabbitProperties properties; ...... @Bean
@ConditionalOnSingleCandidate(ConnectionFactory.class)
@ConditionalOnMissingBean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
PropertyMapper map = PropertyMapper.get();
RabbitTemplate template = new RabbitTemplate(connectionFactory);
......
return template;
}
......

RabbitAutoConfiguration使和RabbitMQ相关的配置生效,并根据相关属性创建和RabbitMQ的连接,并依赖连接工厂创建rabbitTemplate

RabbitAutoConfiguration同时导入了RabbitAnnotationDrivenConfiguration,注入了rabbitListenerContainerFactory

RabbitMQ的Queue,Exchange和Routing Key关系创建

先看一个例子:

    @Bean
Binding bindingExchangeMessage(Queue queue, TopicExchange exchange) {
// 将队列1绑定到名为topicKey.A的routingKey
return BindingBuilder.bind(queue).to(exchange).with("routingKey");
}

将queue通过"routingKey"绑定到exchange。

RabbitAdmin类的initialize方法会拿出注入的Exchange,Queue和Binding进行声明。

	public void initialize() {
......
Collection<Exchange> contextExchanges = new LinkedList<Exchange>(
this.applicationContext.getBeansOfType(Exchange.class).values());
Collection<Queue> contextQueues = new LinkedList<Queue>(
this.applicationContext.getBeansOfType(Queue.class).values());
Collection<Binding> contextBindings = new LinkedList<Binding>(
this.applicationContext.getBeansOfType(Binding.class).values());
......
final Collection<Exchange> exchanges = filterDeclarables(contextExchanges);
final Collection<Queue> queues = filterDeclarables(contextQueues);
final Collection<Binding> bindings = filterDeclarables(contextBindings);
......
this.rabbitTemplate.execute(channel -> {
declareExchanges(channel, exchanges.toArray(new Exchange[exchanges.size()]));
declareQueues(channel, queues.toArray(new Queue[queues.size()]));
declareBindings(channel, bindings.toArray(new Binding[bindings.size()]));
return null;
});
}

消息发送

以下面的发送为例:

    rabbitTemplate.convertAndSend(MQConst.TOPIC_EXCHANGE, MQConst.TOPIC_KEY1, "from key1");

这个方法会先对消息进行转换,预处理,最终通过调用ChannelN的basicPublish方法提交一个AMQCommand,由AMQCommand完成最终的消息发送。

    public void transmit(AMQChannel channel) throws IOException {
int channelNumber = channel.getChannelNumber();
AMQConnection connection = channel.getConnection(); synchronized (assembler) {
Method m = this.assembler.getMethod();
connection.writeFrame(m.toFrame(channelNumber));
if (m.hasContent()) {
byte[] body = this.assembler.getContentBody();
connection.writeFrame(this.assembler.getContentHeader()
.toFrame(channelNumber, body.length));
int frameMax = connection.getFrameMax();
int bodyPayloadMax = (frameMax == 0) ? body.length : frameMax
- EMPTY_FRAME_SIZE; for (int offset = 0; offset < body.length; offset += bodyPayloadMax) {
int remaining = body.length - offset;
int fragmentLength = (remaining < bodyPayloadMax) ? remaining
: bodyPayloadMax;
Frame frame = Frame.fromBodyFragment(channelNumber, body,
offset, fragmentLength);
connection.writeFrame(frame);
}
}
}
connection.flush();
}

消息接收

先看一个消费的事例:

@Component
public class TopicConsumer { @RabbitListener(queues = MQConst.TOPIC_QUEUENAME1)
@RabbitHandler
public void process1(String message) {
System.out.println("queue:topic.message1,message:" + message);
}
}

SpringBoot解析@RabbitListener@RabbitHandler两个注解的是RabbitListenerAnnotationBeanPostProcessor中的buildMetadata方法:

	private TypeMetadata buildMetadata(Class<?> targetClass) {
Collection<RabbitListener> classLevelListeners = findListenerAnnotations(targetClass);
final boolean hasClassLevelListeners = classLevelListeners.size() > 0;
final List<ListenerMethod> methods = new ArrayList<>();
final List<Method> multiMethods = new ArrayList<>();
ReflectionUtils.doWithMethods(targetClass, method -> {
Collection<RabbitListener> listenerAnnotations = findListenerAnnotations(method);
if (listenerAnnotations.size() > 0) {
methods.add(new ListenerMethod(method,
listenerAnnotations.toArray(new RabbitListener[listenerAnnotations.size()])));
}
if (hasClassLevelListeners) {
RabbitHandler rabbitHandler = AnnotationUtils.findAnnotation(method, RabbitHandler.class);
if (rabbitHandler != null) {
multiMethods.add(method);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
if (methods.isEmpty() && multiMethods.isEmpty()) {
return TypeMetadata.EMPTY;
}
return new TypeMetadata(
methods.toArray(new ListenerMethod[methods.size()]),
multiMethods.toArray(new Method[multiMethods.size()]),
classLevelListeners.toArray(new RabbitListener[classLevelListeners.size()]));
}

@RabbitListener可以放在方法上,也可以放在类上。对于Class级别的Listener,会配合RabbitHandle进行绑定。对于method级别的Listener则不会。

解析完成后,由processListener方法对处理Listener。

	protected void processListener(MethodRabbitListenerEndpoint endpoint, RabbitListener rabbitListener, Object bean,
Object adminTarget, String beanName) {
endpoint.setBean(bean);
endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
endpoint.setId(getEndpointId(rabbitListener));
endpoint.setQueueNames(resolveQueues(rabbitListener));
endpoint.setConcurrency(resolveExpressionAsStringOrInteger(rabbitListener.concurrency(), "concurrency"));
endpoint.setBeanFactory(this.beanFactory);
endpoint.setReturnExceptions(resolveExpressionAsBoolean(rabbitListener.returnExceptions()));
String errorHandlerBeanName = resolveExpressionAsString(rabbitListener.errorHandler(), "errorHandler");
if (StringUtils.hasText(errorHandlerBeanName)) {
endpoint.setErrorHandler(this.beanFactory.getBean(errorHandlerBeanName, RabbitListenerErrorHandler.class));
}
String group = rabbitListener.group();
if (StringUtils.hasText(group)) {
Object resolvedGroup = resolveExpression(group);
if (resolvedGroup instanceof String) {
endpoint.setGroup((String) resolvedGroup);
}
}
String autoStartup = rabbitListener.autoStartup();
if (StringUtils.hasText(autoStartup)) {
endpoint.setAutoStartup(resolveExpressionAsBoolean(autoStartup));
} endpoint.setExclusive(rabbitListener.exclusive());
String priority = resolve(rabbitListener.priority());
if (StringUtils.hasText(priority)) {
try {
endpoint.setPriority(Integer.valueOf(priority));
}
catch (NumberFormatException ex) {
throw new BeanInitializationException("Invalid priority value for " +
rabbitListener + " (must be an integer)", ex);
}
} String rabbitAdmin = resolve(rabbitListener.admin());
if (StringUtils.hasText(rabbitAdmin)) {
Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve RabbitAdmin by bean name");
try {
endpoint.setAdmin(this.beanFactory.getBean(rabbitAdmin, RabbitAdmin.class));
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanInitializationException("Could not register rabbit listener endpoint on [" +
adminTarget + "], no " + RabbitAdmin.class.getSimpleName() + " with id '" +
rabbitAdmin + "' was found in the application context", ex);
}
} RabbitListenerContainerFactory<?> factory = null;
String containerFactoryBeanName = resolve(rabbitListener.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, RabbitListenerContainerFactory.class);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanInitializationException("Could not register rabbit listener endpoint on [" +
adminTarget + "] for bean " + beanName + ", no " + RabbitListenerContainerFactory.class.getSimpleName() + " with id '" +
containerFactoryBeanName + "' was found in the application context", ex);
}
} this.registrar.registerEndpoint(endpoint, factory);
}

先设置endpoint的相关属性,再获取rabbitListenerContainerFactory,根据endpoint和rabbitListenerContainerFactory创建一个messageListenerContainer,并创建endpoint的id到messageListenerContainer的映射。

当收到消息时,SpringBoot会调用绑定好的方法。


本篇到此结束,如果读完觉得有收获的话,欢迎点赞、关注、加公众号【贰级天災】,查阅更多精彩历史!!!

SpringBoot2.0源码分析(三):整合RabbitMQ分析的更多相关文章

  1. SpringBoot2.0源码分析(二):整合ActiveMQ分析

    SpringBoot具体整合ActiveMQ可参考:SpringBoot2.0应用(二):SpringBoot2.0整合ActiveMQ ActiveMQ自动注入 当项目中存在javax.jms.Me ...

  2. SpringBoot2.0源码分析(一):SpringBoot简单分析

    SpringBoot2.0简单介绍:SpringBoot2.0应用(一):SpringBoot2.0简单介绍 本系列将从源码角度谈谈SpringBoot2.0. 先来看一个简单的例子 @SpringB ...

  3. SpringBoot2.0源码分析(四):spring-data-jpa分析

    SpringBoot具体整合rabbitMQ可参考:SpringBoot2.0应用(四):SpringBoot2.0之spring-data-jpa JpaRepositories自动注入 当项目中存 ...

  4. AFNetworking2.0源码解析<三>

    本篇说说安全相关的AFSecurityPolicy模块,AFSecurityPolicy用于验证HTTPS请求的证书,先来看看HTTPS的原理和证书相关的几个问题. HTTPS HTTPS连接建立过程 ...

  5. [Android FrameWork 6.0源码学习] Window窗口类分析

    了解这一章节,需要先了解LayoutInflater这个工具类,我以前分析过:http://www.cnblogs.com/kezhuang/p/6978783.html Window是Activit ...

  6. webpack4.0源码解析之esModule打包分析

    入口文件index.js采用esModule方式导入模块文件,非入口文件index1.js分别采用CommonJS和esmodule规范进行导出. 首先,init之后创建一个简单的webpack基本的 ...

  7. AFNetworking (3.1.0) 源码解析 <三>

    今天要介绍的是Reachability文件夹下的AFNetworkReachabilityManager类.通过字面意思我们就可以知道AFNetworkReachabilityManager是用来监测 ...

  8. [Android FrameWork 6.0源码学习] View的重绘过程之WindowManager的addView方法

    博客首页:http://www.cnblogs.com/kezhuang/p/关于Activity的contentView的构建过程,我在我的博客中已经分析过了,不了解的可以去看一下<[Andr ...

  9. Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)

    Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及P ...

随机推荐

  1. cropper.js 裁剪图片

    https://blog.csdn.net/weixin_38023551/article/details/78792400

  2. spring深入学习(三)-----spring容器内幕

    之前都是说了怎么配置bean以及用法之类的,这篇博文来介绍下spring容器内幕. 内部容器工作机制 Spring中AbstractApplicationContext抽象类的refresh()方法是 ...

  3. fiddler抓包工具总结

    Fiddler 抓包工具总结 Fiddler是一个蛮好用的抓包工具,可以将网络传输发送与接受的数据包进行截获.重发.编辑.转存等操作.也可以用来检测网络安全.反正好处多多,举之不尽呀!当年学习的时候也 ...

  4. Machine learning | 机器学习中的范数正则化

    目录 1. \(l_0\)范数和\(l_1\)范数 2. \(l_2\)范数 3. 核范数(nuclear norm) 参考文献 使用正则化有两大目标: 抑制过拟合: 将先验知识融入学习过程,比如稀疏 ...

  5. 2018-04-10 我的GitHub诞生的日子,欢迎大家吐槽批评

    我的GitHub,诞生的日子,欢迎大家吐槽与批评,嘻嘻 首先是自己想刷一下LeetCode上的代码,其次创建了自己的读书笔记以及面试经验与教训 下边是仓库的Git链接,欢迎大家的批评与修正,谢谢: L ...

  6. 使用rsync实现不同Linux服务器间目录同步

    实现目标:    A 服务器上 /opt/web 目录,与B服务器上 /opt/web目录实现同步.即:B主动与A进行同步.   OS: Reaht AS4   A Server  192.168.1 ...

  7. 2.Spring 拦截器应用

    首先咱们来了解一下具体的业务场景(这个跟第一篇中的很相似但有不同):具体的业务是这样的,现在系统中有六十多个主档(功能模块),每个主档都有新增.修改.删除功能,当我们在对每个主档做这些操作时需要对其记 ...

  8. The test form is only available for requests from the local machine

    使用浏览器测试Web服务时出现提示“The test form is only available for requests from the local machine.”的解决办法 在Web服务项 ...

  9. yield(),wait(),sleep(),join()

    yield(),wait(),sleep(),join()yield()虽然可以让线程由“运行状态”进入到“就绪状态”:但是,它不一定会让其它线程获取CPU执行权(即,其它线程进入到“运行状态”),即 ...

  10. DevOps最佳工具集实践

    在列出DevOps 工具链之前,介绍一下什么是DevOps,虽然DevOps这个概念现在还没有标准的定义,但我们可以追溯一下其过去九年的历史发展过程(从2009年-2017年),列出几个相对明确又有所 ...