spring-kafka通过@KafkaListener实现消费者监听流程分析

2023-05-16

文章目录

  • 主流程
    • 处理EnableKafka注解
    • 实现BeanPostProcessor接口
    • postProcessAfterInitialization扫描@KafkaListener
    • registerListenerContainer注册监听
    • concurrency参数决定线程数
    • 开启ListenerConsumer线程
    • run方法调用kafka的poll方法拉取消息
  • 流程图

主流程

处理EnableKafka注解

首先通过@EnableKafka注解,注入KafkaBootstrapConfiguration类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// @Import是spring提供的注入类的方式
@Import(KafkaBootstrapConfiguration.class)
public @interface EnableKafka {
}

再注入KafkaListenerAnnotationBeanPostProcessor

@Configuration
public class KafkaBootstrapConfiguration {

	@SuppressWarnings("rawtypes")
	@Bean(name = KafkaListenerConfigUtils.KAFKA_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public KafkaListenerAnnotationBeanPostProcessor kafkaListenerAnnotationProcessor() {
		// 注入KafkaListenerAnnotationBeanPostProcessor
		return new KafkaListenerAnnotationBeanPostProcessor();
	}

	@Bean(name = KafkaListenerConfigUtils.KAFKA_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)
	public KafkaListenerEndpointRegistry defaultKafkaListenerEndpointRegistry() {
		return new KafkaListenerEndpointRegistry();
	}

}

实现BeanPostProcessor接口

可以看到KafkaListenerAnnotationBeanPostProcessor实现了BeanPostProcessor,那自然存在postProcessBeforeInitialization和postProcessAfterInitialization两个关键方法

public class KafkaListenerAnnotationBeanPostProcessor<K, V>
		implements BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {
		// 代码省略...
}

postProcessAfterInitialization扫描@KafkaListener

	@Override
	public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
		if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
			Class<?> targetClass = AopUtils.getTargetClass(bean);
			//收集注解方法
			Collection<KafkaListener> classLevelListeners = findListenerAnnotations(targetClass);
			final boolean hasClassLevelListeners = classLevelListeners.size() > 0;
			final List<Method> multiMethods = new ArrayList<Method>();
			Map<Method, Set<KafkaListener>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
					new MethodIntrospector.MetadataLookup<Set<KafkaListener>>() {

						@Override
						public Set<KafkaListener> inspect(Method method) {
							Set<KafkaListener> listenerMethods = findListenerAnnotations(method);
							return (!listenerMethods.isEmpty() ? listenerMethods : null);
						}

					});
			if (hasClassLevelListeners) {
				Set<Method> methodsWithHandler = MethodIntrospector.selectMethods(targetClass,
						new ReflectionUtils.MethodFilter() {

							@Override
							public boolean matches(Method method) {
								return AnnotationUtils.findAnnotation(method, KafkaHandler.class) != null;
							}

						});
				multiMethods.addAll(methodsWithHandler);
			}
			if (annotatedMethods.isEmpty()) {
				this.nonAnnotatedClasses.add(bean.getClass());
				if (this.logger.isTraceEnabled()) {
					this.logger.trace("No @KafkaListener annotations found on bean type: " + bean.getClass());
				}
			}
			else {
				// Non-empty set of methods
				for (Map.Entry<Method, Set<KafkaListener>> entry : annotatedMethods.entrySet()) {
					Method method = entry.getKey();
					for (KafkaListener listener : entry.getValue()) {
						//遍历处理
						processKafkaListener(listener, method, bean, beanName);
					}
				}
				if (this.logger.isDebugEnabled()) {
					this.logger.debug(annotatedMethods.size() + " @KafkaListener methods processed on bean '"
							+ beanName + "': " + annotatedMethods);
				}
			}
			if (hasClassLevelListeners) {
				processMultiMethodListeners(classLevelListeners, multiMethods, bean, beanName);
			}
		}
		return bean;
	}
