消息中间件——RabbitMQ(九)RabbitMQ整合Spring AMQP实战!(全)

}

});

}

4.1.3 查看管控台

运行前,可以看到queue001中是没有消息的。

执行前Queue001

运行testSendMessage()方法。并获取消息。

执行后Queue001

获取消息

4.1.4 简单写法

@Test

public void testSendMessage2() throws Exception {

//1 创建消息

MessageProperties messageProperties = new MessageProperties();

messageProperties.setContentType(“text/plain”);

Message message = new Message(“mq 消息1234”.getBytes(), messageProperties);

rabbitTemplate.send(“topic001”, “spring.abc”, message);

rabbitTemplate.convertAndSend(“topic001”, “spring.amqp”, “hello object message send!”);

rabbitTemplate.convertAndSend(“topic002”, “rabbit.abc”, “hello object message send!”);

}

我们往topic001中发送了两条消息,topic002中发送了一条消息。运行testSendMessage2() 接下来再查看下管控台

查看管控台

获取消息2

获取消息3

可以看到topic001中已经有了三条消息,刚才发送的消息也还在。GetMessage并不是消费消息,而只是获取消息。

5. SimpleMessageListenerContainer


简单消息监听容器

  • 这个类非常的强大,我们可以对它进行很多设置,对于消费者的配置项,这个类都可以满足

  • 监听队列(多个队列)、自动启动、自动声明功能

  • 设置事务特性、事务管理器、事务属性、事务容器(并发)、是否开启事务、回滚消息等

  • 设置消费者数量、最小最大数量、批量消费

  • 设置消息确认和自动确认模式、是否重回队列、异常捕捉handler函数

  • 设置消费者标签生成策略、是否独占模式、消费者属性等

  • 设置具体的监听器、消息转换器等等。

注意:

  • SimpleMessageListenerContainer可以进行动态设置,比如在运行中的应用可以动态的修改其消费者数量的大小、接收消息的模式等

  • 很多机遇RabbitMQ的自制定话后端管控台在进行动态设置的时候,也是根据这一特性去实现的。所以可以看出SpringAMQP非常的强大

思考

SimpleMessageListenerContainer为什么可以动态感知配置变更?

5.1 代码演示

5.1.1 RabbitMQConfig类

配置中添加如下代码:

@Bean

public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {

SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);

//添加多个队列进行监听

container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());

//当前消费者数量

container.setConcurrentConsumers(1);

//最大消费者数量

container.setMaxConcurrentConsumers(5);

//设置重回队列,一般设置false

container.setDefaultRequeueRejected(false);

//设置自动签收机制

container.setAcknowledgeMode(AcknowledgeMode.AUTO);

//设置listener外露

container.setExposeListenerChannel(true);

//消费端标签生成策略

container.setConsumerTagStrategy(new ConsumerTagStrategy() {

@Override

public String createConsumerTag(String queue) {

//每个消费端都有自己独立的标签

return queue + “_” + UUID.randomUUID().toString();

}

});

//消息监听

container.setMessageListener(new ChannelAwareMessageListener() {

@Override

public void onMessage(Message message, Channel channel) throws Exception {

String msg = new String(message.getBody());

System.err.println("----------消费者: " + msg);

}

});

return container;

}

运行之前写的testSendMessage2()方法,查看管控台中的相关信息以及控制台打印信息

查看Channel1

查看Channel2

打印信息

6. MessageListenerAdapter


MessageListenerAdapter 即消息监听适配器

6.1 代码演示

6.1.1 适配器使用方式1

我们把之前的消息监听代码注释,可以不用直接加消息监听,而是采用MessageListenerAdapter的方式,通过适配器方式1,我们来学习下如何使用默认的handleMessage,自定义方法名,自定义转换器。

使用默认handleMessage

//消息监听

/*container.setMessageListener(new ChannelAwareMessageListener() {

@Override

public void onMessage(Message message, Channel channel) throws Exception {

String msg = new String(message.getBody());

System.err.println("----------消费者: " + msg);

}

});*/

MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());

container.setMessageListener(adapter);

MessageListenerAdapter 适配器类,熟悉适配器模式的朋友肯定了解适配器模式的话,可以通过适配器,适配自己的实现,这里我们适配自定义的MessageDelegate类。我们就可以不采用监听的方式,采用适配的方式。

自定义MessageDelegate

