【Java生态圈技术总结】之深度剖析MapStruct对象拷贝工具

简介: 属性拷贝工具有很多,也许你用过如下的一些:Apache commons-beanutilsSpring BeanUtilscglib BeanCopierHuTool BeanUtilsMapStructgetter & setter这些属性拷贝工具各自有什么特点和区别?在日常开发使用中,我们该如何做出选择?

目录导航
一、常用的对象拷贝工具基本介绍
1.1 Apache BeanUtils
1.2 Spring BeanUtils
1.3 cglib BeanCopier
1.4 HuTool BeanUtils
1.5 MapStruct
1.6 getter & setter
1.7 总结
二、使用介绍
2.1 准备工作
2.2 映射
2.2.1 基本映射
2.2.2 不同属性名映射
2.2.3 不同个数属性映射
2.2.4 多个源合并映射
2.2.5 子对象映射
2.2.6 集合属性映射
2.2.7 枚举映射
2.2.8 集合映射
2.3 转换
2.3.1 类型转换
2.3.2 格式转换
2.4 高级特性
2.4.1 依赖注入
2.4.2 设置默认值
2.4.3 使用表达式
2.4.4 前置及后置方法
三、参考文献
四、补充填坑
一、常用的对象拷贝工具基本介绍
属性拷贝工具有很多,也许你用过如下的一些:

Apache commons-beanutils
Spring BeanUtils
cglib BeanCopier
HuTool BeanUtils
MapStruct
getter & setter
这些属性拷贝工具各自有什么特点和区别?在日常开发使用中,我们该如何做出选择?

1.1 Apache BeanUtils
参数顺序和其它的工具正好相反,导致使用不顺手,容易产生问题;
阿里巴巴代码扫描插件会给出明确的告警;
基于反射实现,性能较差;
不推荐使用;
1.2 Spring BeanUtils
基于内省+反射,借助getter/setter方法实现属性拷贝,性能比apache高;
在简单的属性拷贝场景下推荐使用;
1.3 cglib BeanCopier
通过动态代理的方式来实现属性拷贝;
性能高效;
在简单的属性拷贝场景下推荐使用;
1.4 HuTool BeanUtils
性能介于apache和Spring之间;
需要额外引入HuTool的依赖;
1.5 MapStruct
基于getter/setter方法实现属性拷贝,在编译时自动生成实现类的代码;
性能媲美getter & setter;
强大的功能可以实现深度拷贝;
缺点是需要声明bean的转换接口类;
1.6 getter & setter
性能最高,但是需要手动拷贝;
1.7 总结
经过第三方的对比结果,总的下来,推荐使用顺序为:

apache < HuTool < Spring < cglib < Mapstruct

二、使用介绍
2.1 准备工作

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>


...

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
            <source>1.8</source>
            <target>1.8</target>
            <annotationProcessorPaths>
                <path>
                    <groupId>org.mapstruct</groupId>
                    <artifactId>mapstruct-processor</artifactId>
                    <version>${org.mapstruct.version}</version>
                </path>
            </annotationProcessorPaths>
        </configuration>
    </plugin>
</plugins>


2.2 映射
2.2.1 基本映射
我们现在需要实现一个场景,Car是一个domain层的对象实例,在从数据库读取出来后传递给service层需要转换为CarDTO,这两个实例的所有属性全部相同,现在需要使用mapstruct来完成这个目标。

public class Car {

private String brand;
private Double price;
private Boolean onMarket;
...
// setters + getters + toString

}
public class CarDTO {

private String brand;
private Double price;
private Boolean onMarket;
...
// setters + getters + toString

}
我们需要新建一个Mapper接口,来映射这两个对象之间的属性。

@Mapper
public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

CarDTO carToCarDTO(Car car);

}
然后就可以进行测试了:

