Orika是一个不错的Bean拷贝工具,可以将类的不同属性做映射进行拷贝。比如A对象的price和B对象的fee都表示价格,类型也都一样,只是字段名称不一样,这样通过映射可以方便的进行拷贝。
下面是个示例:
Source类:
public class Source{
private Integer id;
private Double price;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
Target类:
public class Target{
private Integer id;
private Double fee;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Double getFee() {
return fee;
}
public void setFee(Double fee) {
this.fee = fee;
}
}
使用Orika
maven坐标:
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.4</version>
</dependency>
oirka拷贝程序--错误示例
public class OOMTest {
private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();
public static void main(String[] args) {
while(true) {
Source source = new Source();
source.setId(111);
source.setPrice(123d);
System.out.println(JSON.toJSONString(source));
MAPPER_FACTORY.classMap(Source.class, Target.class)
.field("price", "fee")
.byDefault()
.register();
Target target2 = MAPPER_FACTORY.getMapperFacade().map(source, Target.class);
System.out.println(JSON.toJSONString(target2));
}
}
}
静态变量创建MapperFactory,classMap是映射两个类型中的相同含义不同名称的属性。
打印的结果:
{"id":111,"price":123.0}
{"fee":123.0,"id":111}
之所以要用while循环,是为了测试大量调用的时候元空间溢出的问题,jvm对元空间的设置:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=48m,最终报错:
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:274)
at javassist.ClassPool.toClass(ClassPool.java:1232)
at javassist.CtClass.toClass(CtClass.java:1384)
at ma.glasnost.orika.impl.generator.JavassistCompilerStrategy.compileClass(JavassistCompilerStrategy.java:246)
at ma.glasnost.orika.impl.generator.SourceCodeContext.compileClass(SourceCodeContext.java:228)
at ma.glasnost.orika.impl.generator.SourceCodeContext.getInstance(SourceCodeContext.java:244)
at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:73)
at ma.glasnost.orika.impl.DefaultMapperFactory.buildMapper(DefaultMapperFactory.java:1478)
at ma.glasnost.orika.impl.DefaultMapperFactory.registerClassMap(DefaultMapperFactory.java:1254)
at ma.glasnost.orika.impl.DefaultMapperFactory.registerClassMap(DefaultMapperFactory.java:1267)
at ma.glasnost.orika.metadata.ClassMapBuilder.register(ClassMapBuilder.java:768)
at orika.OOMTest.main(OOMTest.java:23)
在jdk8中,元空间在jvm之外,也就是说对jvm的监控是没有元空间的报警的。
程序执行过程中,通过命令查看元空间的占用情况,打开命令提示符
查看java进行,jps -l
27796 org.jetbrains.jps.cmdline.Launcher
34036 org.jetbrains.jps.cmdline.Launcher
13528 sun.tools.jps.Jps
28056
14860 org.jetbrains.idea.maven.server.RemoteMavenServer
19964 org.jetbrains.jps.cmdline.Launcher
24268 orika.OOMTest
通过jstat查看元空间的使用情况:jstat -gcmetacapacity 24268 1000
MC列表示当前元空间的使用情况,发现元空间的占用是越来越大的。是因为Orika的类映射是生成字节码文件,该文件是存到元空间中的,直到占满设置的元空间大小(本示例是48M) ,导致的内存溢出。
问题解决:
对象映射不用每次都做,注册一次就够了。
public class OOMTest {
private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();
static {
MAPPER_FACTORY.classMap(Source.class, Target.class)
.field("price", "fee")
.field("id", "id")
.byDefault()
.register();
}
public static void main(String[] args) {
while(true) {
Source source = new Source();
source.setId(111);
source.setPrice(123d);
System.out.println(JSON.toJSONString(source));
// MAPPER_FACTORY.classMap(Source.class, Target.class)
// .field("price", "fee")
// .byDefault()
// .register();
Target target2 = MAPPER_FACTORY.getMapperFacade().map(source, Target.class);
System.out.println(JSON.toJSONString(target2));
}
}
}
程序的改动就是将每次执行拷贝的地方的classMap注释,放在外面的静态块中,只执行一次就可以了。
这次在看元空间的监控效果:
MC不在增长。解决问题。
元空间查看参考文章:https://www.jianshu.com/p/123079b47670