public class MessageDelegate {

public void handleMessage(byte[] messageBody) {

System.err.println(“默认方法, 消息内容:” + new String(messageBody));

}

}

MessageDelegate类中,方法名与参数handleMessage(byte[] messageBody)是固定的。为什么呢?

MessageListenerAdapter源码分析

我们来看下MessageListenerAdapter底层代码

MessageListenerAdapter

MessageListenerAdapter类中

public static final String ORIGINAL_DEFAULT_LISTENER_METHOD = “handleMessage”;

默认方法名就是叫handleMessage。当然也可以自己去指定设置。通过messageListenerAdapter的代码我们可以看出如下核心属性

  • defaultListenerMethod默认监听方法名称:用于设置监听方法名称

  • Delegate 委托对象:实际真实的委托对象,用于处理消息

  • queueOrTagToMethodName 队列标识与方法名称组成集合

  • 可以一一进行队列与方法名称的匹配

  • 队列和方法名称绑定,即指定队列里的消息会被绑定的方法所接受处理

测试一下默认使用的handleMessage方法。启动ApplicationTests类,运行testSendMessage()测试方法。

打印消息

自定义方法名

MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());

adapter.setDefaultListenerMethod(“consumeMessage”);

container.setMessageListener(adapter);

修改MessageDelegate()类

public class MessageDelegate {

public void consumeMessage(byte[] messageBody) {

System.err.println(“字节数组方法, 消息内容:” + new String(messageBody));

}

}

自定义TextMessageConverter转换器

public class TextMessageConverter implements MessageConverter {

@Override

public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {

return new Message(object.toString().getBytes(), messageProperties);

}

@Override

public Object fromMessage(Message message) throws MessageConversionException {

String contentType = message.getMessageProperties().getContentType();

if(null != contentType && contentType.contains(“text”)) {

return new String(message.getBody());

}

return message.getBody();

}

}

修改RabbitMQConfig类

MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());

adapter.setDefaultListenerMethod(“consumeMessage”);

adapter.setMessageConverter(new TextMessageConverter());

container.setMessageListener(adapter);

修改MessageDelegate类

public class MessageDelegate {

public void consumeMessage(String messageBody) {

System.err.println(“字符串方法, 消息内容:” + messageBody);

}

}

运行testSendMessage4Text()测试方法

@Test

public void testSendMessage2() throws Exception {

//1 创建消息

MessageProperties messageProperties = new MessageProperties();

messageProperties.setContentType(“text/plain”);

Message message = new Message(“mq 消息1234”.getBytes(), messageProperties);

rabbitTemplate.send(“topic001”, “spring.abc”, message);

rabbitTemplate.convertAndSend(“topic001”, “spring.amqp”, “hello object message send!”);

rabbitTemplate.convertAndSend(“topic002”, “rabbit.abc”, “hello object message send!”);

}

注意:在发消息的时候,必须符合自己的转换器。

打印结果

打印结果

6.1.2 适配器使用方式2

自定义队列名称和方法名称。

