开源轮子 - Bean转换

news/2024/12/26 4:15:49/

开源轮子 - Bean转换

文章目录

  • 开源轮子 - Bean转换
    • 一:JavaBean问题
    • 二:MapStruct
      • 1:入门使用
        • 1.1:依赖引入
        • 1.2:po,vo
        • 1.3:mapper
        • 1.4:测试一下
      • 2:自定义映射
      • 3:映射方法级别
      • 4:其他映射说明
        • 4.1:集合映射
        • 4.2:枚举映射
        • 4.3:构造函数映射
        • 4.4:嵌套属性映射
        • 4.5:反向映射
        • 4.6:多映射规则
        • 4.7:默认值映射
      • 5:@BeforeMapping和@AfterMapping
      • 6:@MapperConfig配置全局映射策略
      • 7:@IterableMapping和@MapMapping处理集合
      • 8:@MapMapping 注解来处理 Map 类型的映射
      • 9:@Qualifier自定义映射方法选择
    • 三:Orika
      • 1:什么是Orika
      • 2:配置类构建
      • 3:调用过程

一:JavaBean问题

在开发的时候经常会有业务代码之间有很多的 JavaBean 之间的相互转化,比如PO/DTO/VO/QueryParam之间的转换问题。

拷贝技术

  • org.apache.commons.beanutils.PropertyUtils.copyProperties
  • org.apache.commons.beanutils.BeanUtils.copyProperties
  • org.springframework.beans.BeanUtils.copyProperties
  • net.sf.cglib.beans.BeanCopier

纯get/set

  • 辅助IDE插件拷贝对象时可以自动set所有方法字段 (这种方式可能有些开发人员不清楚)
  • 不仅看上去冗余添加新的字段时依然需要手动
  • 开发效率比较低

二:MapStruct

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

MapStruc主要特性

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

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

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

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

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

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

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

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

1、使用方式:

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

2、性能:

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

3、灵活性和安全性:

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

1:入门使用

MapSturct 是一个生成类型安全,高性能且无依赖的 JavaBean 映射代码的注解处理器(annotation processor)。

工具可以帮我们实现 JavaBean 之间的转换, 通过注解的方式。

同时, 作为一个工具类,相比于手写, 其应该具有便捷, 不容易出错的特点

下面展示最基本的PO转VO的例子,使用的是IDEA + Lombok + MapStruct

1.1:依赖引入
<!-- map struct -->
<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.5.5.Final</version>
</dependency><!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version>
</dependency><!-- fast json -->
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version>
</dependency><!-- junit -->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope>
</dependency><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version><configuration><source>1.8</source><target>1.8</target><!-- 添加注解处理器路径 --><annotationProcessorPaths><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.4.2.Final</version></path></annotationProcessorPaths></configuration></plugin></plugins>
</build>
1.2:po,vo
java">package org.example.mapStruct;import lombok.Data;
import lombok.experimental.Accessors;import java.time.LocalDate;
import java.time.LocalDateTime;/*** <p>* 功能描述:User* </p>** @author cui haida* @date 2024/04/16/19:29*/
@Data
@Accessors(chain = true)
public class User {private Long id;private String username;private String password; // 密码private Integer sex;  // 性别private LocalDate birthday; // 生日private LocalDateTime createTime; // 创建时间private String config; // 其他扩展信息,以JSON格式存储
}
java">package org.example.mapStruct;import lombok.Data;
import lombok.experimental.Accessors;import java.time.LocalDate;
import java.util.List;/*** <p>* 功能描述:* </p>** @author cui haida* @date 2024/04/16/19:34*/
@Data
@Accessors(chain = true)
public class UserVo {private Long id;private String username;private String password;private Integer gender;private LocalDate birthday;private String createTime;private List<UserConfig> config;@Datapublic static class UserConfig {private String field1;private Integer field2;}
}
1.3:mapper

核心的映射关系都在这里

