# bean-copy-demo **Repository Path**: jjfinal/bean-copy-demo ## Basic Information - **Project Name**: bean-copy-demo - **Description**: orika、cglib、mapstruct 类拷贝示例及性能对比 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2025-10-22 - **Last Updated**: 2025-10-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # bean copy 组件对比 复制组件的使用简介,以及性能对比。 一篇对比文档: baeldung文档: ## orika 官方文档: baeldung: orika - 通过字节码生成技术来进行类的属性复制,因此性能很好 - 同时提供了非常灵活的映射配置,不同名称、不同类型的映射 - 支持深度拷贝 允许进行的配置: - 指定类的映射配置 - 整个类完全自定义转换 - 字段转换 - 非同名字段映射配置 - List映射字段 - Map映射字段 - 内部类映射字段 - 单向映射 - 排除映射 - 空值:是否进行映射处理 - 全局不同字段类型转换 - 全局级别 - 字段级别 映射配置文档: 自定义类型转换器: orika提供了两种获取复制组件的方法,前者性能更好,后者更加通用 两种映射组件都可以反复使用 ```java BoundMapperFacade bound = mapperFactory.getMapperFacade(Source.class, Dest.class); MapperFacade mapper = mapperFactory.getMapperFacade(); ``` 基本使用:如果简单使用,不进行复杂的映射配置,直接获取MapperFacade就可以,不用去进行classMap的注册。 ```java package com.sayyi.demo.bean.copy.orika; import com.sayyi.demo.bean.domain.Dest; import com.sayyi.demo.bean.domain.Source; import ma.glasnost.orika.MapperFacade; import ma.glasnost.orika.impl.DefaultMapperFactory; /** * @author xuchuang * date 2021/12/20 11:11 AM */ public class OrikaDemo { public static final DefaultMapperFactory mapperFactory = new DefaultMapperFactory.Builder().build(); public static void main(String[] args) { // 这个过程创建了一个 ClassMapBuilder 实例,进行特定类的映射配置,然后将其注册到 MapperFactory 中 // 这里可以配置映射关系、转换器等 mapperFactory.classMap(Source.class, Dest.class) // 配置自定义的自动映射关系 .field("name", "name") // 其余字段使用默认映射关系 .byDefault() // 将映射信息注册到 MapperFactory 中。 .register(); // 获取转换组件。线程安全组件 final MapperFacade mapperFacade = mapperFactory.getMapperFacade(); Source source = new Source(); source.setName("aCai"); source.setAge(21); final Dest dest = mapperFacade.map(source, Dest.class); System.out.println(dest); } } ``` ## cglib - 和手写性能差不多 - 只能复制相同名称、相同类型的字段 基本使用:需要自己缓存对应的BeanCopier对象,避免每次都重新生成 ```java package com.sayyi.demo.bean.copy.cglib; import com.sayyi.demo.bean.copy.ParentDemo; import com.sayyi.demo.bean.domain.Dest; import com.sayyi.demo.bean.domain.Source; import net.sf.cglib.beans.BeanCopier; /** * @author xuchuang * date 2021/12/20 2:39 PM */ public class CglibDemo { public static void main(String[] args) { final Source source = ParentDemo.genSource(); final BeanCopier beanCopier = BeanCopier.create(Source.class, Dest.class, false); Dest dest = new Dest(); beanCopier.copy(source, dest, null); System.out.println(dest); } } ``` ## mapstruct 官方网站: - 通过接口注解,生成getset的转换代码 - 性能接近手写 - 可以进行灵活的转换配置 - 不同于orika,mapstruct可以为两个类型设置多种映射关系,更加灵活 - 支持深度拷贝 - 需要配置maven compile插件,会导致与lombok、jmh的冲突 plugin config ```xml org.apache.maven.plugins maven-compiler-plugin 3.8.1 org.mapstruct mapstruct-processor ${org.mapstruct.version} org.projectlombok lombok ${lombok.version} org.openjdk.jmh jmh-generator-annprocess ${jmh.version} ``` 使用: 声明转换接口 ```java @Mapper public interface SourceMapper { SourceMapper INSTANCE = Mappers.getMapper(SourceMapper.class); Dest sourceToDest(Source source); } ``` 调用转换逻辑 ```java Source source = ParentDemo.genSource(); final Dest dest = SourceMapper.INSTANCE.sourceToDest(source); System.out.println(dest); ``` ## JMH测试结果 ``` # JMH version: 1.33 # VM version: JDK 1.8.0_292, OpenJDK 64-Bit Server VM, 25.292-b10 # VM invoker: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/bin/java # VM options: -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=57882:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect) # Warmup: 1 iterations, 5 s each # Measurement: 3 iterations, 5 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.cglib # Run progress: 0.00% complete, ETA 00:01:40 # Fork: 1 of 1 # Warmup Iteration 1: 7.032 ns/op Iteration 1: 6.567 ns/op Iteration 2: 6.172 ns/op Iteration 3: 6.169 ns/op Result "com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.cglib": 6.303 ±(99.9%) 4.177 ns/op [Average] (min, avg, max) = (6.169, 6.303, 6.567), stdev = 0.229 CI (99.9%): [2.125, 10.480] (assumes normal distribution) # JMH version: 1.33 # VM version: JDK 1.8.0_292, OpenJDK 64-Bit Server VM, 25.292-b10 # VM invoker: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/bin/java # VM options: -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=57882:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect) # Warmup: 1 iterations, 5 s each # Measurement: 3 iterations, 5 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.classMap # Run progress: 20.00% complete, ETA 00:01:23 # Fork: 1 of 1 # Warmup Iteration 1: 80.834 ns/op Iteration 1: 160.969 ns/op Iteration 2: 166.946 ns/op Iteration 3: 167.017 ns/op Result "com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.classMap": 164.977 ±(99.9%) 63.338 ns/op [Average] (min, avg, max) = (160.969, 164.977, 167.017), stdev = 3.472 CI (99.9%): [101.639, 228.316] (assumes normal distribution) # JMH version: 1.33 # VM version: JDK 1.8.0_292, OpenJDK 64-Bit Server VM, 25.292-b10 # VM invoker: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/bin/java # VM options: -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=57882:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect) # Warmup: 1 iterations, 5 s each # Measurement: 3 iterations, 5 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.manual # Run progress: 40.00% complete, ETA 00:01:02 # Fork: 1 of 1 # Warmup Iteration 1: 1.808 ns/op Iteration 1: 11.603 ns/op Iteration 2: 11.154 ns/op Iteration 3: 11.147 ns/op Result "com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.manual": 11.301 ±(99.9%) 4.761 ns/op [Average] (min, avg, max) = (11.147, 11.301, 11.603), stdev = 0.261 CI (99.9%): [6.540, 16.063] (assumes normal distribution) # JMH version: 1.33 # VM version: JDK 1.8.0_292, OpenJDK 64-Bit Server VM, 25.292-b10 # VM invoker: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/bin/java # VM options: -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=57882:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect) # Warmup: 1 iterations, 5 s each # Measurement: 3 iterations, 5 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.mapper # Run progress: 60.00% complete, ETA 00:00:41 # Fork: 1 of 1 # Warmup Iteration 1: 354.183 ns/op Iteration 1: 361.970 ns/op Iteration 2: 363.543 ns/op Iteration 3: 365.491 ns/op Result "com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.mapper": 363.668 ±(99.9%) 32.182 ns/op [Average] (min, avg, max) = (361.970, 363.668, 365.491), stdev = 1.764 CI (99.9%): [331.486, 395.850] (assumes normal distribution) # JMH version: 1.33 # VM version: JDK 1.8.0_292, OpenJDK 64-Bit Server VM, 25.292-b10 # VM invoker: /Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home/jre/bin/java # VM options: -javaagent:/Applications/IntelliJ IDEA CE.app/Contents/lib/idea_rt.jar=57882:/Applications/IntelliJ IDEA CE.app/Contents/bin -Dfile.encoding=UTF-8 # Blackhole mode: full + dont-inline hint (default, use -Djmh.blackhole.autoDetect=true to auto-detect) # Warmup: 1 iterations, 5 s each # Measurement: 3 iterations, 5 s each # Timeout: 10 min per iteration # Threads: 1 thread, will synchronize iterations # Benchmark mode: Average time, time/op # Benchmark: com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.mapstruct # Run progress: 80.00% complete, ETA 00:00:20 # Fork: 1 of 1 # Warmup Iteration 1: 3.849 ns/op Iteration 1: 13.459 ns/op Iteration 2: 12.571 ns/op Iteration 3: 12.720 ns/op Result "com.sayyi.demo.bean.copy.jmh.BeanCopyJmh.mapstruct": 12.917 ±(99.9%) 8.682 ns/op [Average] (min, avg, max) = (12.571, 12.917, 13.459), stdev = 0.476 CI (99.9%): [4.235, 21.599] (assumes normal distribution) # Run complete. Total time: 00:01:44 REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial experiments, perform baseline and negative tests that provide experimental control, make sure the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. Do not assume the numbers tell you what you want them to tell. Benchmark Mode Cnt Score Error Units BeanCopyJmh.cglib avgt 3 6.303 ± 4.177 ns/op BeanCopyJmh.classMap avgt 3 164.977 ± 63.338 ns/op BeanCopyJmh.manual avgt 3 11.301 ± 4.761 ns/op BeanCopyJmh.mapper avgt 3 363.668 ± 32.182 ns/op BeanCopyJmh.mapstruct avgt 3 12.917 ± 8.682 ns/op ```