MapStruct 实体类转换工具,看这一篇就够了!

news2024/12/23 12:25:32

前言

  • DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,泛指用于展示层与服务层之间的数据传输对象。
  • DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。

说明:在一个成熟的工程中,尤其是现在的分布式系统中,应用与应用之间,还有单独的应用细分模块之后,DO 一般不会让外部依赖,这时候需要在提供对外接口的模块里放 DTO 用于对象传输,也即是 DO 对象对内,DTO对象对外,DTO 可以根据业务需要变更(只需部分字段或字段名不同等),并不需要映射 DO 的全部属性。

这种 对象与对象之间的互相转换,就需要有一个专门用来解决转换问题的工具,毕竟每一个字段都 get/set 会很麻烦。

MapStruct 就是这样的一个属性映射工具,只需要定义一个 Mapper 接口,MapStruct 就会自动实现这个映射接口,避免了复杂繁琐的映射实现。MapStruct官网地址: http://mapstruct.org/

MapStruct是一个用于Java的代码生成库,它基于注解处理器在编译时生成类型安全且高性能的对象映射代码。MapStruct可以帮助开发者减少手动编写繁琐的JavaBean之间的转换代码,提高代码的可读性和维护性。

一、什么是 MapStruct

MapStruct 核心概念

MapStruct是一个Java注解处理器,它的主要功能是自动生成类型安全、高性能且无依赖的bean映射代码。这个工具基于“约定优于配置”的原则,极大地简化了Java Bean类型之间的映射实现过程。

图片

在多层架构的应用中,经常需要在不同的对象模型之间进行转换,例如在持久层的实体和传输层的DTO(Data Transfer Object,数据传输对象)之间。手动编写这种映射代码是一项繁琐且容易出错的任务。MapStruct通过自动化的方式解决了这个问题,它可以在编译时生成映射代码,从而保证了高性能、快速的开发反馈以及严格的错误检查。

具体来说,使用MapStruct时,开发者只需要定义一个接口,并在接口中定义转换方法。然后,MapStruct会自动生成实现这些方法的代码。这些生成的代码使用纯方法调用,因此速度快、类型安全且易于理解。

MapStruc主要特性

1、类型安全:MapStruct在编译时生成映射代码并进行类型检查,如果源对象和目标对象的属性不匹配,会在编译阶段就报错。

2、性能优秀:由于MapStruct在编译时就生成了映射代码,运行时无需通过反射进行属性拷贝,因此性能较高。

3、灵活性:MapStruct支持复杂的映射,如嵌套映射、集合映射、自定义转换规则等。

4、简洁性:MapStruct使用注解来定义映射规则,使得映射规则的定义更加直观和简洁。

5、无依赖:MapStruct不依赖于任何第三方库,可以很容易地集成到任何项目中。

6、集成Spring:MapStruct也可以与Spring框架集成,允许在映射器中注入Spring管理的bean。

使用MapStruct,开发者只需要定义一个接口,并在接口中声明源对象和目标对象之间的映射关系,MapStruct会在编译时自动生成映射实现类。这极大地提高了代码的可读性和可维护性,同时也避免了手动编写繁琐的转换代码。

二、MapStruct和BeanUtils区别

MapStruct和BeanUtils都是Java中常用的对象属性映射工具,但它们在使用方式和性能上有一些区别。

「1、使用方式:」BeanUtils:使用反射机制进行属性拷贝,使用简单,无需写额外的映射代码。 MapStruct:需要定义映射接口,在编译阶段生成映射实现类,使用注解来定义源对象和目标对象之间的映射关系。

「2、性能:」BeanUtils:由于使用了反射机制,性能较低。 MapStruct:在编译阶段就生成了映射代码,运行时无需通过反射进行属性拷贝,因此性能较高。

「3、灵活性和安全性:」BeanUtils:由于是动态映射,如果源对象和目标对象的属性不匹配,可能会在运行时出现错误。 MapStruct:在编译阶段就进行了类型检查,如果源对象和目标对象的属性不匹配,会在编译阶段就报错,提高了类型安全性。另外,也支持复杂的映射,如嵌套映射、集合映射等。