java">package org.example.mapStruct;import com.alibaba.fastjson.JSON;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;import java.util.List;/*** <p>* 功能描述:java bean converter interface* </p>** @author cui haida* @date 2024/04/16/19:35*/
@Mapper
public interface UserConverter {UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);@Mapping(target = "gender", source = "sex")@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")UserVo do2vo(User user);@Mapping(target = "sex", source = "gender")@Mapping(target = "password", ignore = true)@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")User vo2do(UserVo userVo);List<UserVo> do2voList(List<User> userList);// po 的 config json string -> vo 的 config内部类default List<UserVo.UserConfig> strConfigToListUserConfig(String config) {return JSON.parseArray(config, UserVo.UserConfig.class);}default String listUserConfigToStrConfig(List<UserVo.UserConfig> list) {return JSON.toJSONString(list);}
}
1.4:测试一下
java">@Test
public void do2VoTest() {User user = new User().setId(1L).setUsername("zhangsan").setSex(1).setPassword("abc123").setCreateTime(LocalDateTime.now()).setBirthday(LocalDate.of(1999, 9, 27)).setConfig("[{\"field1\":\"Test Field1\",\"field2\":500}]");UserVo userVo = UserConverter.INSTANCE.do2vo(user);// assetassertNotNull(userVo);assertEquals(userVo.getId(), user.getId());// printSystem.out.println(user);System.out.println(userVo);
}Test
public void vo2DoTest() {UserVo.UserConfig userConfig = new UserVo.UserConfig();userConfig.setField1("Test Field1");userConfig.setField2(500);UserVo userVo = new UserVo().setId(1L).setUsername("zhangsan").setGender(2).setCreateTime("2020-01-18 15:32:54").setBirthday(LocalDate.of(1999, 9, 27)).setConfig(Collections.singletonList(userConfig));User user = UserConverter.INSTANCE.vo2Do(userVo);// assetassertNotNull(userVo);assertEquals(userVo.getId(), user.getId());// printSystem.out.println(user);System.out.println(userVo);
}

MapStruct 来生成的代码,其类似于人手写。 速度上可以得到保证。

总结下,大概流程如下

  1. 导入依赖
  2. 定义mapper
  3. 构建mapper实例,并使用

2:自定义映射

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

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

java">@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: 这个参数允许你引用一个具有@Named注解的方法作为自定义的映射逻辑。例如:

java">@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方法来获取全名。

3:映射方法级别

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

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

resultType

这个参数允许你指定映射方法的返回类型。这在目标类型可以是多个实现类时非常有用。
如果目标类型有多个实现类,并且你希望在映射时使用特定的实现类。通过指定resultType,你可以确保生成的映射代码使用正确的目标类型

java">@BeanMapping(resultType = CarDto.class)
CarDto map(Car car);

qualifiedBy和qualifiedByName

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

java">@BeanMapping(qualifiedByName = "fullName")
PersonDto personToPersonDto(Person person);@Named("fullName")
default String customerToString(Customer customer) {return customer.getFirstName() + " " + customer.getLastName();
}

ignoreByDefault

这个参数允许你忽略所有未明确映射的属性。然后,你可以使用@Mapping注解来明确需要映射的属性。

java">@BeanMapping(ignoreByDefault = true)
@Mapping(target = "name", source = "fullName")
PersonDto personToPersonDto(Person person);

nullValuePropertyMappingStrategy

这个参数允许你指定当源属性为null时应如何处理目标属性。例如,你可以选择是否在源属性为null时调用目标的setter方法。

java">@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
PersonDto personToPersonDto(Person person);

4:其他映射说明

4.1:集合映射

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

java">@Mapper
public interface CarMapper {List<CarDto> carsToCarDtos(List<Car> cars);
}
4.2:枚举映射

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

java">public enum SourceEnum {TYPE_A,TYPE_B,TYPE_C
}public enum TargetEnum {TYPE_X,TYPE_Y,TYPE_Z
}
java">@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 或未映射的情况。例如:

java">@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);
}
4.3:构造函数映射

从MapStruct 1.5开始,你可以使用构造函数来创建目标对象。你只需要在你的目标类中定义一个合适的构造函数,MapStruct就会自动使用它。

java">public class CarDto {private final String make;private final int seatCount;public CarDto(String make, int seatCount) {this.make = make;this.seatCount = seatCount;}// getters
}@Mapper
public interface CarMapper {CarDto carToCarDto(Car car);
}

MapStruct将使用CarDto的构造函数来创建新的CarDto实例。

4.4:嵌套属性映射

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

java">@Mapper
public interface CarMapper {@Mapping(source = "engine.horsePower", target = "horsePower")CarDto carToCarDto(Car car);
}
4.5:反向映射

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

