RabbitMQ入门中篇

news2025/4/13 1:03:49

本篇博文目录

      • 一.Spring整合RabbitMQ
        • 1.导入依赖
        • 2.生产者
        • 3.消费者
        • 4.测试
      • 二.SpringBoot整合RabbitMQ
        • 1.导入依赖
        • 2.生产者
        • 3.消费者
        • 4.测试
      • 三.代码下载

一.Spring整合RabbitMQ

在spring项目中使用RabbitMQ的Exchange模式的Topics,项目分为消费者spring项目和生产者spring项目,其中都导入amqp-client和spring-rabbit依赖,前者为RabbitMQ的client依赖,后者为spring整合RabbitMQ的依赖。

在这里插入图片描述

1.导入依赖

    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.0.5.RELEASE</version>
        </dependency>
    </dependencies>

备注:amqp-client 最新版本为5.16.0;spring-rabbit最新版本3.0.1。

2.生产者

  • 在resources包中创建applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    <!-- 设置连接工厂,配置基本参数 -->
    <rabbit:connection-factory id="connectionFactory" host="127.0.0.1" port="5672" username="guest" password="guest"
                               virtual-host="/test"></rabbit:connection-factory>

    <!--
           fanout-exchange | direct-exchange | topic-exchange
          声明一个名为topicExchange的topic交换机,如果这个交换机不存在,则自动创建
     -->
    <rabbit:topic-exchange name="topicExchange" auto-declare="true">

    </rabbit:topic-exchange>

    <!-- Spring为我们封装了RabbitTemplate对象来简化生产者发送数据的过程,对常用的方法进行了封装。 -->
    <rabbit:template id="template" connection-factory="connectionFactory" exchange="topicExchange"></rabbit:template>
    <!--在生产者中配置template对象,用于发送数据-->
    <bean id="newsProducer" class="com.itlaoqi.rabbit.exchange.NewsProducer">
        <property name="rabbitTemplate" ref="template"/>
    </bean>
    <!-- RabbitAdmin对象用于创建、绑定、管理队列与交换机 -->
    <rabbit:admin id="rabbitAdmin" connection-factory="connectionFactory"/>
</beans>

生产者的配置信息其实就是将Rabbit-client代码中的代码配置转换为配置文件的配置,会简单的进行修改即可,下来对上面相关配置信息进行解释。

<rabbit:connection-factory 标签是设置连接工厂,用来初始化Rabbitmq的连接对象,根据自己的Rabbitmq的配置信息填入连接对象的参数。

  • id:bean的名称
  • host:主机地址
  • port:主机端口号
  • username:登入的用户名
  • password:登入的密码
  • virtual-host:虚拟路径

<rabbit:topic-exchange 标签用来初始化Topics类型的交换机信息,详细参数解释如下:

  • id:bean的名称
  • name:交换机的名称
  • auto-declare:自动创建

备注:交换机有三种方式:fanout-exchange(Sub/Pub) | direct-exchange(Routing) | topic-exchange(Topics)

<rabbit:template Spring为我们封装了RabbitTemplate对象来简化生产者发送数据的过程,对常用的方法进行了封装。

  • id:bean的名称
  • connection-factory:连接工厂
  • exchange:交换机

既然有了上面的RabbitTemplate对象,我们可以通过RabbitTemplate对象来发送数据,创建一个NewsProducer类,用来处理数据的发送:

NewsProducer类:用来处理生产者的数据发送(该类是一个封装类,数据的发送通过该类的sendNews()进行实现)

package com.itlaoqi.rabbit.exchange;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Date;

public class NewsProducer {
    private RabbitTemplate rabbitTemplate = null;

    public RabbitTemplate getRabbitTemplate() {
        return rabbitTemplate;
    }