protected void processKafkaListener(KafkaListener kafkaListener, Method method, Object bean, String beanName) {
		Method methodToUse = checkProxy(method, bean);
		MethodKafkaListenerEndpoint<K, V> endpoint = new MethodKafkaListenerEndpoint<K, V>();
		endpoint.setMethod(methodToUse);
		endpoint.setBeanFactory(this.beanFactory);
		processListener(endpoint, kafkaListener, bean, methodToUse, beanName);
	}
protected void processListener(MethodKafkaListenerEndpoint<?, ?> endpoint, KafkaListener kafkaListener, Object bean,
			Object adminTarget, String beanName) {
		//把kafkaListener中的属性封装成对象
		endpoint.setBean(bean);
		endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
		endpoint.setId(getEndpointId(kafkaListener));
		endpoint.setTopicPartitions(resolveTopicPartitions(kafkaListener));
		endpoint.setTopics(resolveTopics(kafkaListener));
		endpoint.setTopicPattern(resolvePattern(kafkaListener));
		String group = kafkaListener.containerGroup();
		if (StringUtils.hasText(group)) {
			Object resolvedGroup = resolveExpression(group);
			if (resolvedGroup instanceof String) {
				endpoint.setGroup((String) resolvedGroup);
			}
		}

		KafkaListenerContainerFactory<?> factory = null;
		String containerFactoryBeanName = resolve(kafkaListener.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, KafkaListenerContainerFactory.class);
			}
			catch (NoSuchBeanDefinitionException ex) {
				throw new BeanInitializationException("Could not register Kafka listener endpoint on [" + adminTarget
						+ "] for bean " + beanName + ", no " + KafkaListenerContainerFactory.class.getSimpleName()
						+ " with id '" + containerFactoryBeanName + "' was found in the application context", ex);
			}
		}
		//交给KafkaListenerEndpointRegistry处理
		this.registrar.registerEndpoint(endpoint, factory);
	}
public void registerEndpoint(KafkaListenerEndpoint endpoint, KafkaListenerContainerFactory<?> factory) {
		Assert.notNull(endpoint, "Endpoint must be set");
		Assert.hasText(endpoint.getId(), "Endpoint id must be set");
		// Factory may be null, we defer the resolution right before actually creating the container
		KafkaListenerEndpointDescriptor descriptor = new KafkaListenerEndpointDescriptor(endpoint, factory);
		synchronized (this.endpointDescriptors) {
			if (this.startImmediately) { // Register and start immediately
				this.endpointRegistry.registerListenerContainer(descriptor.endpoint,
						resolveContainerFactory(descriptor), true);
			}
			else {
				this.endpointDescriptors.add(descriptor);
			}
		}
	}

registerListenerContainer注册监听

public void registerListenerContainer(KafkaListenerEndpoint endpoint, KafkaListenerContainerFactory<?> factory,
			boolean startImmediately) {
		Assert.notNull(endpoint, "Endpoint must not be null");
		Assert.notNull(factory, "Factory must not be null");

		String id = endpoint.getId();
		Assert.hasText(id, "Endpoint id must not be empty");
		synchronized (this.listenerContainers) {
			Assert.state(!this.listenerContainers.containsKey(id),
					"Another endpoint is already registered with id '" + id + "'");
			//创建ConcurrentMessageListenerContainer
			MessageListenerContainer container = createListenerContainer(endpoint, factory);
			this.listenerContainers.put(id, container);
			if (StringUtils.hasText(endpoint.getGroup()) && this.applicationContext != null) {
				List<MessageListenerContainer> containerGroup;
				if (this.applicationContext.containsBean(endpoint.getGroup())) {
					containerGroup = this.applicationContext.getBean(endpoint.getGroup(), List.class);
				}
				else {
					containerGroup = new ArrayList<MessageListenerContainer>();
					this.applicationContext.getBeanFactory().registerSingleton(endpoint.getGroup(), containerGroup);
				}
				containerGroup.add(container);
			}
			if (startImmediately) {
				//开启监听入口
				startIfNecessary(container);
			}
		}
	}