@Test
public void test1(){

Car wuling = new Car();
wuling.setBrand("wuling");
wuling.setPrice(6666.66);
wuling.setOnMarket(true);

CarDTO wulingDTO = CarMapper.INSTANCE.carToCarDTO(wuling);
// 结果为:Car{brand='wuling', price=6666.66, onMarket=true}
System.out.println("结果为:" + wulingDTO);

}
可以看到,mapstruct很好地完成了我们的目标,那么它是如何做到的呢?我们查看CarMapper.INSTANCE.carToCarDTO(wuling)的实现类,可以看到在编译过程中自动生成了如下内容的接口实现类:

public class CarMapperImpl implements CarMapper {

@Override
public CarDTO carToCarDTO(Car car) {
    if ( car == null ) {
        return null;
    }

    CarDTO carDTO = new CarDTO();

    carDTO.setBrand( car.getBrand() );
    carDTO.setPrice( car.getPrice() );
    carDTO.setOnMarket( car.getOnMarket() );

    return carDTO;
}

}
所以,mapstruct并没有使用反射的机制,而是使用了普通的set和get方法来进行属性拷贝的,因此要求我们的对象也一定要有set和get方法。

2.2.2 不同属性名映射
在如上示例中,我们源对象和目标对象的属性名称全都一致,但是在很多的场景下,源对象和目标对象的同一个字段很可能名称是不同的,这种情况下,只需要在映射接口类中指定即可:

public class Car {

...
private Boolean onMarket;
...
// setters + getters + toString

}
public class CarDTO {

...
private Boolean onSale;
...
// setters + getters + toString

}
@Mapper
public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = "car.onMarket", target = "onSale")
CarDTO carToCarDTO(Car car);

}
如此,生成的接口实现类如下:

@Override
public CarDTO carToCarDTO(Car car) {

if ( car == null ) {
    return null;
}

CarDTO carDTO = new CarDTO();

carDTO.setOnSale( car.getOnMarket() );
carDTO.setBrand( car.getBrand() );
carDTO.setPrice( car.getPrice() );

return carDTO;

}
2.2.3 不同个数属性映射
我们假设Car和CarDTO各有一个对方没有的属性,那么在进行对象拷贝时会发生什么?

public class Car {

...
private Date birthdate;
// setters + getters + toString

}
public class CarDTO {

...
private String owner;
// setters + getters + toString

}
@Test
public void test1(){

Car wuling = new Car();
wuling.setBrand("wuling");
wuling.setPrice(6666.66);
wuling.setOnMarket(true);
wuling.setBirthdate(new Date());

CarDTO wulingDTO = CarMapper.INSTANCE.carToCarDTO(wuling);
System.out.println("结果为:" + wulingDTO);

}
然后我们执行如上转换的案例,发现并没有报错,从Car拷贝属性到CarDTO时,CarDTO由于没有birthdate属性,则不会赋值;同时,CarDTO的owner因为Car中没有,因此也不会被赋值,生成的接口实现类如下:

@Override
public CarDTO carToCarDTO(Car car) {

if ( car == null ) {
    return null;
}

CarDTO carDTO = new CarDTO();

carDTO.setOnSale( car.getOnMarket() );
carDTO.setBrand( car.getBrand() );
carDTO.setPrice( car.getPrice() );

return carDTO;

}
因此,mapstruct只会对共有的交集属性进行拷贝操作。

2.2.4 多个源合并映射
我们新增一个Person类,其中的name属性对应CarDTO中的owner属性。