java">@Mapper
public interface CarMapper {CarDto carToCarDto(Car car);@InheritInverseConfigurationCar carDtoToCar(CarDto carDto);
}
4.6:多映射规则
java">@Mapper
public interface CarMapper {@Mappings({@Mapping(source = "numberOfSeats", target = "seatCount"),@Mapping(source = "manufacturingDate", target = "year")})CarDto carToCarDto(Car car);
}
4.7:默认值映射
java">@Mapper
public interface CarMapper {@Mapping(target = "seatCount", source = "numberOfSeats", defaultValue = "4")CarDto carToCarDto(Car car);
}

5:@BeforeMapping和@AfterMapping

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

java">@Mapper
public abstract class CarMapper {@BeforeMappingprotected void enrichCar(Car car) {car.setMake(car.getMake().toUpperCase());}@Mapping(source = "numberOfSeats", target = "seatCount")public abstract CarDto carToCarDto(Car car);@AfterMappingprotected void enrichDto(Car car, @MappingTarget CarDto carDto) {carDto.setSeatCount(car.getNumberOfSeats());}
}

6:@MapperConfig配置全局映射策略

@MapperConfig 注解在 MapStruct 中用于定义全局或共享的映射配置。这个注解可以被应用在接口或抽象类上,然后其他的 @Mapper 可以通过 config 属性引用这个配置。
下面是一个简单的例子:
首先,我们定义一个全局的映射配置:

java">@MapperConfig(componentModel = "spring", uses = DateMapper.class)
public interface GlobalMapperConfig {
}

在这个例子中,我们指定了 componentModel 为 “spring”,这意味着生成的映射器将是 Spring 的组件,可以使用 @Autowired 进行注入。我们还指定了 uses 属性为 DateMapper.class,这意味着所有引用这个配置的映射器都可以使用 DateMapper 中定义的方法进行映射。
然后,我们创建一个映射器并引用这个全局配置:

java">@Mapper(config = GlobalMapperConfig.class)
public interface MyMapper {// mapping methods
}

在这个例子中,MyMapper 将继承 GlobalMapperConfig 中定义的所有配置。
注意,如果 @Mapper 和 @MapperConfig 中都定义了相同的属性,那么 @Mapper 中的属性将会覆盖 @MapperConfig 中的属性。

@MapperConfig 是一种强大的工具,可以帮助你减少重复的配置,并使你的代码更易于维护

7:@IterableMapping和@MapMapping处理集合

当处理集合和映射时,你可能需要特定的转换规则。@IterableMapping 注解的作用是定义一个方法,用于将一个 Iterable 类型的源对象集合映射为目标对象集合。

具体来说,@IterableMapping 注解用于标记一个接口方法,该方法的参数类型为源对象集合,返回类型为目标对象集合。在生成的映射代码中,MapStruct 会将每个源对象映射为一个目标对象,并将它们添加到目标对象集合中。需要注意的是,源对象集合和目标对象集合的元素类型可以不同,此时需要手动指定元素类型转换方式。

@IterableMapping 注解还有一些属性,用于配置映射的行为,例如:

  • qualifiedBy:用于指定一个限定符注解,当存在多个映射器时,可以使用该属性来选择特定的映射器。
  • elementTargetType:用于指定目标对象集合的元素类型。
  • nullValueMappingStrategy:用于处理源对象集合中包含空对象或者 null 值的情况。

一个示例的 @IterableMapping 注解的使用方式如下所示:

java">@Mapper
public interface UserMapper {@IterableMapping(nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)List<UserDTO> toUserDTOList(Iterable<User> users);
}

5
上述代码中,UserMapper 接口中的 toUserDTOList 方法使用了 @IterableMapping 注解,用于将 User 集合转换为 UserDTO 集合。其中,nullValueMappingStrategy 属性指定当源对象集合中包含空对象或者 null 值时,返回默认值。

8:@MapMapping 注解来处理 Map 类型的映射

@MapMapping 注解用于方法级别,指示 MapStruct 如何映射 Map 类型的属性。你可以在映射器接口中的方法上使用该注解,并提供一些配置选项。

java">@Mapper
public interface CarMapper {@MapMapping(keyTargetType = String.class, valueTargetType = CarDto.class)Map<String, CarDto> carsToCarDtos(Map<String, Car> cars);
}

在这个示例中,我们定义了一个名为 CarMapper 的映射器接口,并使用了 @Mapper 注解将它标记为 MapStruct 映射器。