先看一个案例:

@Mapper
public interface CarMapper {
  
    @BeanMapping(resultType = CarDto.class, ignoreByDefault = true, mappingControl = MappingControl.FILTER)
    @Mapping(target = "color", ignore = true)
    @Mapping(source = "model", target = "modelName")
    @Mapping(condition = "java(source.getAge() >= 18)", target = "isAdult")
    CarDto map(Car car);
  
}

在这个例子中,我们定义了一个名为CarMapper的映射器接口,并使用了@Mapper注解将它标记为MapStruct映射器。然后,我们在映射方法上使用了@BeanMapping注解,并提供了以下配置:

  • resultType = CarDto.class:指定映射方法的返回类型为CarDto。
  • ignoreByDefault = true:在目标类型CarDto中忽略所有未映射的属性。
  • mappingControl = MappingControl.FILTER:如果存在未匹配的属性,过滤它们而不报告错误或警告。

接下来,我们使用了@Mapping注解,对特定属性进行了额外的配置:

  • target = “color”, ignore = true:忽略源对象的color属性,在目标对象CarDto中不进行映射。
  • source = “model”, target = “modelName”:将源对象的model属性映射到目标对象的modelName属性。
  • condition = “java(source.getAge() >= 18)”:添加条件判断,只有当源对象的age属性大于等于18时,才进行映射,并将结果映射到目标对象的isAdult属性。

通过这些配置,我们能够灵活地控制映射方法的行为。可以根据需求指定返回类型、忽略属性、设置映射控制策略,并添加条件判断来决定是否执行映射操作。

三、MapStruct的使用方法

添加依赖

在你的pom.xml文件中添加MapStruct的依赖:

  • org.mapstruct:mapstruct:包含了一些必要的注解,例如@Mapping。若我们使用的JDK版本高于1.8,当我们在pom里面导入依赖时候,建议使用坐标是:org.mapstruct:mapstruct-jdk8,这可以帮助我们利用一些Java8的新特性。
  • org.mapstruct:mapstruct-processor:注解处理器,根据注解自动生成mapper的实现。
<!--mapstruct核心-->
<dependency>
    <groupId>org.mapstruct</groupId>
  	<!-- jdk8以下就使用mapstruct -->
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.5.5.Final</version>
</dependency>
<!--mapstruct编译-->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
</dependency>

基本映射

在生成的方法实现中,源类型(例如Person)的所有可读属性都将被复制到目标类型(例如PersonDto)的相应属性中:

  1. 当一个属性与其目标实体对应的名称相同时,它将被隐式映射。
  2. 当属性在目标实体中具有不同的名称时,可以通过@Mapping注释指定其名称。
  • 如果不指定@Mapping,默认映射name相同的field
  • 如果映射的对象field name不一样,通过 @Mapping 指定。
  • 忽略字段加@Mapping#ignore() = true

Person类

@Data
public class Person {

    private String id;

    private String name;

    private BigDecimal source;

    private double height;

    private Date createTime;
	
}

PersonDto类

@Data
public class PersonDTO {

    private Long id;

    private String personName;

    private String source;

    private String height;
    
}

定义Mapper接口

定义一个Mapper接口,这个接口将包含你想要转换的方法。

例如,假设你有两个类,Person和PersonDto,你想要将Person对象转换为PersonDto对象,那么你可以这样定义你的Mapper:

@Mapper
public interface PersonMapper {
  
    PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );

		@Mapping(target = "name", source = "personName")
	  @Mapping(target = "id", ignore = true) // 忽略id,不进行映射
		PersonDto personToPersonDto(Person person);

}

使用Mapper

当你已经定义了Mapper接口后,你就可以在你的代码中使用它了:

@Test
public void test(){
     Person person = new Person();
  	 person.setId("001");
     person.setAge(18);
     person.setName("张三");
     person.setHeight(170.5);
     person.setSource(new BigDecimal("100"));

     PersonDTO dto = PersonMapper.INSTANCT.conver(person);

     System.out.println(dto);
    // PersonDTO(id=null, personName=张三, age=18, source=100, height=170.5)
}