    public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    public void sendNews(String routingKey , News news){
        //convertAndSend 用于向exchange发送数据
        //第一个参数是routingkey
        //第二个参数是要传递的对象,可以是字符串、byte【】或者任何实现了【序列化接口】的对象
        rabbitTemplate.convertAndSend(routingKey , news);
        System.out.println("新闻发送成功");
    }
  public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        NewsProducer np = (NewsProducer)ctx.getBean("newsProducer");
        np.sendNews("us.20190101" , new News("新华社" , "特朗普又又又退群啦" , new Date() , "国际新闻内容"));
        np.sendNews("china.20190101" , new News("凤凰TV" , "XXX企业荣登世界500强" , new Date() , "国内新闻内容"));
    }
}

上面的NewsProducer类,通过外部传递一个RabbitTemplate对象进行初始化,然后通过 sendNews()方法中的 rabbitTemplate.convertAndSend(routingKey , news); 来发送数据,routingKey 表示路由主键,news表示数据对象,详细的news代码如下,注意该数据对象需要实现Serializable不然会因为无法序列化而抛出异常信息。

News类:传递的数据对象

package com.itlaoqi.rabbit.exchange;

import java.io.Serializable;
import java.util.Date;

public class News implements Serializable{
    private String source;
    private String title;
    private Date createTime;
    private String content;

    public News(String source, String title, Date createTime, String content) {
        this.source = source;
        this.title = title;
        this.createTime = createTime;
        this.content = content;
    }

    public String getSource() {
        return source;
    }

    public void setSource(String source) {
        this.source = source;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

}

<bean id="newsProducer" 创建NewsProducer类的bean对象,并传入初始化参数rabbitTemplate,通过该bean对象实现数据的发送,详细代码如下:

  public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        NewsProducer np = (NewsProducer)ctx.getBean("newsProducer");
        np.sendNews("us.20190101" , new News("新华社" , "特朗普又又又退群啦" , new Date() , "国际新闻内容"));
        np.sendNews("china.20190101" , new News("凤凰TV" , "XXX企业荣登世界500强" , new Date() , "国内新闻内容"));
    }

<rabbit:admin RabbitAdmin对象用于创建、绑定、管理队列与交换机

  • id:bean的名称
  • connection-factory:连接工厂

通过上面的<rabbit:admin,我们可以通过RabbitAdmin对象来实现队列和交换机的创建,绑定和管理,详细的测试代码如下:

package com.itlaoqi.rabbit.exchange;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.util.HashMap;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class RabbitAdminTestor {
    @Resource(name="rabbitAdmin")
    private RabbitAdmin rabbitAdmin;
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 创建交换机
     */
    @Test
    public void testCreateExchange(){
        rabbitAdmin.declareExchange(new FanoutExchange("test.exchange.fanout" , true ,false));
        rabbitAdmin.declareExchange(new DirectExchange("test.exchange.direct" , true ,false));
        rabbitAdmin.declareExchange(new TopicExchange("test.exchange.topic" , true ,false));
    }


    /**
     * 创建队列,绑定交换机并通过生产者发送数据
     */
    @Test
    public void testQueueAndBind(){
        rabbitAdmin.declareQueue(new Queue("test.queue"));
        rabbitAdmin.declareBinding(new Binding(
                "test.queue", Binding.DestinationType.QUEUE,
                "test.exchange.topic", "#", new HashMap<String, Object>()
        ));
        rabbitTemplate.convertAndSend("test.exchange.topic" , "abc" , "abc123");
    }

    /**
     * 删除队列和交换机
     */
    @Test
    public void testDelete(){
        rabbitAdmin.deleteQueue("test.queue");
        rabbitAdmin.deleteExchange("test.exchange.fanout");
        rabbitAdmin.deleteExchange("test.exchange.direct");
        rabbitAdmin.deleteExchange("test.exchange.topic");
    }

}