private void startIfNecessary(MessageListenerContainer listenerContainer) {
	if (this.contextRefreshed || listenerContainer.isAutoStartup()) {
		listenerContainer.start();
	}
}
@Override
public final void start() {
	synchronized (this.lifecycleMonitor) {
		Assert.isTrue(
				this.containerProperties.getMessageListener() instanceof KafkaDataListener,
				"A " + KafkaDataListener.class.getName() + " implementation must be provided");
		doStart();
	}
}

concurrency参数决定线程数

@Override
protected void doStart() {
	if (!isRunning()) {
		ContainerProperties containerProperties = getContainerProperties();
		TopicPartitionInitialOffset[] topicPartitions = containerProperties.getTopicPartitions();
		if (topicPartitions != null
				&& this.concurrency > topicPartitions.length) {
			this.logger.warn("When specific partitions are provided, the concurrency must be less than or "
					+ "equal to the number of partitions; reduced from " + this.concurrency + " to "
					+ topicPartitions.length);
			this.concurrency = topicPartitions.length;
		}
		setRunning(true);
		
		//根据主题中的分区数和concurrency数计算,决定创建多少KafkaMessageListenerContainer,也就是开启多少线程
		for (int i = 0; i < this.concurrency; i++) {
			KafkaMessageListenerContainer<K, V> container;
			if (topicPartitions == null) {
				container = new KafkaMessageListenerContainer<>(this.consumerFactory, containerProperties);
			}
			else {
				container = new KafkaMessageListenerContainer<>(this.consumerFactory, containerProperties,
						partitionSubset(containerProperties, i));
			}
			if (getBeanName() != null) {
				container.setBeanName(getBeanName() + "-" + i);
			}
			if (getApplicationEventPublisher() != null) {
				container.setApplicationEventPublisher(getApplicationEventPublisher());
			}
			container.setClientIdSuffix("-" + i);
			//KafkaMessageListenerContainer的doStart()
			container.start();
			this.containers.add(container);
		}
	}
}

开启ListenerConsumer线程

@Override
protected void doStart() {
	if (isRunning()) {
		return;
	}
	ContainerProperties containerProperties = getContainerProperties();

	if (!this.consumerFactory.isAutoCommit()) {
		AckMode ackMode = containerProperties.getAckMode();
		if (ackMode.equals(AckMode.COUNT) || ackMode.equals(AckMode.COUNT_TIME)) {
			Assert.state(containerProperties.getAckCount() > 0, "'ackCount' must be > 0");
		}
		if ((ackMode.equals(AckMode.TIME) || ackMode.equals(AckMode.COUNT_TIME))
				&& containerProperties.getAckTime() == 0) {
			containerProperties.setAckTime(5000);
		}
	}

	Object messageListener = containerProperties.getMessageListener();
	Assert.state(messageListener != null, "A MessageListener is required");
	if (messageListener instanceof GenericAcknowledgingMessageListener) {
		this.acknowledgingMessageListener = (GenericAcknowledgingMessageListener<?>) messageListener;
	}
	else if (messageListener instanceof GenericMessageListener) {
		this.listener = (GenericMessageListener<?>) messageListener;
	}
	else {
		throw new IllegalStateException("messageListener must be 'MessageListener' "
				+ "or 'AcknowledgingMessageListener', not " + messageListener.getClass().getName());
	}
	if (containerProperties.getConsumerTaskExecutor() == null) {
		SimpleAsyncTaskExecutor consumerExecutor = new SimpleAsyncTaskExecutor(
				(getBeanName() == null ? "" : getBeanName()) + "-C-");
		containerProperties.setConsumerTaskExecutor(consumerExecutor);
	}
	if (containerProperties.getListenerTaskExecutor() == null) {
		SimpleAsyncTaskExecutor listenerExecutor = new SimpleAsyncTaskExecutor(
				(getBeanName() == null ? "" : getBeanName()) + "-L-");
		containerProperties.setListenerTaskExecutor(listenerExecutor);
	}
	//创建ListenerConsumer
	this.listenerConsumer = new ListenerConsumer(this.listener, this.acknowledgingMessageListener);
	setRunning(true);
	//开启ListenerConsumer线程
	this.listenerConsumerFuture = containerProperties
			.getConsumerTaskExecutor()
			.submitListenable(this.listenerConsumer);
}