componentModel 属性

@Mapper 注解的 componentModel 属性用于指定自动生成的接口实现类的组件类型,这个属性支持四个值:

  • default: 这是默认的情况,mapstruct 不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。
@Mapper
public interface PersonMapper {

    PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
    
    PersonDto personToPersonDto(Person person);

}
@Service
public class PersonService {
    
  	public PersonDto convert(Person person){
      PersonDto dto = PersonMapper.INSTANCE.personToPersonDto(person);
      return dto;
    }
   
}
  • cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
  • spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired或者@Resource方式进行注入
@Mapper(componentModel = "spring")
public interface PersonMapper {

    @Mapping(source = "name", target = "fullName")
    PersonDto personToPersonDto(Person person);

}
@Service
public class PersonService {

    @Autowired
  	private PersonMapper personMapper;
    
  	public PersonDto convert(Person person){
      PersonDto dto = personMapper.personToPersonDto(person);
      return dto;
    }
   
}
  • jsr330: 生成的实现类上会添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取

nullValueCheckStrategy属性

是否在生成的实现类中,对每一个属性进行null检查,可选值有两个:ON_IMPLICIT_CONVERSION(默认值)和 ALWAYS

ALWAYS

ALWAYS 表示在赋值之前,对每一个属性进行!= null的检查。

@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS)
public interface PersonMapper {

    PersonDto personToPersonDto(Person person);

}

ON_IMPLICIT_CONVERSION

ON_IMPLICIT_CONVERSION则表示直接进行赋值,不进行 != null 判断。

@Mapper(nullValueCheckStrategy = NullValueCheckStrategy.ON_IMPLICIT_CONVERSION)
public interface PersonMapper {

    PersonDto personToPersonDto(Person person);

}

nullValuePropertyMappingStrategy属性

指定当源属性为null或者不存在时目标属性生成值的策略,可选值有:

SET_TO_NULL(默认值)

当源属性为null或者不存在时,设置目标属性的值为null。

SET_TO_DEFAULT

根据源属性的类型,设置指定的默认值。

源属性是List,则默认值是ArrayList;

源属性是String,则默认值是"";源属性是基本类型,则默认值是0或者false;

源属性是Map,则默认值是LinkedHashMap;

源属性是数组,则默认值是空数组。

IGNORE

如果源属性是null或者不存在,则不会将null赋值给目标属性,目标属性是什么值就保持什么值。

指定默认值

在@Mapper接口类里面的转换方法上添加@Mapping注解
target() 必须添加,source()可以不添加,则直接使用defaultValue

@Mapping(target = "describe", defaultValue = "默认值")
PersonDTO conver(Person person);

自定义映射

在某些情况下,可能需要自定义字段映射。可以通过在@Mapping注解中使用expressionqualifiedByName参数来实现这一点。

expression

expression: 这个参数允许你使用Java表达式来定义字段映射。这在源和目标字段之间需要一些特定逻辑时非常有用。

注意: 这个属性不能与source()defaultValue()defaultExpression()qualifiedBy()qualifiedByName()constant()一起使用。

例如:

@Mapper
public interface OrderMapper {
    @Mapping(target = "orderDate", expression = "java(new java.text.SimpleDateFormat(\"yyyy-MM-dd\").format(order.getCreationDate()))")
    OrderDto orderToOrderDto(Order order);
}

在这个例子中,orderToOrderDto方法将Order的creationDate字段(类型为Date)转换为OrderDto的orderDate字段(类型为String),并且使用了特定的日期格式。

qualifiedByName和@namd

qualifiedByName: 这个参数允许你引用一个具有@Named注解的方法作为自定义的映射逻辑。例如:

@Mapper
public interface OrderMapper {
  
    @Mapping(target = "customerName", source = "customer", qualifiedByName = "fullName")
    OrderDto orderToOrderDto(Order order);

		@Named("fullName")
		default String customerToString(Customer customer) {
    		return customer.getFirstName() + " " + customer.getLastName();
		}

}