public class Person {

private String name;
private String age;
// setters + getters + toString

}
@Mapper
public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = "car.onMarket", target = "onSale")
@Mapping(source = "person.name", target = "owner")
CarDTO carToCarDTO(Car car, Person person);

}
public class TestMapper1 {

@Test
public void test1(){
    Car wuling = new Car();
    wuling.setBrand("wuling");
    wuling.setPrice(6666.66);
    wuling.setOnMarket(true);
    wuling.setBirthdate(new Date());

    Person jack = new Person();
    jack.setName("jack");
    jack.setAge("22");

    CarDTO wulingDTO = CarMapper.INSTANCE.carToCarDTO(wuling, jack);
    // 结果为:CarDTO{brand='wuling', price=6666.66, onSale=true, owner='jack'}
    System.out.println("结果为:" + wulingDTO);
}

自动生成的接口实现类如下:

@Override
public CarDTO carToCarDTO(Car car, Person person) {

if ( car == null && person == null ) {
    return null;
}

CarDTO carDTO = new CarDTO();

if ( car != null ) {
    carDTO.setOnSale( car.getOnMarket() );
    carDTO.setBrand( car.getBrand() );
    carDTO.setPrice( car.getPrice() );
}
if ( person != null ) {
    carDTO.setOwner( person.getName() );
}

return carDTO;

}
2.2.5 子对象映射
如果需要转换的Car对象中的某个属性不是基本数据类型,而是一个对象怎么处理呢。

public class Person {

private String name;

private String age;

// setters + getters + toString

}

public class PersonDTO {

private String name;

private String age;

// setters + getters + toString

}
public class Car {

private String brand;
private Double price;
private Boolean onMarket;
private Person owner;

// setters + getters + toString

}

public class CarDTO {

private String brand;
private Double price;
private Boolean onMarket;
private PersonDTO owner;

// setters + getters + toString

}
@Mapper
public interface PersonMapper {

PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

PersonDTO personToPersonDTO(Person person);

}
@Mapper(uses = {PersonMapper.class})
public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = "onMarket", target = "onSale")
CarDTO carToCarDTO(Car car);

}
@Test
public void test1(){

Person jack = new Person();
jack.setName("jack");
jack.setAge("22");

Car wuling = new Car();
wuling.setBrand("wuling");
wuling.setPrice(6666.66);
wuling.setOnMarket(true);
wuling.setOwner(jack);

CarDTO wulingDTO = CarMapper.INSTANCE.carToCarDTO(wuling);
// 结果为:CarDTO{brand='wuling', price=6666.66, onSale=true, owner=Person{name='jack', age='22'}}
System.out.println("结果为:" + wulingDTO);

}
这里最重要的是:

需要增加PersonMapper接口,让mapstruct能够对Person和PersonDTO进行转换;
CarMapper中需要引入PersonMapper,如果存在多个对象属性,此处就要引入多个对象属性的Mapper接口;
2.2.6 集合属性映射
如果需要转换的Car对象中的某个属性不是基本数据类型,而是一个集合类型该怎么处理?

public class Car {

private String brand;
private Double price;
private Boolean onMarket;
private List<Person> ownerList;

// setters + getters + toString

}
同2.2.5内容。

2.2.7 枚举映射
枚举映射的工作方式与字段映射相同。MapStruct会对具有相同名称的枚举进行映射。

public enum PayType {

CASH,
ALIPAY,
WEPAY,
DIGITAL_CASH,
CARD_VISA,
CARD_CREDIT;

}

public enum PayTypeNew {

CASH,
ALIPAY,
WEPAY,
DIGITAL_CASH,
CARD_VISA,
CARD_CREDIT;

}
@Mapper
public interface PayTypeMapper {

PayTypeMapper INSTANCE = Mappers.getMapper(PayTypeMapper.class);

PayTypeNew payTypeToPayTypeNew(PayType payType);

}
@Test
public void test2(){

PayType p1 = PayType.ALIPAY;

PayTypeNew p2 = PayTypeMapper.INSTANCE.payTypeToPayTypeNew(p1);
// 结果为:ALIPAY
System.out.println("结果为:" + p2);

}
但是在更多的场景下,源枚举和目标枚举并不是一一对应的,比如目标枚举如下:

public enum PayTypeNew {

CASH,
NETWORK,
CARD;

}
此时,我们就需要手动指定源枚举和目标枚举之间的对应关系:

@Mapper
public interface PayTypeMapper {

PayTypeMapper INSTANCE = Mappers.getMapper(PayTypeMapper.class);

@ValueMappings({
        @ValueMapping(source = "ALIPAY", target = "NETWORK"),
        @ValueMapping(source = "WEPAY", target = "NETWORK"),
        @ValueMapping(source = "DIGITAL_CASH", target = "CASH"),
        @ValueMapping(source = "CARD_VISA", target = "CARD"),
        @ValueMapping(source = "CARD_CREDIT", target = "CARD")
})
PayTypeNew payTypeToPayTypeNew(PayType payType);

}
如果对应CARD的场景比较多,手动一个个地对应会比较繁琐,因此还有一种方式能实现相同的效果,而且比较简洁:

@Mapper
public interface PayTypeMapper {

PayTypeMapper INSTANCE = Mappers.getMapper(PayTypeMapper.class);

@ValueMappings({
        @ValueMapping(source = "ALIPAY", target = "NETWORK"),
        @ValueMapping(source = "WEPAY", target = "NETWORK"),
        @ValueMapping(source = "DIGITAL_CASH", target = "CASH"),
        @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "CARD")
})
PayTypeNew payTypeToPayTypeNew(PayType payType);

}
MappingConstants.ANY_REMAINING表示剩下其它的源枚举和目标枚举对应不上的全部映射为指定的枚举对象。

还有一种方式,使用MappingConstants.ANY_UNMAPPED表示所有未显示指定目标枚举的都会被映射为CARD:

@Mapper
public interface PayTypeMapper {

PayTypeMapper INSTANCE = Mappers.getMapper(PayTypeMapper.class);

@ValueMappings({
        @ValueMapping(source = "ALIPAY", target = "NETWORK"),
        @ValueMapping(source = "WEPAY", target = "NETWORK"),
        @ValueMapping(source = "DIGITAL_CASH", target = "CASH"),
        @ValueMapping(source = MappingConstants.ANY_UNMAPPED, target = "CARD")
})
PayTypeNew payTypeToPayTypeNew(PayType payType);

}
2.2.8 集合映射
如果源对象和目标对象都是集合,且对象中的属性都是基本数据类型,则映射方法和之前类似,映射接口改为如下即可:

@Mapper
public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

List<CarDTO> carToCarDTOList(List<Car> carList);

Set<CarDTO> carToCarDTOSet(Set<Car> carSet);

Map<String, CarDTO> carToCarDTOMap(Map<String, Car> carMap);

}
如果对象中属性不仅是基本数据类型,还有对象类型或对象类型的集合类型的话,mapstruct也是支持映射的,详情参考官网文档,此处不再赘述。

2.3 转换
2.3.1 类型转换
mapstruct提供了基本数据类型和包装数据类型、一些常见场景下的自动转换;

基本类型及其对应的包装类型之间的转换;比如int和Integer、float和Float、long和Long、boolean和Boolean等;
任意基本类型和任意包装类型之间的转换;比如int和long、byte和Integer等;
任意基本类型、包转类型和String之间的转换;比如boolean和String、Integer和String;
枚举和String;
大数类型(BigInteger、BigDecimal)、基本类型、基本类型包装类型、String之间的相互转换;
其它一些场景,参考MapStruct 1.4.2.Final Reference Guide;
2.3.2 格式转换
mapstruct可以对源对象的属性值进行格式化之后拷贝给目标对象的属性;

日期格式转换
public class Car {

private String brand;
private Double price;
private LocalDate marketDate;
// setters + getters + toString

}

public class CarDTO {

private String brand;
private Double price;
private String saleDate;
// setters + getters + toString

}
@Mapper
public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = "marketDate", target = "saleDate", dateFormat = "dd/MM/yyyy")
CarDTO carToCarDTO(Car car);

}
@Test
public void test1(){

Car wuling = new Car();
wuling.setBrand("wuling");
wuling.setPrice(6666.66);
wuling.setMarketDate(LocalDate.now());

// 转换前为:Car{brand='wuling', price=6666.66, marketDate=2022-01-19}
System.out.println("转换前为:" + wuling);
CarDTO wulingDTO = CarMapper.INSTANCE.carToCarDTO(wuling);
// 结果为:CarDTO{brand='wuling', price=6666.66, saleDate='19/01/2022'}
System.out.println("结果为:" + wulingDTO);

}
数字格式转换
@Mapper
public interface CarMapper {

CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

@Mapping(source = "price", target = "price", numberFormat = "$#.00")
CarDTO carToCarDTO(Car car);

}
2.4 高级特性
2.4.1 依赖注入
在前面例子中的Mapper映射接口中,我们都需要CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);来创建一个实例,如果我们是Spring工程的话,就可以把这个实例托管给Spring进行管理。