然后,我们在 carsToCarDtos 方法上使用了 @MapMapping 注解,并提供了以下配置选项:

keyTargetType = String.class:指定目标键类型为 String。这会告诉 MapStruct 将源 Map 的键映射为 String 类型。
valueTargetType = CarDto.class:指定目标值类型为 CarDto。这会告诉 MapStruct 将源 Map 的值映射为 CarDto 类型。
通过这样配置 @MapMapping 注解,MapStruct 将自动生成适当的映射代码,按照指定的映射规则将源 Map 中的键值对映射到目标 Map。

需要注意的是,如果你的映射逻辑更加复杂,可以在 @MapMapping 注解的方法参数中提供自定义的转换器。例如:

java">@Mapper
public interface CarMapper {@MapMapping(keyTargetType = String.class, valueTargetType = CarDto.class,keyQualifiedBy = {ToUpperCase.class}, valueQualifiedBy = {ConvertValue.class})Map<String, CarDto> carsToCarDtos(Map<String, Car> cars);@Qualifier@Target(ElementType.METHOD)@Retention(RetentionPolicy.CLASS)public @interface ToUpperCase {}@Qualifier@Target(ElementType.METHOD)@Retention(RetentionPolicy.CLASS)public @interface ConvertValue {Class<?> targetType();}@ToUpperCasepublic String toUpperCase(String key) {return key.toUpperCase();}@ConvertValue(targetType = CarDto.class)public CarDto convertValue(Car car) {// Custom conversion logicreturn new CarDto(car.getMake(), car.getNumberOfSeats());}
}

在这个示例中,我们添加了自定义的转换器。通过使用 keyQualifiedBy 和 valueQualifiedBy 参数,我们可以指定用于键和值的转换器。

我们定义了两个自定义的限定符注解 @ToUpperCase 和 @ConvertValue,并在转换器方法上使用它们。然后,在 carsToCarDtos 方法上分别指定了这两个限定符注解。

这样,当 MapStruct 遇到需要转换键或值的情况时,它将使用相应的转换器方法来进行转换。

9:@Qualifier自定义映射方法选择

@Qualifier 注解用于标识自定义转换器方法和映射过程中的限定符。
通过使用 @Qualifier 注解,你可以为转换器方法或映射方法提供更具体的选择标准,以决定哪个方法应该被调用。

下面是一个示例,展示了如何在 MapStruct 1.5 中使用 @Qualifier 注解:

java">@Mapper
public interface CarMapper {@Qualifier@Target(ElementType.METHOD)@Retention(RetentionPolicy.CLASS)public @interface FastCar {}@Qualifier@Target(ElementType.METHOD)@Retention(RetentionPolicy.CLASS)public @interface ElectricCar {}// 转换器方法1@FastCarCarDto convertToFastCarDto(Car car);// 转换器方法2@ElectricCarCarDto convertToElectricCarDto(Car car);// 映射方法@Mapping(target = "carDto", qualifiedBy = {FastCar.class, ElectricCar.class})GarageDto mapGarageToDto(Garage garage);
}

在这个示例中,我们定义了两个注解 @FastCar 和 @ElectricCar,它们都是通过 @Qualifier 注解来定义的。这些注解用于标识转换器方法 convertToFastCarDto 和 convertToElectricCarDto。

然后,在映射方法 mapGarageToDto 上,我们使用了 qualifiedBy 参数来标记多个限定符。通过这样配置,MapStruct 将根据指定的限定符选择适当的转换器方法来进行映射。

请注意,@Qualifier 注解需要与自定义转换器方法一起使用。你可以根据实际需求定义自己的限定符注解,并将其应用于合适的转换器方法上。

三:Orika

1:什么是Orika

Orika是一个简单、快速的JavaBean拷贝框架,它能够递归地将数据从一个JavaBean复制到另一个JavaBean,这在多层应用开发中是非常有用的

<!-- 是一个Java Bean映射框架,主要用于自动化对象之间的数据转换 -->
<dependency><groupId>ma.glasnost.orika</groupId><artifactId>orika-core</artifactId><version>1.5.4</version>
</dependency>

2:配置类构建

java">package org.example.myeasyexcel.config;import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @author cui haida* 2024/12/23*/
@Configuration
public class OrikaConfig {/*** 这个方法使用了 Spring 的 @Bean 注解,表明它会向 Spring 容器中注册一个名为 mapperFactory 的 Bean。* 该方法创建并返回了一个 DefaultMapperFactory 实例* 它是 Orika 库中用于构建对象映射关系的核心工厂类。* 通过调用其内部静态类 Builder 的 build 方法来完成实例的构造,后续可以基于这个工厂类来定义不同对象之间的映射规则等操作。* @return MapperFactory工厂对象*/@Beanpublic MapperFactory mapperFactory() {return new DefaultMapperFactory.Builder().build();}/*** 这个方法使用了 Spring 的 @Bean 注解,表明它会向 Spring 容器中注册一个名为 mapperFacade 的 Bean。* 该方法创建并返回了一个 MapperFacade 实例,该实例是 Orika 库中用于执行对象映射关系的核心组件。* 它通过调用工厂对象 mapperFactory 来完成实例的构造,后续可以基于这个映射器来完成对象之间的映射操作。* 例如 -> DestinationObject dest = mapperFacade.map(sourceObject, DestinationObject.class);* 将 sourceObject 按照配置好的规则转换为 DestinationObject 类型的对象。* @return MapperFacade映射器对象*/@Beanpublic MapperFacade mapperFacade() {return mapperFactory().getMapperFacade();}
}

3:调用过程

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


http://www.ppmy.cn/news/1558168.html

相关文章

鸿蒙Next API 12开发,使用@ohos/axios进行HTTP请求

创建了一个名为 HttpRequest 的类&#xff0c;它封装了 axios 的功能&#xff0c;并添加了请求和响应拦截器以处理一些通用的请求和响应逻辑。这个类提供了多种 HTTP 方法&#xff08;GET、POST、PUT、DELETE&#xff09;以及用于发送请求并处理响应数据的方法。以下是对您的代…

加电:DETR论文阅读

DETR&#xff1a;End ot End object detection with transformer. 主要工作 这篇文章的主要工作在于&#xff0c;是将transformer引入到目标检测这一类计算机视觉的任务中&#xff0c;transformer的优势在于&#xff1a;模型的通用性和高上限(gpt是最好的例子&#xff0c;算力…

opencv sdk for java中提示无stiching模块接口的问题

1、问题介绍 安卓项目中有新的需求&#xff0c;在 jni 中增加 stiching_detail.cpp 中全景拼接的实现。 但是在编译时&#xff0c;出现大量报错&#xff0c;如下截图所示 实际上&#xff0c;其他opencv的接口函数 例如 core dnn等都能正常使用&#xff0c;直觉上初步怀疑 ope…

简述Git中如何将一个新增文件添加到本地仓库?

在Git中&#xff0c;将一个新增文件添加到本地仓库通常需要以下步骤&#xff1a; 将文件添加到暂存区&#xff1a;首先&#xff0c;你需要使用git add命令来将新文件添加到暂存区。 使用文件名&#xff1a;git add <filename>使用点号添加所有文件&#xff1a;git add .使…

BERT outputs

Yes so BERT (the base model without any heads on top) outputs 2 things: last_hidden_state and pooler_output. 是的&#xff0c;BERT&#xff08;顶部没有任何头部的基础模型&#xff09;输出 2 个东西&#xff1a; last_hidden_state 和 pooler_output 。 First questi…

Scala图书管理系统

项目创建并实现基础UI package org.appimport scala.io.StdInobject Main {def main(args: Array[String]): Unit {var running truewhile (running) {println("欢迎来到我的图书管理系统&#xff0c;请选择")println("1.查看所有图书")println("2…

STM32在bootloader跳转到application时设置MSP

1. 简介 在做bootloader 跳转到application时&#xff0c;经常会看到设置MSP的操作__set_MSP(*(__IO uint32_t*) APPLICATION_ENTRY);。 1.1 MSP的作用 在STM32微控制器中&#xff0c;MSP&#xff08;Main Stack Pointer&#xff0c;主堆栈指针&#xff09;是一个非常重要的…

《第十二部分》1.STM32之RTC实时时钟介绍---BKP实验

本章将介绍一种计数计时的外设 RTC实时时钟-----Whappy STM32提供了4中时钟来源&#xff01; 函数名功能作用void BKP_DeInit(void);复位备份区域寄存器配置&#xff0c;将备份域的所有寄存器恢复到默认状态。void BKP_TamperPinLevelConfig(uint16_t BKP_TamperPinLevel);配置…