在这个例子中,orderToOrderDto方法将Order的customer字段(类型为Customer)转换为OrderDto的customerName字段(类型为String),并且使用了customerToString方法来获取全名。

映射方法级别的详细配置

从MapStruct 1.5开始,可以使用@BeanMapping注解在MapStruct中用于在映射方法级别提供更详细的配置。这个注解有许多参数可以使用,例如,你可以选择在更新时忽略null值。

以下是一些常见的使用场景:

  • resultType: 这个参数允许你指定映射方法的返回类型。这在目标类型可以是多个实现类时非常有用。 如果目标类型有多个实现类,并且你希望在映射时使用特定的实现类。通过指定resultType,你可以确保生成的映射代码使用正确的目标类型
@BeanMapping(resultType = CarDto.class)
CarDto map(Car car);
  • qualifiedByqualifiedByName: 这两个参数允许你引用一个具有@Qualifier@Named注解的方法作为自定义的映射逻辑。
@BeanMapping(qualifiedByName = "fullName")
PersonDto personToPersonDto(Person person);

@Named("fullName")
default String customerToString(Customer customer) {
    return customer.getFirstName() + " " + customer.getLastName();
}
  • ignoreByDefault: 这个参数允许你忽略所有未明确映射的属性。然后,你可以使用@Mapping注解来明确需要映射的属性。
@BeanMapping(ignoreByDefault = true)
@Mapping(target = "name", source = "fullName")
PersonDto personToPersonDto(Person person);
  • nullValuePropertyMappingStrategy: 这个参数允许你指定当源属性为null时应如何处理目标属性。例如,你可以选择是否在源属性为null时调用目标的setter方法。
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
PersonDto personToPersonDto(Person person);

集合映射

MapStruct也支持集合的映射,你可以很方便地将一个对象的集合转换为另一个对象的集合。

@Mapper
public interface CarMapper {
    List<CarDto> carsToCarDtos(List<Car> cars);
}

在这个例子中,carsToCarDtos方法将List转换为List。

枚举映射

MapStruct 的 @ValueMapping 注解是用来映射枚举值的。这个注解只能在 @Mapper 的接口或抽象类中使用。 下面是一个简单的例子,展示了如何使用 @ValueMapping 在两个枚举类型之间进行映射:

首先,我们定义两个枚举类型:

public enum SourceEnum {
    TYPE_A,
    TYPE_B,
    TYPE_C
}
public enum TargetEnum {
    TYPE_X,
    TYPE_Y,
    TYPE_Z
}

然后,我们创建一个映射器接口并使用 @ValueMapping:

@Mapper
public interface EnumMapper {
    @ValueMappings({
        @ValueMapping(source = "TYPE_A", target = "TYPE_X"),
        @ValueMapping(source = "TYPE_B", target = "TYPE_Y"),
        @ValueMapping(source = "TYPE_C", target = "TYPE_Z")
    })
    TargetEnum sourceToTarget(SourceEnum sourceEnum);
}

在上述代码中,我们定义了一个 sourceToTarget 方法,它将 SourceEnum 对象映射到 TargetEnum 对象。@ValueMapping 注解指定了源枚举值到目标枚举值的映射。

此外,MapStruct 还提供了特殊的源/目标值 NULL 和 ANY,可以用于处理源枚举值为 null 或未映射的情况。例如:

@Mapper
public interface EnumMapper {
    @ValueMappings({
        @ValueMapping(source = "TYPE_A", target = "TYPE_X"),
        @ValueMapping(source = "TYPE_B", target = "TYPE_Y"),
        @ValueMapping(source = "TYPE_C", target = "TYPE_Z"),
        @ValueMapping(source = "NULL", target = "TYPE_Z"),
        @ValueMapping(source = "ANY", target = "TYPE_X")
    })
    TargetEnum sourceToTarget(SourceEnum sourceEnum);
}

在这个例子中,如果源枚举值为 null,则目标枚举值为 TYPE_Z;如果源枚举值没有映射(即源枚举有其他值),则目标枚举值为 TYPE_X。