@Mapper(componentModel = "spring")
public interface CarMapper {

List<CarDTO> carToCarDTOList(List<Car> carList);

}
然后使用的时候,从Spring中自动注入接口对象即可:

@SpringBootTest
public class TestMapper1 {

@Autowired
private CarMapper carMapper;

@Test
public void test1(){
    Car wuling = new Car();
    wuling.setBrand("wuling");
    wuling.setPrice(6666.66);
    wuling.setMarketDate(LocalDate.now());
    Car changan = new Car();
    changan.setBrand("changan");
    changan.setPrice(7777.77);
    changan.setMarketDate(LocalDate.now());
    List<Car> carList = new ArrayList<>();
    carList.add(wuling);
    carList.add(changan);

    List<CarDTO> carDTOList = carMapper.carToCarDTOList(carList);
    System.out.println("结果为:" + carDTOList);
}

}
2.4.2 设置默认值
常量默认值
无论源对象的属性字段值是什么,目标对象的该字段都是给定的常量值。

@Mapper(componentModel = "spring")
public interface PersonMapper {

@Mapping(target = "name", constant = "zhangsan")
PersonDTO personToPersonDTO(Person person);

}
空值默认值
如果源对象的属性字段值为空,那么就使用指定的默认值。

@Mapper(componentModel = "spring")
public interface PersonMapper {

@Mapping(source = "name", target = "name", defaultValue = "unknown")
PersonDTO personToPersonDTO(Person person);

}
2.4.3 使用表达式
mapstruct甚至允许在对象属性映射中使用java表达式:

@Mapper(componentModel = "spring", imports = {UUID.class, LocalDateTime.class})
public interface PersonMapper {

@Mapping(target = "id", expression = "java(UUID.randomUUID().toString())")
@Mapping(source = "birthdate", target = "birthdate", defaultExpression = "java(LocalDateTime.now())")
PersonDTO personToPersonDTO(Person person);

}
或者等价写法为:

@Mapper(componentModel = "spring")
public interface PersonMapper {

@Mapping(target = "id", expression = "java(java.util.UUID.randomUUID().toString())")
@Mapping(source = "birthdate", target = "birthdate", defaultExpression = "java(java.time.LocalDateTime.now())")
PersonDTO personToPersonDTO(Person person);

}
2.4.4 前置及后置方法
@Mapper(componentModel = "spring")
public abstract class PersonMapper {

@BeforeMapping
public void before(Person person){
    System.out.println("前置处理!!!");
    if(ObjectUtils.isEmpty(person.getName())){
        System.out.println("Person的name不能为空!");
        return;
    }
}

@Mapping(target = "id", expression = "java(java.util.UUID.randomUUID().toString())")
@Mapping(source = "birthdate", target = "birthdate", defaultExpression = "java(java.time.LocalDateTime.now())")
public abstract PersonDTO personToPersonDTO(Person person);

@AfterMapping
public void after(@MappingTarget PersonDTO personDTO){
    System.out.println("后置处理:" + personDTO.getName() + "!!!");
}

}
三、参考文献
MapStruct 1.4.2.Final Reference Guide

MapStruct使用指南 - 知乎 (zhihu.com)

常见Bean拷贝框架使用姿势及性能对比 - 知乎 (zhihu.com)

四、补充填坑
在正文的实例中,我们对于Bean对象都是使用手动写getter、setter、toString方法的,但是在真实开发中,大家都是采用了Lombok插件,如果不做特殊配置,就会出现mapstruct运行时lombok不生效的问题,只需要在pom配置中增加如下内容:


<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>



具体原理和详情可以参考:当 Lombok 遇见了 MapStruct の「坑」 - 知乎 (zhihu.com)

