RabbitMQ安装配置,封装工具类,发送消息及监听

1. Get-Started

docker安装rabbitmq

  1. 拉取镜像
[root@heima ~]# docker pull rabbitmq:3.8-management
3.8-management: Pulling from library/rabbitmq
7b1a6ab2e44d: Pull complete 
37f453d83d8f: Pull complete 
e64e769bc4fd: Pull complete 
c288a913222f: Pull complete 
13adc5da62c6: Pull complete 
bd67e639afcb: Pull complete 
9a48b5ad2519: Pull complete 
1cdfc59624be: Pull complete 
8f5ad79f0ad6: Pull complete 
Digest: sha256:543f7268600a27a39e2fdd532f8df479636fc0cf528aadde88d5fe718bed71e4
Status: Downloaded newer image for rabbitmq:3.8-management
docker.io/library/rabbitmq:3.8-management
  1. 创建目录
mkdir -p /home/apps/rabbitmq/data
  1. 运行容器
docker run \
-d \
--name rabbitmq \
--restart=always \
--privileged=true \
-p 5672:5672 \
-p 15672:15672 \
-v /home/apps/rabbitmq/data:/var/lib/rabbitmq \
-e RABBITMQ_DEFAULT_VHOST=vhost0 \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin123 \
rabbitmq:3.8-management
  1. 启用web界面管理插件
docker exec -it rabbitmq rabbitmq-plugins enable rabbitmq_management
或
-- 进入容器
docker exec -it rabbitmq /bin/bash
-- 安装插件
rabbitmq-plugins enable rabbitmq_management
  1. 浏览器访问 http://虚拟机ip:15672/出现以下界面说明安装成功。

在这里插入图片描述
输入上面在初始化Rabbitmq容器时我们自己指定了默认账号和密码:admin/admin123,如果没有指定的话那么rabbitmq的默认账号密码是:guest/guest

扩展安装延迟队列

  • 去官网下载插件(v3.8.17)
  • 地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases
# 将下载好的插件复制到mq容器内部
docker cp rabbitmq_delayed_message_exchange-3.8.17.8f537ac.ez rabbitmq:/plugins# 进入mq容器
docker exec -it rabbitmq /bin/bash# 开启插件支持 
rabbitmq-plugins enable rabbitmq_delayed_message_exchange# 查看插件列表
rabbitmq-plugins list

2. 生产者模块

2.1 项目引入依赖:

rabbitmq依赖包,使用RabbitMq这2个依赖就够了。

<dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-amqp</artifactId><scope>provided</scope>
</dependency>
<dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-rabbit</artifactId><scope>provided</scope>
</dependency>

封装工具类用到的包

<!--hutool工具包-->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.17</version>
</dependency>

2.2 工具类

  • RabbitMqHelper