嵌套属性映射

MapStruct也支持嵌套属性的映射。例如,如果你的Car类有一个Engine属性,你可以这样定义你的Mapper:

@Mapper
public interface CarMapper {
    @Mapping(source = "engine.horsePower", target = "horsePower")
    CarDto carToCarDto(Car car);
}

在这个例子中,carToCarDto方法将Car的engine.horsePower属性映射到CarDto的horsePower属性。

反向映射

MapStruct还提供了反向映射的功能。你可以使用@InheritInverseConfiguration注解来创建反向的映射方法:

@Mapper
public interface CarMapper {
  
    CarDto carToCarDto(Car car);

    @InheritInverseConfiguration
    Car carDtoToCar(CarDto carDto);

}

在这个例子中,carDtoToCar方法是carToCarDto方法的反向映射。

使用装饰器增强Mapper

你可以使用装饰器来增强你的Mapper。

首先,定义一个装饰器类:

public abstract class CarMapperDecorator implements CarMapper {
  
    private final CarMapper delegate;
  
    public CarMapperDecorator(CarMapper delegate) {
        this.delegate = delegate;
    }

    @Override
    public CarDto carToCarDto(Car car) {
        CarDto dto = delegate.carToCarDto(car);
        dto.setMake(dto.getMake().toUpperCase());
        return dto;
    }

}

然后在你的Mapper接口中使用@DecoratedWith注解:

@Mapper
@DecoratedWith(CarMapperDecorator.class)
public interface CarMapper {
    CarDto carToCarDto(Car car);
}

使用@BeforeMapping和@AfterMapping进行预处理和后处理

你可以使用@BeforeMapping和@AfterMapping注解来进行映射前后的处理:

@Mapper
public abstract class CarMapper {
  
    @BeforeMapping
    protected void enrichCar(Car car) {
        car.setMake(car.getMake().toUpperCase());
    }

    @Mapping(source = "numberOfSeats", target = "seatCount")
    public abstract CarDto carToCarDto(Car car);
    
    @AfterMapping
    protected void enrichDto(Car car, @MappingTarget CarDto carDto) {
        carDto.setSeatCount(car.getNumberOfSeats());
    }

}

在这个例子中,enrichCar方法在映射前被调用,enrichDto方法在映射后被调用。

映射更新

MapStruct允许你将一个对象的属性更新到另一个已存在的对象。例如:

@Mapper
public interface CarMapper {
    @Mapping(source = "numberOfSeats", target = "seatCount")
    void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}

在这个例子中,updateCarFromDto方法将CarDto的属性更新到已存在的Car对象。

忽略某些字段

有时候,你可能想要忽略源对象中的某些字段。你可以使用@Mapping注解的ignore参数来实现这一点:

@Mapper
public interface CarMapper {
    @Mapping(target = "seatCount", ignore = true)
    CarDto carToCarDto(Car car);
}

在这个例子中,carToCarDto方法将忽略Car的seatCount字段。

常量映射

@Mapping注解constant属性可以用于将源对象的某个固定值映射到目标对象的属性:

@Mapper
public interface CarMapper {
    @Mapping(target = "carType", constant = "SEDAN")
    CarDto carToCarDto(Car car);
}

在这个例子中,carToCarDto方法将会把CarDto的carType字段设置为"SEDAN",无论Car对象的实际内容如何。

使用@Mappings定义多个映射规则

你可以使用@Mappings注解来定义多个映射规则:

@Mapper
public interface CarMapper {
    @Mappings({
        @Mapping(source = "numberOfSeats", target = "seatCount"),
        @Mapping(source = "manufacturingDate", target = "year")
    })
    CarDto carToCarDto(Car car);
}

在这个例子中,carToCarDto方法将Car的numberOfSeats属性映射到CarDto的seatCount属性,将manufacturingDate属性映射到year属性。

默认值映射

MapStruct也支持默认值映射,你可以使用@Mapping注解的defaultValue参数来实现这一点:

@Mapper
public interface CarMapper {
    @Mapping(target = "seatCount", source = "numberOfSeats", defaultValue = "4")
    CarDto carToCarDto(Car car);
}

在这个例子中,如果Car的numberOfSeats属性为null,那么carToCarDto方法会将CarDto的seatCount字段设置为"4"。

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

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

相关文章

基于NXP IMX8P+FPGA+AI全自动化学发光免疫分析仪解决方案

全自动化学发光免疫分析仪 全自动化学发光免疫分析仪可对受试者的全血、血清、血浆标本进行分析&#xff0c;从而制定科学的治疗或者处置方案。 IMX8P采用Cortex-A53架构四核处理器&#xff0c;主频高达1.8GHz&#xff0c;标配2G内存&#xff0c;强劲的视频处理能力搭配高版本…

贪心算法总结(4)

一、跳跃游戏I 55. 跳跃游戏 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool canJump(vector<int>& nums) {//贪心双指针 用left和right指向两个区间 然后maxpos表示下一层的最右端点int left0,right0,maxpos0,nnums.size();while(left<…

《乳腺密度高的女性中,使用AI辅助的乳腺X线筛查与补充筛查超声的比较研究》| 文献速递-基于深度学习的乳房、前列腺疾病诊断系统

Title 题目 Screening Outcomes of Mammography with AI in Dense Breasts: A Comparative Study with Supplemental Screening US 《乳腺密度高的女性中&#xff0c;使用AI辅助的乳腺X线筛查与补充筛查超声的比较研究》 Background 背景 Comparative performance between…

html+css 实现hover 故障效果按钮

前言:哈喽,大家好,今天给大家分享html+css 实现hover 故障效果按钮!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、原理解析💡1.这是一个,鼠标hover时,显示故障的效…

WechatBotCMD:通过命令行接口实现微信机器人自动化

WechatBotCMD&#x1f916;&#xff1a;通过命令行接口实现微信机器人自动化 WechatBotCMD简介WechatBotCMD的核心功能1. **命令行界面 (CLI)**2. **消息处理与自动回复**3. **定时任务**4. **群聊管理**5. **配置管理** 如何安装或引入 WechatBotCMDWechatBotCMD使用示例启动和…

Leetcode JAVA刷刷站(33)搜索旋转排序数组

一、题目概述 二、思路方向 要设计一个时间复杂度为 O(log n) 的算法来找到旋转排序数组中的目标值&#xff0c;我们可以利用二分查找的变种方法。关键在于&#xff0c;虽然数组被旋转了&#xff0c;但数组被分为两部分后&#xff0c;每部分仍然是升序的。我们可以利用这个性质…

后端Web核心之请求响应

目录 1.概述 2.接收请求 简单参数和实体参数 数组集合参数 日期参数和JSON参数 路径参数 总结 3.返回响应 1.概述 Web请求和响应是HTTP协议中的核心概念&#xff0c;它们是客户端&#xff08;通常是浏览器&#xff09;与服务器之间通信的基础。 浏览器发起请求后&…

大数据-85 Spark 集群 RDD创建 RDD-Action Key-Value RDD详解 RDD的文件输入输出

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09; HDFS&#xff08;已更完&#xff09; MapReduce&#xff08;已更完&…

机器学习:knn算法

1、概述 全称是k-nearest neighbors&#xff0c;通过寻找k个距离最近的数据&#xff0c;来确定当前数据值的大小或类别。K-近邻算法是一种基本而又有效的机器学习算法&#xff0c;用于分类和回归任务。它属于实例学习方法&#xff0c;或者说是一种基于规则的记忆方法。 2、基本…

STM32外设篇:MPU6050

MPU6050简介 MPU6050是一个6轴姿态传感器&#xff0c;可以测量芯片自身X、Y、Z轴的加速度、角速度参数&#xff0c;通过数据融合可进一步得到姿态角&#xff08;欧拉角&#xff09;&#xff0c;常应用于平衡小车、飞行器等需要检测自身姿态的场景。 3轴加速度计3轴陀螺仪传感…

各种国产操作系统,一个 U 盘搞定

熟悉 Windows 装机的朋友对老毛桃和大白菜这类装机工具应该不陌生。这两款流行的工具可以用来制作启动盘&#xff0c;方便进行系统安装、备份和还原等操作。它们集成了多种磁盘工具&#xff0c;并支持一个启动 U 盘安装多个版本的 Windows 系统&#xff0c;如 Windows 7、Windo…

css中的高度塌陷

CSS高度塌陷&#xff08;或称为高度坍塌&#xff09;是指在某些特定情况下&#xff0c;元素的高度无法被正确计算或显示的现象。这通常发生在具有浮动属性的元素或使用绝对定位的元素周围。 原因 高度塌陷通常发生在父元素包含着一个或多个浮动元素时。由于浮动元素被移出了正…

ChatTTS部署

1、创建conda环境 conda create -n TTS python3.10 conda activate TTS2、拉取源代码 # 从 GitHub 下载代码 git clone https://github.com/2noise/ChatTTS cd ChatTTS拉取模型文件 git clone https://www.modelscope.cn/pzc163/chatTTS.git ChatTTS-Model3、安装环境依赖 …

UDP详解/消息边界

本文旨在解释了为什么说UDP是不可靠,到底什么是UDP的消息边界,以及UDP是否会出现粘包和半包的问题 概念 UDP协议是一种面向非连接的协议,面向非连接指的是在正式通信前不必与对方先建立连接,不管对方状态就直接发送,至于对方是否可以接收到这些数据内容,UDP协议无法控制…

HTML+CSS进阶用法 (下)——移动端适配、媒体查询和响应式布局

欢迎来到移动端适配方案的介绍&#xff01;随着移动互联网的快速发展&#xff0c;越来越多的用户通过手机和平板电脑访问网站。为了确保网站能够在各种设备上呈现出最佳的视觉效果和用户体验&#xff0c;我们需要采取有效的适配策略。本篇文章将带你了解几种常用的适配方法&…

记事本打不开(保姆级教程)

问题可能是这样的&#xff1a; 1. 应用程序故障&#xff1a;记事本程序可能遇到了临时的应用程序故障或错误。 2. 系统文件损坏&#xff1a;系统文件损坏或丢失可能导致记事本无法正常启动。 3. 注册表问题&#xff1a;注册表中的条目错误或缺失可能影响记事本的加载。 4. 输入…

Blender的Python编程介绍

在Blender这个免费的开源3D设计软件中&#xff0c;最值得称道的一点是可以用Python程序来辅助进行3D设计&#xff0c;我们可以通过Python来调整物体的属性&#xff0c;生成新的物体&#xff0c;甚至生成新的动画等等。 在最近的一个项目中&#xff0c;我用Blender制作了一个动…

PVE 系统下虚拟机数据盘从IDE转换为VIRIO

一、卸载已经挂载的 IDE 数据盘 [rootlocalhost ~]# df -h 文件系统 容量 已用 可用 已用% 挂载点 /dev/mapper/centos-root 29G 897M 29G 4% / devtmpfs 909M 0 909M 0% /dev tmpfs 920M 0 920M 0% /dev/shm tmpfs 920M 8.5M 912M 1% /run tmpfs 920M 0 920M 0% /sys/fs/cgro…

nginx的平滑升级及版本回滚

官方源码包下载地址&#xff1a;nginx: download 一、编译安装Nginx-1.24.0 [rootNginx ~]# dnf install gcc pcre-devel zlib-devel openssl-devel -y [rootNginx ~]# mkdir /nginx #创建目录&#xff0c;将nginx-1.24.0.tar.gz放在这个目录里 [rootNginx nginx]# tar…

C++的动态数组以及std:vector的优化

文章目录 静态数组动态数组代码背景第一种打印方式&#xff1a;使用 for 循环和索引解释 第二种打印方式&#xff1a;使用基于范围的 for 循环解释改进方式&#xff1a;避免拷贝 总结清理数组 代码示例代码分析输出结果总结 代码示例代码详解总结使用 reserve 的优点:使用 empl…