3.消费者

  • 在resources包中创建applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <rabbit:connection-factory id="connectionFactory" host="127.0.0.1" port="5672" username="guest" password="guest"
                               virtual-host="/test"></rabbit:connection-factory>
    <rabbit:admin connection-factory="connectionFactory"></rabbit:admin>
    <!--创建队列-->
    <rabbit:queue name="topicQueue" auto-declare="true" auto-delete="false" durable="false" exclusive="false"/>
    <!--交换机与队列绑定,并指明筛选条件-->
    <rabbit:topic-exchange name="topicExchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding queue="topicQueue" pattern="us.*"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>
    <!--启动消费者后,Spring底层自动监听对应的topicQueue数据,一旦有新的消息进来,自动传入到consumer Bean的recv的News参数中,
    之后再程序对News进一步处理-->
    <rabbit:listener-container connection-factory="connectionFactory">
        <rabbit:listener ref="consumer" method="recv" queue-names="topicQueue"/>
    </rabbit:listener-container>
    <bean id="consumer" class="com.itlaoqi.rabbitmq.NewsConsumer"></bean>
</beans>

消费者的配置信息和生产者的存在很多相同,下来主要对没有出现过的配置信息进行解释。

<rabbit:queue 创建队列

  • id:bean的名称
  • name:队列的名称
  • auto-declare:自动创建
  • auto-delete:自动删除
  • exclusive:是否独占
  • durable:是否持久化

<rabbit:bindings 交换机与队列绑定,并指明筛选条件

  • queue:队列
  • pattern:筛选条件,*表示任意一个字符,#表示任意多个字符

<rabbit:listener-container 启动消费者后,Spring底层自动监听对应的topicQueue数据,一旦有新的消息进来,自动传入到consumer Bean的recv的News参数中,之后再程序对News进一步处理

  • connection-factory:连接工厂

<rabbit:listener 具体的监听对象

  • ref:监听对象的bean
  • method:监听方法
  • queue-names:队列名称

<bean id="consumer" 监听的bean对象,具体的监听方法recv

package com.itlaoqi.rabbitmq;

import com.itlaoqi.rabbit.exchange.News;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class NewsConsumer {
    public void recv(News news){
        System.out.println("接收到最新新闻:" + news.getTitle() + ":" + news.getSource());
    }

    public static void main(String[] args) {
        //初始化IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

    }
}

4.测试

在这里插入图片描述
在这里插入图片描述

二.SpringBoot整合RabbitMQ

1.导入依赖

	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

2.生产者

  • 添加一个名为springboot-exchange的交换机

在这里插入图片描述

  • application.properties配置
# rabbitmq连接配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/test
spring.rabbitmq.connection-timeout=1000ms

#producer
#confirmlistener
# 在springboot2.2.0.RELEASE版本之后该属性已经过时了,不能在使用了
#spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-confirm-type=CORRELATED
#returnlistener
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true


spring.rabbitmq.publisher-confirms 配置信息在springboot2.2.0.RELEASE版本之后该属性已经过时了,不能在使用了,而是采用spring.rabbitmq.publisher-confirm-type,该属性一共有三种值(来源于:https://blog.csdn.net/qingyuan2014/article/details/113916449):

  • NONE值是禁用发布确认模式,是默认值
  • CORRELATED值是发布消息成功到交换器后会触发回调方法
  • SIMPLE值经测试有两种效果,其一效果和CORRELATED值一样会触发回调方法,其二在发布消息成功后使用rabbitTemplate调用waitForConfirms或waitForConfirmsOrDie方法等待broker节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是waitForConfirmsOrDie方法如果返回false则会关闭channel,则接下来无法发送消息到broker;

spring.rabbitmq.publisher-returns 开启rabbitmq确认机制的return
spring.rabbitmq.template.mandatory mandatory true代表如果消息无法正常投递则return回生产者,如果false,则直接将消息放弃

  • 生产者数据发送对象

MessageProducer类:用于生产者数据发送,通过sendMsg()里的rabbitTemplate.convertAndSend()方法发送数据。

package com.itlaoqi.rabbitmq.springboot;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;

@Component
public class MessageProducer {
    @Resource
    private RabbitTemplate rabbitTemplate ;
    RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
        @Override
        /**
         * CorrelationData 消息的附加信息,即自定义id
         * isack 代表消息是否被broker(MQ)接收 true 代表接收 false代表拒收。
         * cause 如果拒收cause则说明拒收的原因,帮助我们进行后续处理
         */
        public void confirm(CorrelationData correlationData, boolean isack, String cause) {
            System.out.println(correlationData);
            System.out.println("ack:" + isack);
            if(isack == false){
                System.err.println(cause);
            }
        }
    };

    RabbitTemplate.ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
        @Override
        public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingkey) {
            System.err.println("Code:" + replyCode + ",Text:" + replyText );
            System.err.println("Exchange:" + exchange + ",RoutingKey:" + routingkey );
        }
    };

    public void sendMsg(Employee emp){
        //CorrelationData对象的作用是作为消息的附加信息传递,通常我们用它来保存消息的自定义id
        CorrelationData cd = new CorrelationData(emp.getEmpno() + "-" + new Date().getTime());
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        rabbitTemplate.convertAndSend("springboot-exchange" , "hr.employee" , emp , cd);
    }


}