/**

  • 2 适配器方式: 我们的队列名称 和 方法名称 也可以进行一一的匹配

  • /

MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());

adapter.setMessageConverter(new TextMessageConverter());

Map<String, String> queueOrTagToMethodName = new HashMap<>();

queueOrTagToMethodName.put(“queue001”, “method1”);

queueOrTagToMethodName.put(“queue002”, “method2”);

adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);

container.setMessageListener(adapter);

public class MessageDelegate {

public void method1(String messageBody) {

System.err.println(“method1 收到消息内容:” + new String(messageBody));

}

public void method2(String messageBody) {

System.err.println(“method2 收到消息内容:” + new String(messageBody));

}

}

运行 测试方法

@Test

public void testSendMessage4Text() throws Exception {

//1 创建消息

MessageProperties messageProperties = new MessageProperties();

messageProperties.setContentType(“text/plain”);

Message message = new Message(“mq 消息1234”.getBytes(), messageProperties);

rabbitTemplate.send(“topic001”, “spring.abc”, message);

rabbitTemplate.send(“topic002”, “rabbit.abc”, message);

}

运行结果:

打印结果

7. MessageConverter消息转换器


我们在进行发送消息的时候,正常情况下消息体为二进制的数据方式进行传输,如果希望内部帮我们进行转换,或者指定自定义的转换器,就需要用到MessageConverter

  • 自定义常用转换器:MessageConverter,一般来讲都需要实现这个接口

  • 重写下面两个方法:

toMessage:java对象转换为Message

fromMessage:Message对象转换为java对象

  • Json转换器:Jackson2JsonMessageConverter:可以进行Java对象的转换功能

  • DefaultJackson2JavaTypeMapper映射器:可以进行java对象的映射关系

  • 自定义二进制转换器:比如图片类型、PDF、PPT、流媒体

7.1 代码演示

其实我们在介绍MessageListenerAdapter的时候,中间就介绍到了TextMessageConverter转换器,将二进制数据转换成字符串数据。

7.1.1 添加json格式的转换器

修改RabbitMQConfig类

// 1.1 支持json格式的转换器

MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());

adapter.setDefaultListenerMethod(“consumeMessage”);

//重点,加入json格式的转换器 json对应Map对象

Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();

adapter.setMessageConverter(jackson2JsonMessageConverter);

container.setMessageListener(adapter);

修改MessageDelegate

public class MessageDelegate {

//json对应Map对象

public void consumeMessage(Map messageBody) {

System.err.println(“map方法, 消息内容:” + messageBody);

}

}

定义一个Order对象

public class Order {

private String id;

private String name;

private String content;

…省略get/set等方法

}

定义测试方法

@Test

public void testSendJsonMessage() throws Exception {

Order order = new Order();

order.setId(“001”);

order.setName(“消息订单”);

order.setContent(“描述信息”);

ObjectMapper mapper = new ObjectMapper();

String json = mapper.writeValueAsString(order);

System.err.println("order 4 json: " + json);

MessageProperties messageProperties = new MessageProperties();

//这里注意一定要修改contentType为 application/json

messageProperties.setContentType(“application/json”);

Message message = new Message(json.getBytes(), messageProperties);

rabbitTemplate.send(“topic001”, “spring.order”, message);

}

打印结果:

打印结果

7.1.2 添加支持Java对象转换

修改RabbitMQConfig类

MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());

adapter.setDefaultListenerMethod(“consumeMessage”);

Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();

//需要将javaTypeMapper放入到Jackson2JsonMessageConverter对象中

DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();

jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);

adapter.setMessageConverter(jackson2JsonMessageConverter);

container.setMessageListener(adapter);

修改MessageDelegate

public class MessageDelegate {

public void consumeMessage(Order order) {

System.err.println("order对象, 消息内容, id: " + order.getId() +

", name: " + order.getName() +

", content: "+ order.getContent());

}

}

定义测试方法

@Test

public void testSendJavaMessage() throws Exception {

Order order = new Order();

order.setId(“001”);

order.setName(“订单消息”);

order.setContent(“订单描述信息”);

ObjectMapper mapper = new ObjectMapper();

String json = mapper.writeValueAsString(order);

System.err.println("order 4 json: " + json);

MessageProperties messageProperties = new MessageProperties();

//这里注意一定要修改contentType为 application/json

messageProperties.setContentType(“application/json”);

//添加typeid 与类的全路径

messageProperties.getHeaders().put(“TypeId”, “com.cp.spring.entity.Order”);

Message message = new Message(json.getBytes(), messageProperties);

rabbitTemplate.send(“topic001”, “spring.order”, message);

}

打印结果:

打印结果

7.1.3 添加支持java对象多映射转换

修改RabbitMQConfig类

//1.3 DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter 支持java对象多映射转换

MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());

adapter.setDefaultListenerMethod(“consumeMessage”);

Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();

DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();

//key表示标签 对应一个类的具体全路径。类和标签绑定之后,标签是order,意思就是转换成order类

Map<String, Class<?>> idClassMapping = new HashMap

idClassMapping.put(“order”, com.cp.spring.entity.Order.class);

idClassMapping.put(“packaged”, com.cp.spring.entity.Packaged.class);

javaTypeMapper.setIdClassMapping(idClassMapping);

//一层套一层

jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);

adapter.setMessageConverter(jackson2JsonMessageConverter);

container.setMessageListener(adapter);

修改MessageDelegate

public class MessageDelegate {

//json对应Map对象

public void consumeMessage(Order order) {

System.err.println("order对象, 消息内容, id: " + order.getId() +

", name: " + order.getName() +

", content: "+ order.getContent());

}

public void consumeMessage(Packaged pack) {

System.err.println("package对象, 消息内容, id: " + pack.getId() +

", name: " + pack.getName() +

", content: "+ pack.getDescription());

}

}

定义一个Packaged对象

public class Packaged {

private String id;

private String name;

private String description;

…省略get/set等方法

}

定义测试方法

@Test

public void testSendMappingMessage() throws Exception {

ObjectMapper mapper = new ObjectMapper();

Order order = new Order();

order.setId(“001”);

order.setName(“订单消息”);

order.setContent(“订单描述信息”);

String json1 = mapper.writeValueAsString(order);

System.err.println("order 4 json: " + json1);

MessageProperties messageProperties1 = new MessageProperties();

//这里注意一定要修改contentType为 application/json

messageProperties1.setContentType(“application/json”);

//设置的是标签,而不是全路径

messageProperties1.getHeaders().put(“TypeId”, “order”);

Message message1 = new Message(json1.getBytes(), messageProperties1);

rabbitTemplate.send(“topic001”, “spring.order”, message1);

Packaged pack = new Packaged();

pack.setId(“002”);

pack.setName(“包裹消息”);

pack.setDescription(“包裹描述信息”);

String json2 = mapper.writeValueAsString(pack);

System.err.println("pack 4 json: " + json2);

MessageProperties messageProperties2 = new MessageProperties();

//这里注意一定要修改contentType为 application/json

messageProperties2.setContentType(“application/json”);

//设置的是标签,而不是全路径

messageProperties2.getHeaders().put(“TypeId”, “packaged”);

Message message2 = new Message(json2.getBytes(), messageProperties2);

rabbitTemplate.send(“topic001”, “spring.pack”, message2);

}

打印结果:

打印结果

在通过单元测试运行testSendMappingMessage()方法时会存在一个问题:委派对象MessageDelegate可能会收不到对象。

因为单元测试spring容器在运行完毕之后就停止,不会等到消费者消费完消息之后再停止,所以需要通过正常启动springboot项目,可以看到正常消费消息。

7.1.4 添加全局转换器

修改RabbitMQConfig类

@Bean

public Queue queue_image() {

return new Queue(“image_queue”, true); //队列持久

}

@Bean

public Queue queue_pdf() {

return new Queue(“pdf_queue”, true); //队列持久

}

//1.4 ext convert

MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());

adapter.setDefaultListenerMethod(“consumeMessage”);

//全局的转换器:所有小的Converter都可以放到这个大的Converter中

ContentTypeDelegatingMessageConverter convert = new ContentTypeDelegatingMessageConverter();

TextMessageConverter textConvert = new TextMessageConverter();

//text走文本转换器

convert.addDelegate(“text”, textConvert);

convert.addDelegate(“html/text”, textConvert);

convert.addDelegate(“xml/text”, textConvert);

convert.addDelegate(“text/plain”, textConvert);

//json走json转换器

Jackson2JsonMessageConverter jsonConvert = new Jackson2JsonMessageConverter();

convert.addDelegate(“json”, jsonConvert);

convert.addDelegate(“application/json”, jsonConvert);

//图片走图片转换器

ImageMessageConverter imageConverter = new ImageMessageConverter();

convert.addDelegate(“image/png”, imageConverter);

convert.addDelegate(“image”, imageConverter);

//pdf走pdf转换器

PDFMessageConverter pdfConverter = new PDFMessageConverter();

convert.addDelegate(“application/pdf”, pdfConverter);

adapter.setMessageConverter(convert);

container.setMessageListener(adapter);

修改MessageDelegate

public class MessageDelegate {

public void handleMessage(byte[] messageBody) {

System.err.println(“默认方法, 消息内容:” + new String(messageBody));

}

public void consumeMessage(byte[] messageBody) {

System.err.println(“字节数组方法, 消息内容:” + new String(messageBody));

}

public void consumeMessage(String messageBody) {

System.err.println(“字符串方法, 消息内容:” + messageBody);

}

public void method1(String messageBody) {

System.err.println(“method1 收到消息内容:” + new String(messageBody));

}

public void method2(String messageBody) {

System.err.println(“method2 收到消息内容:” + new String(messageBody));

}

//json对应Map对象

public void consumeMessage(Map messageBody) {

System.err.println(“map方法, 消息内容:” + messageBody);

}

public void consumeMessage(Order order) {

System.err.println("order对象, 消息内容, id: " + order.getId() +

", name: " + order.getName() +

", content: "+ order.getContent());

}

public void consumeMessage(Packaged pack) {

System.err.println("package对象, 消息内容, id: " + pack.getId() +

", name: " + pack.getName() +

", content: "+ pack.getDescription());

}

public void consumeMessage(File file) {

System.err.println(“文件对象 方法, 消息内容:” + file.getName());

}

}

添加PDFMessageConverter

public class PDFMessageConverter implements MessageConverter {

@Override

public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {

throw new MessageConversionException(" convert error ! ");

}

@Override

public Object fromMessage(Message message) throws MessageConversionException {

System.err.println(“-----------PDF MessageConverter----------”);

byte[] body = message.getBody();

String fileName = UUID.randomUUID().toString();

String path = “d:/010_test/” + fileName + “.pdf”;

File f = new File(path);

try {

Files.copy(new ByteArrayInputStream(body), f.toPath());

} catch (IOException e) {

e.printStackTrace();

}

return f;

}

}

添加ImageMessageConverter

public class ImageMessageConverter implements MessageConverter {

@Override

public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {

throw new MessageConversionException(" convert error ! ");

}

@Override

public Object fromMessage(Message message) throws MessageConversionException {

System.err.println(“-----------Image MessageConverter----------”);

Object _extName = message.getMessageProperties().getHeaders().get(“extName”);

String extName = _extName == null ? “png” : _extName.toString();

byte[] body = message.getBody();

String fileName = UUID.randomUUID().toString();

//将接受到的图片放到该位置

String path = “d:/010_test/” + fileName + “.” + extName;

File f = new File(path);

try {

Files.copy(new ByteArrayInputStream(body), f.toPath());

} catch (IOException e) {

e.printStackTrace();

}

return f;

}

}

定义测试方法

@Test

public void testSendExtConverterMessage() throws Exception {

// byte[] body = Files.readAllBytes(Paths.get(“d:/002_books”, “picture.png”));

// MessageProperties messageProperties = new MessageProperties();

// messageProperties.setContentType(“image/png”);

// messageProperties.getHeaders().put(“extName”, “png”);

// Message message = new Message(body, messageProperties);

// rabbitTemplate.send(“”, “image_queue”, message);

byte[] body = Files.readAllBytes(Paths.get(“d:/002_books”, “mysql.pdf”));

MessageProperties messageProperties = new MessageProperties();

messageProperties.setContentType(“application/pdf”);

Message message = new Message(body, messageProperties);

rabbitTemplate.send(“”, “pdf_queue”, message);

}

可以自己测试下图片和pdf的保存。

源码地址:https://gitee.com/573059382/rabbitmq-demos

文末


欢迎关注个人微信公众号:Coder编程

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

学习分享,共勉

这里是小编拿到的学习资源,其中包括“中高级Java开发面试高频考点题笔记300道.pdf”和“Java核心知识体系笔记.pdf”文件分享,内容丰富,囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。同时还有Java进阶学习的知识笔记脑图(内含大量学习笔记)!

资料整理不易,读者朋友可以转发分享下!

Java核心知识体系笔记.pdf

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

中高级Java开发面试高频考点题笔记300道.pdf

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

架构进阶面试专题及架构学习笔记脑图

记一次蚂蚁金服Java研发岗的面试经历,分享下我的复习笔记面经

Java架构进阶学习视频分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
s();

messageProperties.setContentType(“application/pdf”);

Message message = new Message(body, messageProperties);

rabbitTemplate.send(“”, “pdf_queue”, message);

}

可以自己测试下图片和pdf的保存。

源码地址:https://gitee.com/573059382/rabbitmq-demos

文末


欢迎关注个人微信公众号:Coder编程

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-fflgfE9C-1713744328212)]

[外链图片转存中…(img-yOyrNeRc-1713744328213)]

[外链图片转存中…(img-wZ5ei8mI-1713744328213)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

学习分享,共勉

这里是小编拿到的学习资源,其中包括“中高级Java开发面试高频考点题笔记300道.pdf”和“Java核心知识体系笔记.pdf”文件分享,内容丰富,囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。同时还有Java进阶学习的知识笔记脑图(内含大量学习笔记)!

资料整理不易,读者朋友可以转发分享下!

Java核心知识体系笔记.pdf

[外链图片转存中…(img-5R2vVfi5-1713744328213)]

中高级Java开发面试高频考点题笔记300道.pdf

[外链图片转存中…(img-nmgq6PzO-1713744328213)]

架构进阶面试专题及架构学习笔记脑图

[外链图片转存中…(img-iq9i2cKD-1713744328214)]

Java架构进阶学习视频分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值