run方法调用kafka的poll方法拉取消息

@Override
public void run() {
	if (this.theListener instanceof ConsumerSeekAware) {
		((ConsumerSeekAware) this.theListener).registerSeekCallback(this);
	}
	this.count = 0;
	this.last = System.currentTimeMillis();
	if (isRunning() && this.definedPartitions != null) {
		initPartitionsIfNeeded();
		// we start the invoker here as there will be no rebalance calls to
		// trigger it, but only if the container is not set to autocommit
		// otherwise we will process records on a separate thread
		if (!this.autoCommit) {
			startInvoker();
		}
	}
	long lastReceive = System.currentTimeMillis();
	long lastAlertAt = lastReceive;
	while (isRunning()) {
		try {
			if (!this.autoCommit) {
				processCommits();
			}
			processSeeks();
			if (this.logger.isTraceEnabled()) {
				this.logger.trace("Polling (paused=" + this.paused + ")...");
			}
			//拉取消息
			ConsumerRecords<K, V> records = this.consumer.poll(this.containerProperties.getPollTimeout());
			if (records != null && this.logger.isDebugEnabled()) {
				this.logger.debug("Received: " + records.count() + " records");
			}
			if (records != null && records.count() > 0) {
				if (this.containerProperties.getIdleEventInterval() != null) {
					lastReceive = System.currentTimeMillis();
				}
				// if the container is set to auto-commit, then execute in the
				// same thread
				// otherwise send to the buffering queue
				if (this.autoCommit) {
					//自动提交直接反射调用@kafkaListener注解的方法
					invokeListener(records);
				}
				else {
					//非自动提交把消息放入队列中
					if (sendToListener(records)) {
						if (this.assignedPartitions != null) {
							// avoid group management rebalance due to a slow
							// consumer
							this.consumer.pause(this.assignedPartitions);
							this.paused = true;
							this.unsent = records;
						}
					}
				}
			}
			else {
				if (this.containerProperties.getIdleEventInterval() != null) {
					long now = System.currentTimeMillis();
					if (now > lastReceive + this.containerProperties.getIdleEventInterval()
							&& now > lastAlertAt + this.containerProperties.getIdleEventInterval()) {
						publishIdleContainerEvent(now - lastReceive);
						lastAlertAt = now;
						if (this.theListener instanceof ConsumerSeekAware) {
							seekPartitions(getAssignedPartitions(), true);
						}
					}
				}
			}
			this.unsent = checkPause(this.unsent);
		}
		catch (WakeupException e) {
			this.unsent = checkPause(this.unsent);
		}
		catch (Exception e) {
			if (this.containerProperties.getGenericErrorHandler() != null) {
				this.containerProperties.getGenericErrorHandler().handle(e, null);
			}
			else {
				this.logger.error("Container exception", e);
			}
		}
	}
	if (this.listenerInvokerFuture != null) {
		stopInvoker();
		commitManualAcks();
	}
	try {
		this.consumer.unsubscribe();
	}
	catch (WakeupException e) {
		// No-op. Continue process
	}
	this.consumer.close();
	if (this.logger.isInfoEnabled()) {
		this.logger.info("Consumer stopped");
	}
}

源码内容比较多,只截图主流程的业务,有兴趣可以了解过主流程后再细看。