和spring相比springboot使用起来就比较简单了,只需要在application.properties中进行配置(没有进行配置的参数,采用默认参数),就可以使用Rabbitmq提供的RabbitTemplate进行操作,上文中的confirmCallback和returnCallback是Rabbitmq消息确认机制里面对Confirm和return进行监听,通过rabbitTemplate.setConfirmCallback()和rabbitTemplate.setReturnCallback()进行设置。在springboot2.2.0.RELEASE版本之后ReturnCallback和setReturnCallback 已经过时了,建议采用ReturnsCallback和setReturnsCallback(),ReturnsCallback和ReturnCallback的区别就是后者对参数进行了封装。

在这里插入图片描述

ReturnsCallback中returnedMessage的参数为ReturnedMessage

在这里插入图片描述

ReturnCallback中returnedMessage的参数为(message, replyCode, replyText, exchange, routingkey)

在这里插入图片描述

convertAndSend方法中第四个参数CorrelationData 对象作用是作为消息的附加信息传递,通常我们用它来保存消息的自定义id

在这里插入图片描述

  • 数据对象

Employee 类:传递的数据对象,注意需要实现Serializable接口不然会因为无法序列化而产生异常。

package com.itlaoqi.rabbitmq.springboot;

import java.io.Serializable;

public class Employee implements Serializable{
    private String empno;
    private String name;
    private Integer age;

    public Employee(String empno, String name, Integer age) {
        this.empno = empno;
        this.name = name;
        this.age = age;
    }

    public String getEmpno() {
        return empno;
    }