import cn.hutool.core.lang.UUID;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
public class RabbitMqHelper {public static final String REQUEST_ID_HEADER = "requestId";private final RabbitTemplate rabbitTemplate;private final MessagePostProcessor processor = new BasicIdMessageProcessor();private final ThreadPoolTaskExecutor executor;public RabbitMqHelper(RabbitTemplate rabbitTemplate) {this.rabbitTemplate = rabbitTemplate;executor = new ThreadPoolTaskExecutor();//配置核心线程数executor.setCorePoolSize(10);//配置最大线程数executor.setMaxPoolSize(15);//配置队列大小executor.setQueueCapacity(99999);//配置线程池中的线程的名称前缀executor.setThreadNamePrefix("mq-async-send-handler");// 设置拒绝策略:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//执行初始化executor.initialize();}/*** 根据exchange和routingKey发送消息*/public <T> void send(String exchange, String routingKey, T t) {log.debug("准备发送消息,exchange:{}, RoutingKey:{}, message:{}", exchange, routingKey, t);// 1.设置消息标示,用于消息确认,消息发送失败直接抛出异常,交给调用者处理String id = UUID.randomUUID().toString(true);CorrelationData correlationData = new CorrelationData(id);// 2.设置发送超时时间为500毫秒rabbitTemplate.setReplyTimeout(500);// 3.发送消息,同时设置消息idrabbitTemplate.convertAndSend(exchange, routingKey, t, processor, correlationData);}/*** 根据exchange和routingKey发送消息,并且可以设置延迟时间*/public <T> void sendDelayMessage(String exchange, String routingKey, T t, Duration delay) {// 1.设置消息标示,用于消息确认,消息发送失败直接抛出异常,交给调用者处理String id = UUID.randomUUID().toString(true);CorrelationData correlationData = new CorrelationData(id);// 2.设置发送超时时间为500毫秒rabbitTemplate.setReplyTimeout(500);// 3.发送消息,同时设置消息idrabbitTemplate.convertAndSend(exchange, routingKey, t, new DelayedMessageProcessor(delay), correlationData);}/*** 根据exchange和routingKey 异步发送消息,并指定一个延迟时间** @param exchange   交换机* @param routingKey 路由KEY* @param t          数据* @param <T>        数据类型*/public <T> void sendAsync(String exchange, String routingKey, T t, Long time) {String requestId = MDC.get(REQUEST_ID_HEADER);CompletableFuture.runAsync(() -> {try {MDC.put(REQUEST_ID_HEADER, requestId);// 发送延迟消息if (time != null && time > 0) {sendDelayMessage(exchange, routingKey, t, Duration.ofMillis(time));} else {send(exchange, routingKey, t);}} catch (Exception e) {log.error("推送消息异常,t:{},", t, e);}}, executor);}/*** 根据exchange和routingKey 异步发送消息** @param exchange   交换机* @param routingKey 路由KEY* @param t          数据* @param <T>        数据类型*/public <T> void sendAsync(String exchange, String routingKey, T t) {sendAsync(exchange, routingKey, t, null);}
}
  • MqConfig
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.MDC;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.ContainerCustomizer;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;@Configuration
@ConditionalOnClass(value = {MessageConverter.class, AmqpTemplate.class})
public class MqConfig implements EnvironmentAware{public static final String REQUEST_ID_HEADER = "requestId";public static final String ERROR_EXCHANGE = "error.topic";public static final String ERROR_KEY_PREFIX = "error.";public static final String ERROR_QUEUE_TEMPLATE = "error.{}.queue";private String defaultErrorRoutingKey;private String defaultErrorQueue;@Bean(name = "rabbitListenerContainerFactory")@ConditionalOnProperty(prefix = "spring.rabbitmq.listener", name = "type", havingValue = "simple",matchIfMissing = true)SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory,ObjectProvider<ContainerCustomizer<SimpleMessageListenerContainer>> simpleContainerCustomizer) {SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();configurer.configure(factory, connectionFactory);simpleContainerCustomizer.ifUnique(factory::setContainerCustomizer);factory.setAfterReceivePostProcessors(message -> {Object header = message.getMessageProperties().getHeader(REQUEST_ID_HEADER);if(header != null) {MDC.put(REQUEST_ID_HEADER, header.toString());}return message;});return factory;}@Beanpublic MessageConverter messageConverter(ObjectMapper mapper){// 1.定义消息转换器Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(mapper);// 2.配置自动创建消息id,用于识别不同消息jackson2JsonMessageConverter.setCreateMessageIds(true);return jackson2JsonMessageConverter;}/*** <h1>消息处理失败的重试策略</h1>* 本地重试失败后,消息投递到专门的失败交换机和失败消息队列:error.queue*/@Bean@ConditionalOnClass(MessageRecoverer.class)@ConditionalOnMissingBeanpublic MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate){// 消息处理失败后,发送到错误交换机:error.direct,RoutingKey默认是error.微服务名称return new RepublishMessageRecoverer(rabbitTemplate, ERROR_EXCHANGE, defaultErrorRoutingKey);}/*** rabbitmq发送工具**/@Bean@ConditionalOnMissingBean@ConditionalOnClass(RabbitTemplate.class)public RabbitMqHelper rabbitMqHelper(RabbitTemplate rabbitTemplate){return new RabbitMqHelper(rabbitTemplate);}/*** 专门接收处理失败的消息*/@Beanpublic DirectExchange errorMessageExchange(){return new DirectExchange(ERROR_EXCHANGE);}@Beanpublic Queue errorQueue(){return new Queue(defaultErrorQueue, true);}@Beanpublic Binding errorBinding(Queue errorQueue, DirectExchange errorMessageExchange){return BindingBuilder.bind(errorQueue).to(errorMessageExchange).with(defaultErrorRoutingKey);}@Overridepublic void setEnvironment(Environment environment) {String appName = environment.getProperty("spring.application.name");this.defaultErrorRoutingKey = ERROR_KEY_PREFIX + appName;this.defaultErrorQueue = StrUtil.format(ERROR_QUEUE_TEMPLATE, appName);}
}
  • Processor
import cn.hutool.core.lang.UUID;
import org.slf4j.MDC;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;public class BasicIdMessageProcessor implements MessagePostProcessor {public static final String REQUEST_ID_HEADER = "requestId";@Overridepublic Message postProcessMessage(Message message) throws AmqpException {String requestId = MDC.get(REQUEST_ID_HEADER);if (requestId == null) {requestId = UUID.randomUUID().toString(true);}// 写入RequestID标示message.getMessageProperties().setHeader(REQUEST_ID_HEADER, requestId);return message;}
}----------------------------------------------------------------------import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import java.time.Duration;public class DelayedMessageProcessor extends BasicIdMessageProcessor {private final long delay;public DelayedMessageProcessor(Duration delay) {this.delay = delay.toMillis();}@Overridepublic Message postProcessMessage(Message message) throws AmqpException {// 1.添加消息idsuper.postProcessMessage(message);// 2.添加延迟时间message.getMessageProperties().setHeader("x-delay", delay);return message;}
}

2.3 配置

通常mq是放在common模块中,别的模块需要mq时就引入该模块,因此需要把MqConfig放在IOC容器里加载。

  • resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.tianji.common.autoconfigure.mq.MqConfig

配置rabbitmq的地址等信息

  • boostrap.yml
spring:rabbitmq:host: ${mydemo.mq.host:192.168.150.101}port: ${mydemo.mq.port:5672}virtual-host: ${mydemo.mq.vhost:/vhost0}username: ${mydemo.mq.username:admin}password: ${mydemo.mq.password:admin123}listener:simple:retry:enabled: ${mydemo.mq.listener.retry.enable:true} # 开启消费者失败重试initial-interval: ${mydemo.mq.listener.retry.interval:1000ms} # 初始的失败等待时长为1秒multiplier: ${mydemo.mq.listener.retry.multiplier:2} # 失败的等待时长倍数,下次等待时长 = multiplier * last-intervalmax-attempts: ${mydemo.mq.listener.retry.max-attempts:3} # 最大重试次数stateless: ${mydemo.mq.listener.retry.stateless:true} # true无状态;false有状态。如果业务中包含事务,这里改为false

2.4 测试文件

TestController

@RestController
public class TestController {@AutowiredRabbitMqHelper rabbitMqHelper;@GetMapping("/hello")public String hello(){return "hello";}@GetMapping("/sendMsg")public String sendMsg(){rabbitMqHelper.send("order.topic", // "order.topic""order.pay", // "order.pay"UserDto.builder().id(10001).name("gz").age(23).phone("15500000001").email("123@qq.com").build());return "success";}
}

UserDto

import lombok.Builder;
import lombok.Data;@Data
@Builder
public class UserDto {private Integer id;private String name;private String phone;private Integer age;private String email;
}

2.5 生产者测试

生产者模块目录结构
在这里插入图片描述

启动生产者服务,访问http://127.0.0.1:8080/sendMsg
在这里插入图片描述报错了,原因是没有消费者消费消息
但是发现创建了新的错误消息exchange和queue
在这里插入图片描述
因此,接下来创建消费者监听消息。

3. 消费者

3.1 引入通用模块

由于消费者也需要使用刚刚的工具类和UserDto用来接收消息。
在这里插入图片描述
而这些工具类和UserDto都在模块rabbitmq-demo中。因此,在消费者模块中引入该生产者模块(如果这些通用的配置和实体类dto都在一个通用的模块common中,哪些模块需要发送消息,哪些模块需要监听消息,就都引入common模块就行了)

    <groupId>com.gzdemo</groupId><artifactId>rabbitmq-demo</artifactId><version>1.0-SNAPSHOT</version>

因为rabbitmq-demo模块中已经引入了RabbitMq相关依赖,因此消费者模块不需要重复引入RabbitMq相关的依赖。只需要把依赖包添加到classpath下,再import就行了
在这里插入图片描述
在这里插入图片描述

3.2 配置yml

与生产者的配置相同,通常只有消费者才需要配置listener,生产者不需要。
在这里插入图片描述

3.3 编写listener

import com.gzdemo.rabbitmq.pojos.UserDto;import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;@Component
public class UserListener {/*** 监听消息*/@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "learning.lesson.pay.queue", durable = "true"),exchange = @Exchange(name = "order.topic", type = ExchangeTypes.TOPIC),key = "order.pay"))public void listenLessonPay(UserDto userDto){System.out.println(userDto);}
}

3.4 消费者目录结构

这里是随便找了个同工程中的子模块作为消费者测试,在UserListener类中监听消息
在这里插入图片描述

4 测试消息发送、监听

启动这两个服务,浏览器访问http://127.0.0.1:8080/sendMsg,多访问几次后发现消费者接收到了消息
在这里插入图片描述生产者console

2024-06-14 05:46:39.187  INFO 106168 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory       : Attempting to connect to: [192.168.150.101:5672]
2024-06-14 05:46:39.210  INFO 106168 --- [nio-8080-exec-1] o.s.a.r.c.CachingConnectionFactory       : Created new connection: rabbitConnectionFactory#399d82:0/SimpleConnection@1673b17 [delegate=amqp://admin@192.168.150.101:5672/vhost0, localPort= 61317]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/27531.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营day8| 344. 反转字符串,541. 反转字符串II,卡码网:54.替换数字

代码随想录 1、344. 反转字符串【简单】 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1&#xff1a; 输入&…

C# Winform Chart图表使用和详解

Chart控件是微软自带的一种图形可视化组件&#xff0c;能展示种类丰富的图表形式。如曲线图&#xff0c;折线图&#xff0c;饼状图&#xff0c;环形图&#xff0c;柱状图&#xff0c;曲线面积图。 实例代码链接&#xff1a;https://download.csdn.net/download/lvxingzhe3/8943…

【wiki知识库】06.文档管理接口的实现--SpringBoot后端部分

目录 一、&#x1f525;今日目标 二、&#x1f388;SpringBoot部分类的添加 1.调用MybatisGenerator 2.添加DocSaveParam 3.添加DocQueryVo 三、&#x1f686;后端新增接口 3.1添加DocController 3.1.1 /all/{ebokId} 3.1.2 /doc/save 3.1.3 /doc/delete/{idStr} …

机器学习之Transformer模型和大型语言模型(LLMs)

Transformer模型和大型语言模型(LLMs)是现代自然语言处理(NLP)和人工智能(AI)领域的前沿技术。这些模型革新了机器理解和生成人类语言的方式,使得从聊天机器人和自动翻译到复杂的内容生成和情感分析的应用成为可能。 Transformer模型 概述 Transformer模型是现代自然…

vue2 vue3 中指令总结

vue2 vue3 中指令总结 使用方式vue2 指令定义方式钩子的参数指令钩子和组件生命周期的执行顺序是怎样的&#xff1f; vue3 中的指令定义方式hook vs 指令指令钩子函数和组件生命周期的执行顺序 指令和 render 函数指令用在 jsx 上指令和 render 函数一起使用 最佳实践其他问题参…

[Qt] Qt Creator 以及 Qt 在线安装教程

一、Qt Creator 下载及安装 1、从以下镜像源下载安装包常规安装即可 Qt Creator 也可以在第二步Qt 在线安装时一次性勾选安装&#xff0c;见后文 Qt Creator 中科大源下载地址 二、Qt 在线安装 1、根据所在平台选择对应的安装器下载 Qt 在线安装器下载 2、可能的安装报错…

Windows 11部署FunASR离线语音识别系统

Windows 11部署FunASR离线语音识别系统 官网连接 https://github.com/alibaba-damo-academy/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md1-安装Docker 运行Docker Desktop Installer.exe安装Docker 2-Windows添加删除程序增加虚拟机和Linux子系统功能 …

云电脑有多好用?适合哪些人使用?

云电脑作为一种新型的计算模式&#xff0c;其应用场景广泛且多样&#xff0c;适合各类人群使用。云电脑适合什么人群使用&#xff1f;云电脑有哪些应用场景&#xff1f;有什么好的云电脑推荐&#xff1f;以下本文将详细探讨云电脑的主要应用场景及其适用人群的相关内容&#xf…

win10下使用docker和VMware

1. 要使用docker的处理 打开 Hyper-V 管理员身份打开提示符。 输入命令bcdedit /set hypervisorlaunchtype auto 重启电脑 2. 要使用vm虚拟机 关闭 Hyper-V 管理员身份打开提示符。 输入 bcdedit 并回车&#xff0c;找到hypervisorlaunchtype选项发现为auto 输入命令bcdedit /s…

C#使用OpenXml读取Word、PPT、Excel文档内容

DocumentFormat.OpenXml是微软官方推出的一个操作Excel、Word、PPT文件的开源组件&#xff0c;因此它是免费的。以下是一些关于DocumentFormat.OpenXml的详细信息&#xff1a; 开源性质&#xff1a;DocumentFormat.OpenXml是一个开源项目&#xff0c;这意味着其源代码是公开的…

JavaScript内置方法 操作数组和字符串

在JavaScript中&#xff0c;数组和字符串是非常常见的数据类型&#xff0c;它们各自有一套强大的内置方法。以下是一些常用的数组和字符串方法&#xff0c;以及它们的实例和注意事项&#xff1a; 数组操作方法&#xff1a; 1. push() / pop() push()&#xff1a;向数组的末尾…

禁用PS/Photoshop等一系列Adobe旗下软件联网外传用户数据操作

方案一&#xff1a; 下载火绒杀毒&#xff0c;在联网请求上禁用Adobe软件的联网请求&#xff0c;甚至还可以额外发现哪些是它要想要偷偷摸摸干的。 方案二&#xff1a; 最后注意&#xff1a; 用盗版软件只是获得了使用权&#xff01;

Docker系列.Docker Desktop中如何启用Kubernetes

Docker技术概论 Docker Desktop中如何启用Kubernetes - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.…

Linux编辑器 vim使用 (解决普通用户无法进行sudo提权问题)

文章目录 一.vim是什么命令模式底行模式 二.关于vim暂停问题三.注释批量化注释批量化去注释 四.解决普通用户无法进行sudo提权问题五.vim的配置 一.vim是什么 用过VS的都知道&#xff0c;拥有着编辑器编译器调试.编写C&#xff0c;C&#xff0c;python等的功能。就是集成 Linu…

骤旱(Flash drought)相关小结

1. 简介 2. 数据集 3. 识别算法 待更新

1606 - 求一个两位数倒序的结果

问题描述 请输出一个两位的整数 n&#xff0c;倒过来的数&#xff0c;也就是输出这个两位数个位和十位颠倒的结果。 比如&#xff1a;整数 23 倒过来是 32&#xff0c;整数 18 倒过来是 81 &#xff0c;整数 20 倒过来是 2。 输入 两位整数 n。 输出 n 倒过来的整数。 样…

LeetCode | 434.字符串中的单词数

这道题直接使用语言内置的 split 函数可直接分离出字符串中的每个单词&#xff0c;但是要注意区分两种情况&#xff1a;1、空串&#xff1b;2、多个空格连续&#xff0c;分割后会出现空字符的情况&#xff0c;应该舍弃 class Solution(object):def countSegments(self, s):&qu…

java算法:选择排序

文章标题 概述与基本实现优缺点尝试优化 概述与基本实现 选择排序&#xff08;Selection Sort&#xff09;是一种简单直观的排序算法。它的基本思想是每次从待排序的元素中选择最小&#xff08;或最大&#xff09;的元素&#xff0c;放置在已排序的部分的末尾&#xff0c;直到…

JAVA系列---函数式接口

函数式接口的定义 一个函数式接口有且仅有一个抽象方法(SAM&#xff0c;single abstract method)。对于接口来说抽象方法必须重写&#xff0c;默认方法可选重新&#xff0c;静态方法不可重新。Object 类中的 public abstract method 不会被视为单一的抽象方法。这些方法对于函…

java技术专家面试指南100问【java学习+面试宝典】(四)

如何避免“伪共享”&#xff1f; 字节填充&#xff08;创建变量时&#xff0c;使用字段对其进行填充&#xff0c;避免多个变量被分派到同一个缓存行里&#xff09;。JDK8提供了一个Contended注解来解决伪共享。 Netty 的应用场景了解么&#xff1f; Netty 主要用来做网络通信…