相关文章
|
2月前
|
监控 Cloud Native Java
Quarkus 云原生Java框架技术详解与实践指南
本文档全面介绍 Quarkus 框架的核心概念、架构特性和实践应用。作为新一代的云原生 Java 框架,Quarkus 旨在为 OpenJDK HotSpot 和 GraalVM 量身定制,显著提升 Java 在容器化环境中的运行效率。本文将深入探讨其响应式编程模型、原生编译能力、扩展机制以及与微服务架构的深度集成,帮助开发者构建高效、轻量的云原生应用。
358 44
|
1月前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
271 0
|
2月前
|
人工智能 缓存 监控
使用LangChain4j构建Java AI智能体:让大模型学会使用工具
AI智能体是大模型技术的重要演进方向,它使模型能够主动使用工具、与环境交互,以完成复杂任务。本文详细介绍如何在Java应用中,借助LangChain4j框架构建一个具备工具使用能力的AI智能体。我们将创建一个能够进行数学计算和实时信息查询的智能体,涵盖工具定义、智能体组装、记忆管理以及Spring Boot集成等关键步骤,并展示如何通过简单的对话界面与智能体交互。
933 1
|
2月前
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
370 1
|
3月前
|
安全 Java 编译器
new出来的对象,不一定在堆上?聊聊Java虚拟机的优化技术:逃逸分析
逃逸分析是一种静态程序分析技术,用于判断对象的可见性与生命周期。它帮助即时编译器优化内存使用、降低同步开销。根据对象是否逃逸出方法或线程,分析结果分为未逃逸、方法逃逸和线程逃逸三种。基于分析结果,编译器可进行同步锁消除、标量替换和栈上分配等优化,从而提升程序性能。尽管逃逸分析计算复杂度较高,但其在热点代码中的应用为Java虚拟机带来了显著的优化效果。
134 4
|
3月前
|
Java API Maven
2025 Java 零基础到实战最新技术实操全攻略与学习指南
本教程涵盖Java从零基础到实战的全流程,基于2025年最新技术栈,包括JDK 21、IntelliJ IDEA 2025.1、Spring Boot 3.x、Maven 4及Docker容器化部署,帮助开发者快速掌握现代Java开发技能。
811 1
|
1月前
|
人工智能 监控 Java
Java与AI智能体:构建自主决策与工具调用的智能系统
随着AI智能体技术的快速发展,构建能够自主理解任务、制定计划并执行复杂操作的智能系统已成为新的技术前沿。本文深入探讨如何在Java生态中构建具备工具调用、记忆管理和自主决策能力的AI智能体系统。我们将完整展示从智能体架构设计、工具生态系统、记忆机制到多智能体协作的全流程,为Java开发者提供构建下一代自主智能系统的完整技术方案。
387 4
|
2月前
|
人工智能 Java API
Java AI智能体实战:使用LangChain4j构建能使用工具的AI助手
随着AI技术的发展,AI智能体(Agent)能够通过使用工具来执行复杂任务,从而大幅扩展其能力边界。本文介绍如何在Java中使用LangChain4j框架构建一个能够使用外部工具的AI智能体。我们将通过一个具体示例——一个能获取天气信息和执行数学计算的AI助手,详细讲解如何定义工具、创建智能体并处理执行流程。本文包含完整的代码示例和架构说明,帮助Java开发者快速上手AI智能体的开发。
1020 8
|
3月前
|
Java 测试技术 API
2025 年 Java 开发者必知的最新技术实操指南全览
本指南涵盖Java 21+核心实操,详解虚拟线程、Spring Boot 3.3+GraalVM、Jakarta EE 10+MicroProfile 6微服务开发,并提供现代Java开发最佳实践,助力开发者高效构建高性能应用。
652 4
|
3月前
|
缓存 安全 Java
Java反射机制:动态操作类与对象
Java反射机制是运行时动态操作类与对象的强大工具,支持获取类信息、动态创建实例、调用方法、访问字段等。它在框架开发、依赖注入、动态代理等方面有广泛应用,但也存在性能开销和安全风险。本文详解反射核心API、实战案例及性能优化策略,助你掌握Java动态编程精髓。