    public void setEmpno(String empno) {
        this.empno = empno;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

  • 发送数据

在这里插入图片描述

  • 添加一个名为springboot-queue的队列,并绑定到交换机springboot-exchange上,如果不怎么做的话会进入到return中,如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

添加后,再次运行:

在这里插入图片描述

3.消费者

  • application.properties配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/test
spring.rabbitmq.connection-timeout=1000ms

spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.concurrency=1
spring.rabbitmq.listener.simple.max-concurrency=5

spring.rabbitmq.listener.simple.acknowledge-mode 表示消息确认方式,其有三种配置方式,分别是none、manual和auto;默认auto,下面对各种参数进行了解释。来源于(https://juejin.cn/post/7029232312197840904)

  • NONE : 不确认 :

1、默认所有消息消费成功,会不断的向消费者推送消息
2、因为 rabbitmq 认为所有消息都被消费成功。所以队列中存在丢失消息风险。

  • AUTO:自动确认

1、根据消息处理逻辑是否抛出异常自动发送 ack(正常)和nack(异常)给服务端,如果消费者本身逻辑没有处理好这条数据就存在丢失消息的风险。
2、使用自动确认模式时,需要考虑的另一件事情就是消费者过载。

  • MANUAL:手动确认

1、手动确认在业务失败后进行一些操作,消费者调用 ack、nack、reject 几种方法进行确认,如果消息未被 ACK 则发送到下一个消费者或重回队列。
2、ack 用于肯定确认;nack 用于 否定确认 ;reject 用于否定确认(一次只能拒绝单条消息)

spring.rabbitmq.listener.simple.concurrency : 最小的消费者数量
spring.rabbitmq.listener.simple.max-concurrency : 最大的消费者数量
来源于:https://blog.csdn.net/ystyaoshengting/article/details/105267542

  • 消费者接收类
package com.itlaoqi.rabbitmq.springboot;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

@Component
public class MessageConsumer {
    //@RabbitListener注解用于声明式定义消息接受的队列与exhcange绑定的信息
    //在SpringBoot中,消费者这端使用注解获取消息
    @RabbitListener(
            bindings = @QueueBinding(
                    value = @Queue(value="springboot-queue" , durable="true"),
                    exchange = @Exchange(value = "springboot-exchange" , durable = "true" , type = "topic") ,
                    key = "#"
            )
    )
    //用于接收消息的方法
    @RabbitHandler //通知SpringBoot下面的方法用于接收消息。
    // 这个方法运行后将处于等待的状态,有新的消息进来就会自动触发下面的方法处理消息
    //@Payload 代表运行时将消息反序列化后注入到后面的参数中
    public void handleMessage(@Payload Employee employee , Channel channel ,
                              @Headers Map<String,Object> headers) {
        System.out.println("=========================================");
        System.out.println("接收到" + employee.getEmpno() + ":" + employee.getName());
        //所有消息处理后必须进行消息的ack,channel.basicAck()
        Long tag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG);
        try {
            channel.basicAck(tag , false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("=========================================");
    }
}

使用 @RabbitListener 注解用于声明式定义消息接受的队列与exhcange绑定的信息,其中 value = @Queue()指向队列, exchange = @Exchange()指向交换机,key表示条件。

    @RabbitListener(
            bindings = @QueueBinding(
                    value = @Queue(value="springboot-queue" , durable="true"),
                    exchange = @Exchange(value = "springboot-exchange" , durable = "true" , type = "topic") ,
                    key = "#"
            )
    )

使用 @RabbitHandler 注解用于表明该方法为接收消息的方法,运行后将处于等待的状态,有新的消息进来就会自动触发下面的方法进行处理,使用 @Payload 注解表示运行时将消息反序列化后注入到后面的参数中, 使用 @Headers 注解表示消息头的信息,包括描述的辅助信息可以用来进行消息确认。

    @RabbitHandler //通知SpringBoot下面的方法用于接收消息。
    // 这个方法运行后将处于等待的状态,有新的消息进来就会自动触发下面的方法处理消息
    //@Payload 代表运行时将消息反序列化后注入到后面的参数中
    public void handleMessage(@Payload Employee employee , Channel channel ,
                              @Headers Map<String,Object> headers) {
        System.out.println("=========================================");
        System.out.println("接收到" + employee.getEmpno() + ":" + employee.getName());
        //所有消息处理后必须进行消息的ack,channel.basicAck()
        Long tag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG);
        try {
            channel.basicAck(tag , false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("=========================================");
    }
}

headers中的内容:

{amqp_receivedDeliveryMode=PERSISTENT, amqp_receivedExchange=springboot-exchange, amqp_deliveryTag=3, amqp_consumerQueue=springboot-queue, amqp_redelivered=false, amqp_receivedRoutingKey=hr.employee, spring_listener_return_correlation=eac10f9e-b1e0-4b5a-92a1-c6aa8ea1f959, spring_returned_message_correlation=3306-1674918565983, id=6e5d0290-77c6-58c3-6fe7-7ed554a4667a, amqp_consumerTag=amq.ctag-mt44wHOpGCeH_e69j7zFwg, amqp_lastInBatch=false, contentType=application/x-java-serialized-object, timestamp=1674918566121}

其中headers里的 spring_returned_message_correlation=3306-1674918565983 就是生产者中CorrelationData的自定义id

在这里插入图片描述

4.测试

在这里插入图片描述
在这里插入图片描述

三.代码下载

在我的微信公众号后台回复 rabbitmq 就可以获取本篇博文相关的源代码了,如果有什么疑问后台给为留言,我看见会第一时间回复你的。

在这里插入图片描述

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

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

相关文章

Centos7安装kvm服务器

Centos7安装kvm服务器什么是kvm检查硬件是否支持kvm虚拟化启用嵌套虚拟化(可选)查看是否启用嵌套虚拟化嵌套虚拟化立即生效(临时)重新加载模块并验证虚拟机硬件直通虚拟机内查看嵌套虚拟化是否成功安装kvm服务器安装kvm的所有包(不推荐)启动libvirt服务查看是否正确设置了虚拟化…

Linux操作系统之线程安全

一、引入线程安全关于strtok函数不能在多线程中使用&#xff0c;为什么&#xff1f;运行结果原因如下&#xff1a;在strtok内部会有一个全局变量静态指针&#xff08;static char* ptr&#xff09;&#xff0c;一开始指针指向主线程的a&#xff0c;等到后面子线程开始分割时&am…

18 Java反射reflect(类加载+获取类对象+通用操作+设计模式+枚举+注解)

Java反射18 反射reflect18.1 类的加载18.2 Class对象18.3 获取类对象的3种方法18.4 反射通用操作18.4.1 常见方法18.4.2 通用操作18.5 设计模式18.5.1 概念18.5.2 好处18.5.3 工厂设计模式18.5.4 单例模式18.6 枚举18.7 注解18.7.1 概念18.7.2 定义注解18.7.3 注解属性类型18.7…

知名休闲服饰品牌——慕尚集团借力泛微实现统一办公、业务协同

客户简介 宁波中哲慕尚控股有限公司&#xff08;以下简称慕尚集团&#xff09;是中国知名的由新零售模式驱动的休闲时尚服饰多品牌运营公司。 旗下品牌有GXG,gxgjeans,gxg.kids,MODE COMMUTER等&#xff0c;覆盖时尚男女装、童装市场和其他时尚领域。2019年5月慕尚集团正式登…

不要在线上滥用CopyOnWriteArrayList,姿势不对性能真的很拉胯

从JDK1.5版本&#xff0c;JAVA提供了线程安全的List增强版CopyOnWriteArrayList&#xff0c;其保持线程安全的方式是&#xff1a;每次修改数据时&#xff0c;不会直接修改数据&#xff0c;而是把数据复制出来一份&#xff0c;对复制出来的数组进行操作。 通过这样的机制&#…

【图像算法】马赛克识别

【目的】 校验视频中出现马赛克的频率&#xff0c;抽象成将视频切割成图片&#xff0c;对每张代测图片进行自动化验证。 【实现】 图像边缘检测算法识别 算法步骤&#xff1a; 使用高斯滤波器&#xff0c;以平滑图像&#xff0c;滤除噪声。计算图像中每个像素点的梯度强度和…

buuctf-web-[BJDCTF2020]Easy MD51

打开环境一个简单的页面查看源代码一个get传参&#xff0c;随便输入试试看输入1,1",1,均无反应&#xff0c;每次遇到这种有输入框的都以为是sql注入&#xff0c;但是题目为md5标头里面看到提示select * from admin where passwordmd5($pass,true)搜索相关漏洞&#xff0c;…

gost 常用tunnel配置示例(隧道模式)

gost是用golang语言实现的一个安全隧道。地址位于&#xff1a;https://github.com/ginuerzh/gost是一个不可多得的隧道工具。至于什么是隧道&#xff1f; 就是可以通过这个工具传输一些其他协议的数据。就像这个样子。隧道有什么用呢&#xff1f;可以起到一些加速的作用或者流量…

Array.prototype.from()

Array.from() 用于将类数组对象或可迭代对象转化为一个新的浅拷贝数组实例。 let arr Array.from({length:3},(_,i)>({id:item-${i1}}))console.log(arr)Array.from()转换数组 // Array.from 转换成数组let arr2 Array.from(chixinAwen)console.log(arr2) 示例&#xff1a…

如何免安装使用 Python?推荐 17 个在线的 Python 解释器

安装 Python 很容易&#xff0c;但或许你正在用智能手机/平板电脑&#xff0c;在用不允许安装软件的电脑&#xff0c;或者因为其它原因无法安装 Python。那么&#xff0c;如何通过免安装的方式使用 Python 呢&#xff1f; 本文将介绍 17 个免费的 Python 解释器和交互式 Shell…

百里挑一,4款免费又实用的软件,用一次就爱上

好看的皮囊千篇一律&#xff0c;实用的软件百里挑一&#xff0c;下面几款软件都是笔者收集多年所得&#xff0c;实用且免费。 1、坚果云 这是一款颠覆许多人认知的网盘工具&#xff0c;免费使用无广告&#xff0c;不限速的优点就比某度网盘强百倍&#xff0c;支持任何设备&…

AOP案例:测量业务层接口万次执行时间

测量业务层接口万次执行时间1. 准备1.1 service层&#xff1a;1.2 dao层&#xff1a;1.3 SpringConfig配置类&#xff1a;2. AOP2.1 通知类2.2 测试类&#xff1a;3. 问题及改进1. 准备 需求&#xff1a;任意业务层接口执行均可显示执行的时长&#xff1b; 切入点配置&#x…

(day9) 自学Java——常用API

AIP就是Java已经写好的各种功能的java类 目录 1.Math 2.System 3.Runtime 4.Object 5.对象工具类Objects 6.BIgInteger 7.BigDecima 8.正则表达式 (1)爬虫 (2)带条件爬取&#xff0c;贪婪爬取和识别正则的两个方法 (3)捕获分组和非捕获分组 9.JDK7以前时间相关类 …

一起自学SLAM算法:12.3 autoware导航系统

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 上面介绍的ros-navigation和riskrrt导航系统主要都是用于机器人的低速导航&#xff0c;并且大多基于2D地图。而autoware导航系统主要用于无人驾驶汽车的高速导航&#xff0c;并且基于3D地图。除了所导航速度高一…

软件工程(一)——软件开发模型和方法

目录 &#xff08;一&#xff09;软件开发方法 &#xff08;二&#xff09;瀑布模型 &#xff08;三&#xff09;原型模型 &#xff08;四&#xff09;螺旋模型与增量模型 &#xff08;五&#xff09;V模型、喷泉模型、RAD模型 (六) 统一过程&#xff08;RUP&#xff09; …

idea 中 connot run program “svn“ 系统找不到文件

idea 中 connot run program “svn“ 系统找不到文件1. idea中svn的问题1.1 idea connot run program "svn"1.1.1 解决办法-重装svn1.2 idea中check out灰色不可用1.2.1 解决办法—装插件1. idea中svn的问题 1.1 idea connot run program “svn” 如图&#xff1a;…

2023年机床工具行业研究报告

第一章 行业概况 生产和销售机床工具的行业。机床是指制造机器的机器&#xff0c;亦称工作母机或工具机&#xff0c;习惯上简称机床。一般分为金属切削机床、锻压机床和木工机床等。现代机械制造中加工机械零件的方法很多&#xff1a;除切削加工外&#xff0c;还有铸造、锻造、…

【ES实战】索引生命周期管理(一)

索引生命周期管理IL&#xff2d;&#xff08;index lifecycle management&#xff09; 文章目录索引生命周期管理IL&#xff2d;&#xff08;index lifecycle management&#xff09;概述版本矩阵主要概念索引生命周期阶段的过渡阶段的执行生命周期中的操作生命周期策略索引翻滚…

AzureRT:一款能够实现各种Azure红队技战术的PowerShell模块

关于AzureRT AzureRT是一款能够实现各种Azure红队技战术的PowerShell模块&#xff0c;在AzureRT的帮助下&#xff0c;广大研究人员可以从攻击者的角度来与Azure和Azure AD进行交互&#xff0c;以此来测试目标环境的安全性。 AzureRT可以帮助广大研究人员测试和处理基于访问令…

【工具】操作PDF推荐的java依赖

Apache PDFBox |一个 Java PDF 库 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox-app</artifactId><version>2.0.27</version></dependency> Apache PDFBox - A Java PDF Library Apache PDFBox 库…