流程图

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spring-kafka通过@KafkaListener实现消费者监听流程分析 的相关文章

  • 离散点间曲率计算

    本文转自知乎计算离散点的曲率 xff08 附Python MATLAB代码 xff09 在很多学科中的很多计算任务中都需要用到曲线的曲率 xff08 或者曲率半径 xff09 xff0c numpy库里和matlab build in里都没
  • 关于Frenet坐标系内曲率约束

    本文摘自于apollo直播公开课 xff0c 因为车辆存在最小的转弯半径 xff0c 所以我们要对车辆运动学进行限制 由于转弯半径是基于笛卡尔坐标系的 xff0c 需要基于Frenet坐标系进行转换 假设 xff1a xff0c 车辆朝向与
  • caffe内CHECK_EQ等函数意义解释

    个人在学习caffe源码文件时遇到了CHECK EQ函数 xff0c 不理解什么含义 xff0c 经过上下文理解 xff0c 明白了其中含义 CHECK EQ x y lt lt 34 x 61 y 34 xff0c EQ即equation
  • 电路交换方式与分组交换方式计算题

    已知网络速率为1Mb S xff0c 对于每个用户来说 xff0c 有10 的时间是活跃的 xff0c 活跃时网速为100kb s 对于电路交换方式来说 xff0c 最多只能支持10位用户接入网络 由于1Mb 61 1000kb xff0c
  • Carla编译make launch过程中出现UE4_ROOT is not defined

    在编译carla过程中出现如下情况 xff1a BuildCarlaUE4 sh ERROR UE4 ROOT is not defined or points to a non existant directory please set
  • request 模块可以帮助我们发起http请求

    request 模块可以帮助我们发起http请求 步骤 xff1a 1 首先import 下 request 模块 2 然后看请求的方式 xff0c 选择对应的请求方法 3 接受返回的报文信息 有requests get requests
  • 自动提取论文公式方法

    需要的软件 MathType 7 Mathpix Snipping Tool 软件获取链接 xff1a 链接 xff1a https pan baidu com s 1Fz VGkgZJbZhlocL4y1AoA 提取码 xff1a ci0
  • 现代 CMake 简明教程--CMake 基础

    前言 用 CMake 来构建 C C 43 43 项目是业内的主流做法 最近 xff0c 我们的项目代码做了一些拆分和合并 xff1a 引入其他仓库代码 xff0c 并且将公共部分拆分以供多个仓库同时使用 为此 xff0c 就得修改项目中的
  • USB接线定义和链接摄像头

    原文链接 xff1a https www cnblogs com chinalantian articles 2131361 html 写本文的意义在于了解USB的接线定义和实现使用手机数据线读取摄像头图像 USB接口定义 颜色 一般的排列
  • C++小结 析构函数、函数后面接冒号 等等

    讲在前面 本小结有析构函数 C 43 43 函数后面接 xff1a 的含义 C 43 43 中public protected及private用法 条件运算符 fabs 和abs 区别 C 43 43 中的结构体内的函数 类中成员函数声明后
  • C++学习笔记:子类构造函数中冒号的使用 — 同时创建父类和子类对象

    C 43 43 中 xff0c 子类对象创建需要预先创建父类对象 xff0c 对象销毁顺序与此相反 假如父类构造函数只存在有参构造 xff0c 在子类对象实例化之前 xff0c 便需要创建一个父类对象 xff0c 在不存在默认无参构造情况下
  • C语言头文件详解

    1 include的作用 简单一句话 xff1a 在include的地方 xff0c 把头文件里的内容原封不动的复制到引用该头文件的地方 2 头文件的引用 头文件引用有两种形式 xff1a include lt stdio h gt 和 i
  • 字符数组进行复制需要加结束符‘\0’

    如想将str1数组内容复制到str2中 xff08 不用strcpy xff0c 如果按照以下格式复制 xff09 xff0c 需要加字符结束符 0 xff1b span class token macro property span cl
  • 学习Kafka

    1 Kafka是什么 xff1f 学习Kafka的目的 xff0c 为了解决高吞吐量项目的需求 xff0c Kafka号称大数据的杀手锏 xff0c 这款为大数据而生的消息中间件 xff0c 以其百亿级tps的吞吐量名声大噪 xff0c 迅
  • cmake命令之option使用案例

    option的命令形式如下 option lt variable gt 34 lt help text gt 34 value option简介 cmake中option起到编译开关的作用 xff0c CMakeLists txt中opti
  • docker学习

    现代软件开发的一大目的就是隔离 xff0c 应用程序在运行时相互独立互不干扰 xff0c 这种隔离实现起来是很不容易的 xff0c 其中一种解决方案就是上面提到的虚拟机技术 xff0c 通过将应用程序部署在不同的虚拟机中从而实现隔离 但是虚
  • 3.编写CMakeLists文件

    本章将介绍为您的软件编写有效的 CMakeLists 文件的基础知识 它将涵盖处理大多数项目所需的基本命令和问题 虽然 CMake 可以处理极其复杂的项目 xff0c 但对于大多数项目 xff0c 你会发现本章的内容会告诉你所有你需要知道的
  • boost库简介

    欢迎来到boost org Boost 提供免费的经过同行评审的可移植 C 43 43 源库 我们强调与 C 43 43 标准库配合良好的库 Boost 库旨在广泛使用 xff0c 并可用于广泛的应用程序 Boost 许可证鼓励所有用户以最
  • 解决mavros源码安装过程中wstool update -t src -j4报错(网络限制)问题

    继上一篇解决了mavlink安装的网络问题后 xff0c 没想到这个指令更新也需要链接到github 而直接执行时 xff0c 报错 xff1a span class token punctuation span mavlink span
  • QT事件详解

    一 简介 在Qt中 xff0c 事件作为一个对象 xff0c 继承自 QEvent 类 xff0c 常见的有键盘事件 QKeyEvent 鼠标事件 QMouseEvent 和定时器事件 QTimerEvent 等 xff0c 与 QEven

随机推荐

  • QT事件系统之二:鼠标事件和滚轮事件

    一 QMouseEvent的详细描述 QMouseEvent 类用来表示一个鼠标事件 xff0c 当在窗口部件中按下鼠标 释放鼠标和移动鼠标指针时 xff0c 都会产生鼠标事件 QMouseEvent 利用 QMouseEvent 类可以获
  • Qt事件系统之三:键盘事件

    QKeyEvent类用来描述一个键盘事件 当键盘按键被按下或者被释放时 xff0c 键盘事件便会被发送给拥有键盘输人焦点的部件 QKeyEvent的key 函数可以获取具体的按键 xff0c 对于Qt中给定的所有按键 xff0c 可以在帮助
  • 第一个自己写的程序

    22年8月份花了三周时间快速过了一遍某站一位大佬的视频 xff0c 前几天刷朋友圈时偶然看见一位道友写了个矩阵乘积的计算器 xff0c 瞬间给了我灵感 xff0c 直接开始操作 理想很丰满 xff0c 现实很扯淡 xff0c 刚开始写了个4
  • CEF学习质料

    目录 一 编译CEF3里的lib xff1a 1 下载CEF3 http opensource spotify com cefbuilds index html 2 下载CMake xff0c 运行CMake GUI exe 3 CMake
  • 【无标题】

    5 1 内存模型基础 这里从两方面来讲内存模型 xff1a 一方面是基本结构 xff0c 这与事务在内存中是怎样布局的有关 xff1b 另一方面就是并发 对于并发基本结构很重要 xff0c 特别是在低层原子操作 所以我将会从基本结构讲起 C
  • C++`中的原子操作和原子类型

    5 2 C 43 43 中的原子操作和原子类型 原子操作 是个不可分割的操作 在系统的所有线程中 xff0c 你是不可能观察到原子操作完成了一半这种情况的 xff1b 它要么就是做了 xff0c 要么就是没做 xff0c 只有这两种可能 如
  • 编写一个使用锁的线程安全查询表

    6 3 基于锁设计更加复杂的数据结构 栈和队列都很简单 xff1a 接口相对固定 xff0c 并且它们应用于比较特殊的情况 并不是所有数据结构都像它们一样简单 xff1b 大多数数据结构支持更加多样化的操作 原则上 xff0c 这将增大并行
  • __declspec(dllexport)和__declspec(dllimport)以及QT中public: static struct QMetaObject const xxx:staticMe

    假设你的头文件如下 xff1a span class token macro property span class token directive hash span span class token directive keyword
  • /MD 与 /MT、/MTD与/MDD的区别

    VS在 属性页的 C C 43 43 gt Code Generation gt Runtime Library 一项中总共有四个选项 MD 与 MT MTD与 MDD xff0c 它们分别有什么区别 xff1f 1 MD 与 MT 用于R
  • C++多线程案列

    C 43 43 多线程案列 话不多说 xff0c 直接上代码 xff1a span class token comment CMakeList txt ThreadDemo1 的 CMake 项目 xff0c 在此处包括源代码并定义 spa
  • 右值引用、移动语义、完美转发

    右值引用 移动语义 完美转发 左值 右值 xff1a 在c 43 43 中 xff0c 所有的值不是左值 xff0c 就是右值 有名字的对象都是左值 xff0c 右值没有名字 还有一个可以区分左值和右值的方法 xff1a 看能不能对表达式取
  • Deep Meta Learning for Real-Time Target-Aware Visual Tracking 论文阅读

    这篇文章是韩国的一个组做的 一直没中 直到19年中了ICCV xff0c 据说是第一篇将元学习引入目标跟踪的文章 xff0c 用的架构是siamese网络的架构 xff0c 但是在模型在线更新的时候使用了meta learning的思想 M
  • 单链表倒序

    单链表倒序 题目来源 牛客网 题目描述 输入一个链表 xff0c 按链表从尾到头的顺序返回一个ArrayList span class token keyword public span span class token keyword c
  • VS2019中编写C语言

    建立C 43 43 控制台程序 xff0c 将main函数所在的文件后缀名从cpp改成c xff0c 然后复制下列模板即可 xff1b 模板如下 xff1a dsa zju cpp 此文件包含 34 main 34 函数 程序执行将在此处开
  • CMake编译工程/第一个CMakeLists.txt(最详细案例演示)

    目录 在 linux 平台下使用 CMake 构建C C 43 43 工程的流程 当前项目目录结构 最小CMake工程 进入文件夹5 3 1 xff0c VScode打开项目文件5 3 1 在项目5 3 1顶层目录中 xff0c New F
  • 数据分析岗-机器学习相关知识

    1 解释共线性 我们进行回归分析需要了解每个自变量对因变量的单纯效应 xff0c 多重共线性就是说自变量间存在某种函数关系 xff0c 如果你的两个自变量间 xff08 X1和X2 xff09 存在函数关系 xff0c 那么X1改变一个单位
  • make,Makefile简易教程

    一 概述 make是一个类UNIX系统下的编译命令 xff0c 也可以理解为一个项目管理工具 xff0c 通过make可以按照自己指定的编译命令编译整个项目 xff0c 相当于将在命令行的编译命令按序执行 xff0c 省去了反复键入编译命令
  • @Transactional注解事务失效的七种原因分析

    64 Transactional是一种基于注解管理事务的方式 xff0c spring通过动态代理的方式为目标方法实现事务管理的增强 64 Transactional使用起来方便 xff0c 但也需要注意引起 64 Transactiona
  • C头文件相互包含

    今天遇见一个很头疼的事 xff0c 就是1 h头文件包含2 h xff0c 但是1 h里面却找不到2 h定义的一个结构体变量 最后排查发现是2 h里面又包含了1 h导致的 C语言中头文件包含的处理原则 之前一直以为 xff0c 一个 c文件
  • spring-kafka通过@KafkaListener实现消费者监听流程分析

    文章目录 主流程处理EnableKafka注解实现BeanPostProcessor接口postProcessAfterInitialization扫描 64 KafkaListenerregisterListenerContainer注册