起源:https://albenw.github.io/post...

背景

在分层的代码架构中,层与层之间的对象防止不了要做很多转换、赋值等操作,这些操作反复且繁琐,于是乎催生出很多工具来优雅,高效地实现这个操作,有BeanUtils、BeanCopier、Dozer、Orika等等,本文将讲述下面几个工具的应用、性能比照及原理剖析。

性能剖析

其实这几个工具要做的事件很简略,而且在应用上也是相似的,所以我感觉先给大家看看性能剖析的比照后果,让大家有一个大略的意识。我是应用JMH来做性能剖析的,代码如下:

要复制的对象比较简单,蕴含了一些根本类型;有一次warmup,因为一些工具是须要“预编译”和做缓存的,这样做比照才会比拟主观;别离复制1000、10000、100000个对象,这是比拟罕用数量级了吧。

@BenchmarkMode(Mode.AverageTime)  @OutputTimeUnit(TimeUnit.MICROSECONDS)  @Fork(1)  @Warmup(iterations = 1)  @State(Scope.Benchmark)  public class BeanMapperBenchmark {        @Param({"1000", "10000", "100000"})      private int times;        private int time;        private static MapperFactory mapperFactory;        private static Mapper mapper;        static {          mapperFactory = new DefaultMapperFactory.Builder().build();          mapperFactory.classMap(SourceVO.class, TargetVO.class)                  .byDefault()                  .register();            mapper = DozerBeanMapperBuilder.create()                  .withMappingBuilder(new BeanMappingBuilder() {                      @Override                      protected void configure() {                          mapping(SourceVO.class, TargetVO.class)                                  .fields("fullName", "name")                                  .exclude("in");                      }                  }).build();      }        public static void main(String[] args) throws Exception {          Options options = new OptionsBuilder()                  .include(BeanMapperBenchmark.class.getName()).measurementIterations(3)                  .build();          new Runner(options).run();      }        @Setup      public void prepare() {          this.time = times;      }        @Benchmark      public void springBeanUtilTest(){          SourceVO sourceVO = getSourceVO();          for(int i = 0; i < time; i++){              TargetVO targetVO = new TargetVO();              BeanUtils.copyProperties(sourceVO, targetVO);          }      }        @Benchmark      public void apacheBeanUtilTest() throws Exception{          SourceVO sourceVO = getSourceVO();          for(int i = 0; i < time; i++){              TargetVO targetVO = new TargetVO();              org.apache.commons.beanutils.BeanUtils.copyProperties(targetVO, sourceVO);          }        }        @Benchmark      public void beanCopierTest(){          SourceVO sourceVO = getSourceVO();          for(int i = 0; i < time; i++){              TargetVO targetVO = new TargetVO();              BeanCopier bc = BeanCopier.create(SourceVO.class, TargetVO.class, false);              bc.copy(sourceVO, targetVO, null);          }        }        @Benchmark      public void dozerTest(){          SourceVO sourceVO = getSourceVO();          for(int i = 0; i < time; i++){              TargetVO map = mapper.map(sourceVO, TargetVO.class);          }      }        @Benchmark      public void orikaTest(){          SourceVO sourceVO = getSourceVO();          for(int i = 0; i < time; i++){              MapperFacade mapper = mapperFactory.getMapperFacade();              TargetVO map = mapper.map(sourceVO, TargetVO.class);          }      }        private SourceVO getSourceVO(){          SourceVO sourceVO = new SourceVO();          sourceVO.setP1(1);          sourceVO.setP2(2L);          sourceVO.setP3(new Integer(3).byteValue());          sourceVO.setDate1(new Date());          sourceVO.setPattr1("1");          sourceVO.setIn(new SourceVO.Inner(1));          sourceVO.setFullName("alben");          return sourceVO;      }    }  

在我macbook下运行后的后果如下:

Score示意的是均匀运行工夫,单位是微秒。从执行效率来看,能够看出 beanCopier > orika > springBeanUtil > dozer > apacheBeanUtil。这样的后果跟它们各自的实现原理有很大的关系,

上面将具体每个工具的应用及实现原理。

Spring的BeanUtils

应用

这个工具可能是大家日常应用最多的,因为是Spring自带的,应用也简略:BeanUtils.copyProperties(sourceVO, targetVO);

原理

Spring BeanUtils的实现原理也比拟简答,就是通过Java的Introspector获取到两个类的PropertyDescriptor,比照两个属性具备雷同的名字和类型,如果是,则进行赋值(通过ReadMethod获取值,通过WriteMethod赋值),否则疏忽。

为了进步性能Spring对BeanInfoPropertyDescriptor进行了缓存。

(源码基于:org.springframework:spring-beans:4.3.9.RELEASE)

/**    * Copy the property values of the given source bean into the given target bean.    * <p>Note: The source and target classes do not have to match or even be derived    * from each other, as long as the properties match. Any bean properties that the    * source bean exposes but the target bean does not will silently be ignored.    * @param source the source bean    * @param target the target bean    * @param editable the class (or interface) to restrict property setting to    * @param ignoreProperties array of property names to ignore    * @throws BeansException if the copying failed    * @see BeanWrapper    */   private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)     throws BeansException {      Assert.notNull(source, "Source must not be null");    Assert.notNull(target, "Target must not be null");      Class<?> actualEditable = target.getClass();    if (editable != null) {     if (!editable.isInstance(target)) {      throw new IllegalArgumentException("Target class [" + target.getClass().getName() +        "] not assignable to Editable class [" + editable.getName() + "]");     }     actualEditable = editable;    }      //获取target类的属性(有缓存)    PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);    List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);      for (PropertyDescriptor targetPd : targetPds) {     Method writeMethod = targetPd.getWriteMethod();     if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {          //获取source类的属性(有缓存)      PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());      if (sourcePd != null) {       Method readMethod = sourcePd.getReadMethod();       if (readMethod != null &&                //判断target的setter办法入参和source的getter办法返回类型是否统一         ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {        try {         if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {          readMethod.setAccessible(true);         }                //获取源值         Object value = readMethod.invoke(source);         if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {          writeMethod.setAccessible(true);         }                //赋值到target         writeMethod.invoke(target, value);        }        catch (Throwable ex) {         throw new FatalBeanException(           "Could not copy property '" + targetPd.getName() + "' from source to target", ex);        }       }      }     }    }   }  

小结

Spring BeanUtils的实现就是这么简洁,这也是它性能比拟高的起因。

不过,过于简洁就失去了灵活性和可扩展性了,Spring BeanUtils的应用限度也比拟显著,要求类属性的名字和类型统一,这点在应用时要留神。

Apache的BeanUtils

应用

Apache的BeanUtils和Spring的BeanUtils的应用是一样的:

BeanUtils.copyProperties(targetVO, sourceVO);  

要留神,source和target的入参地位不同。

原理

Apache的BeanUtils的实现原理跟Spring的BeanUtils一样,也是次要通过Java的Introspector机制获取到类的属性来进行赋值操作,对BeanInfo和PropertyDescriptor同样有缓存,然而Apache BeanUtils加了一些不那么应用的个性(包含反对Map类型、反对自定义的DynaBean类型、反对属性名的表达式等等)在外面,使得性能绝对Spring的BeanUtils来说有所降落。

(源码基于:commons-beanutils:commons-beanutils:1.9.3)

public void copyProperties(final Object dest, final Object orig)          throws IllegalAccessException, InvocationTargetException {                    if (dest == null) {              throw new IllegalArgumentException                      ("No destination bean specified");          }          if (orig == null) {              throw new IllegalArgumentException("No origin bean specified");          }          if (log.isDebugEnabled()) {              log.debug("BeanUtils.copyProperties(" + dest + ", " +                        orig + ")");          }          // Apache Common自定义的DynaBean          if (orig instanceof DynaBean) {              final DynaProperty[] origDescriptors =                  ((DynaBean) orig).getDynaClass().getDynaProperties();              for (DynaProperty origDescriptor : origDescriptors) {                  final String name = origDescriptor.getName();                  // Need to check isReadable() for WrapDynaBean                  // (see Jira issue# BEANUTILS-61)                  if (getPropertyUtils().isReadable(orig, name) &&                      getPropertyUtils().isWriteable(dest, name)) {                      final Object value = ((DynaBean) orig).get(name);                      copyProperty(dest, name, value);                  }              }          // Map类型          } else if (orig instanceof Map) {              @SuppressWarnings("unchecked")              final              // Map properties are always of type <String, Object>              Map<String, Object> propMap = (Map<String, Object>) orig;              for (final Map.Entry<String, Object> entry : propMap.entrySet()) {                  final String name = entry.getKey();                  if (getPropertyUtils().isWriteable(dest, name)) {                      copyProperty(dest, name, entry.getValue());                  }              }          // 规范的JavaBean          } else {              final PropertyDescriptor[] origDescriptors =                  //获取PropertyDescriptor                  getPropertyUtils().getPropertyDescriptors(orig);              for (PropertyDescriptor origDescriptor : origDescriptors) {                  final String name = origDescriptor.getName();                  if ("class".equals(name)) {                      continue; // No point in trying to set an object's class                  }                  //是否可读和可写                  if (getPropertyUtils().isReadable(orig, name) &&                      getPropertyUtils().isWriteable(dest, name)) {                      try {                          //获取源值                          final Object value =                              getPropertyUtils().getSimpleProperty(orig, name);                          //赋值操作                          copyProperty(dest, name, value);                      } catch (final NoSuchMethodException e) {                          // Should not happen                      }                  }              }          }        }  

小结

Apache BeanUtils的实现跟Spring BeanUtils总体上相似,然而性能却低很多,这个能够从下面性能比拟看进去。阿里的Java标准是不倡议应用的。

BeanCopier

应用

BeanCopier在cglib包里,它的应用也比较简单:

@Test  public void beanCopierSimpleTest() {      SourceVO sourceVO = getSourceVO();      log.info("source={}", GsonUtil.toJson(sourceVO));      TargetVO targetVO = new TargetVO();      BeanCopier bc = BeanCopier.create(SourceVO.class, TargetVO.class, false);      bc.copy(sourceVO, targetVO, null);      log.info("target={}", GsonUtil.toJson(targetVO));  }  

只须要事后定义好要转换的source类和target类就好了,能够抉择是否应用Converter,这个上面会说到。

在下面的性能测试中,BeanCopier是所有中体现最好的,那么咱们剖析一下它的实现原理。

原理

BeanCopier的实现原理跟BeanUtils截然不同,它不是利用反射对属性进行赋值,而是间接应用cglib来生成带有的get/set办法的class类,而后执行。因为是间接生成字节码执行,所以BeanCopier的性能靠近手写

get/set。

BeanCopier.create办法

public static BeanCopier create(Class source, Class target, boolean useConverter) {      Generator gen = new Generator();      gen.setSource(source);      gen.setTarget(target);      gen.setUseConverter(useConverter);      return gen.create();  }    public BeanCopier create() {      Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);      return (BeanCopier)super.create(key);  }  

这里的意思是用KEY_FACTORY创立一个BeanCopier进去,而后调用create办法来生成字节码。

KEY_FACTORY其实就是用cglib通过BeanCopierKey接口生成进去的一个类

private static final BeanCopierKey KEY_FACTORY =  (BeanCopierKey)KeyFactory.create(BeanCopierKey.class);          interface BeanCopierKey {      public Object newInstance(String source, String target, boolean useConverter);  }  

通过设置

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "path");  

能够让cglib输入生成类的class文件,咱们能够反编译看看外面的代码

上面是KEY_FACTORY的类

public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory implements BeanCopierKey {      private final String FIELD_0;      private final String FIELD_1;      private final boolean FIELD_2;        public BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd() {      }        public Object newInstance(String var1, String var2, boolean var3) {          return new BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd(var1, var2, var3);      }        public BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd(String var1, String var2, boolean var3) {          this.FIELD_0 = var1;          this.FIELD_1 = var2;          this.FIELD_2 = var3;      }      //省去hashCode等办法。。。  }  

持续跟踪Generator.create办法,因为Generator是继承AbstractClassGenerator,这个AbstractClassGenerator是cglib用来生成字节码的一个模板类,Generator的super.create其实调用

AbstractClassGenerator的create办法,最终会调用到Generator的模板办法generateClass办法,咱们不去细究AbstractClassGenerator的细节,重点看generateClass。

这个是一个生成java类的办法,了解起来就如同咱们平时写代码一样。

public void generateClass(ClassVisitor v) {      Type sourceType = Type.getType(source);      Type targetType = Type.getType(target);      ClassEmitter ce = new ClassEmitter(v);      //开始“写”类,这里有修饰符、类名、父类等信息      ce.begin_class(Constants.V1_2,                     Constants.ACC_PUBLIC,                     getClassName(),                     BEAN_COPIER,                     null,                     Constants.SOURCE_FILE);      //没有构造方法      EmitUtils.null_constructor(ce);      //开始“写”一个办法,办法名是copy      CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);      //通过Introspector获取source类和target类的PropertyDescriptor      PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);      PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);            Map names = new HashMap();      for (int i = 0; i < getters.length; i++) {          names.put(getters[i].getName(), getters[i]);      }      Local targetLocal = e.make_local();      Local sourceLocal = e.make_local();      if (useConverter) {          e.load_arg(1);          e.checkcast(targetType);          e.store_local(targetLocal);          e.load_arg(0);                          e.checkcast(sourceType);          e.store_local(sourceLocal);      } else {          e.load_arg(1);          e.checkcast(targetType);          e.load_arg(0);          e.checkcast(sourceType);      }      //通过属性名来生成转换的代码      //以setter作为遍历      for (int i = 0; i < setters.length; i++) {          PropertyDescriptor setter = setters[i];          //依据setter的name获取getter          PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());          if (getter != null) {              //获取读写办法              MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());              MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());              //如果用了useConverter,则进行上面的拼装代码形式              if (useConverter) {                  Type setterType = write.getSignature().getArgumentTypes()[0];                  e.load_local(targetLocal);                  e.load_arg(2);                  e.load_local(sourceLocal);                  e.invoke(read);                  e.box(read.getSignature().getReturnType());                  EmitUtils.load_class(e, setterType);                  e.push(write.getSignature().getName());                  e.invoke_interface(CONVERTER, CONVERT);                  e.unbox_or_zero(setterType);                  e.invoke(write);                //compatible用来判断getter和setter是否类型统一              } else if (compatible(getter, setter)) {                  e.dup2();                  e.invoke(read);                  e.invoke(write);              }          }      }      e.return_value();      e.end_method();      ce.end_class();  }    private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {      // TODO: allow automatic widening conversions?      return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());  }  

即便没有应用过cglib也能读懂生成代码的流程吧,咱们看看没有应用useConverter的状况下生成的代码:

public class Object$$BeanCopierByCGLIB$$d1d970c8 extends BeanCopier {      public Object$$BeanCopierByCGLIB$$d1d970c8() {      }        public void copy(Object var1, Object var2, Converter var3) {          TargetVO var10000 = (TargetVO)var2;          SourceVO var10001 = (SourceVO)var1;          var10000.setDate1(((SourceVO)var1).getDate1());          var10000.setIn(var10001.getIn());          var10000.setListData(var10001.getListData());          var10000.setMapData(var10001.getMapData());          var10000.setP1(var10001.getP1());          var10000.setP2(var10001.getP2());          var10000.setP3(var10001.getP3());          var10000.setPattr1(var10001.getPattr1());      }  }  

在比照下面生成代码的代码是不是阔然开朗了。

再看看应用useConverter的状况:

public class Object$$BeanCopierByCGLIB$$d1d970c7 extends BeanCopier {      private static final Class CGLIB$load_class$java$2Eutil$2EDate;      private static final Class CGLIB$load_class$beanmapper_compare$2Evo$2ESourceVO$24Inner;      private static final Class CGLIB$load_class$java$2Eutil$2EList;      private static final Class CGLIB$load_class$java$2Eutil$2EMap;      private static final Class CGLIB$load_class$java$2Elang$2EInteger;      private static final Class CGLIB$load_class$java$2Elang$2ELong;      private static final Class CGLIB$load_class$java$2Elang$2EByte;      private static final Class CGLIB$load_class$java$2Elang$2EString;        public Object$$BeanCopierByCGLIB$$d1d970c7() {      }        public void copy(Object var1, Object var2, Converter var3) {          TargetVO var4 = (TargetVO)var2;          SourceVO var5 = (SourceVO)var1;          var4.setDate1((Date)var3.convert(var5.getDate1(), CGLIB$load_class$java$2Eutil$2EDate, "setDate1"));          var4.setIn((Inner)var3.convert(var5.getIn(), CGLIB$load_class$beanmapper_compare$2Evo$2ESourceVO$24Inner, "setIn"));          var4.setListData((List)var3.convert(var5.getListData(), CGLIB$load_class$java$2Eutil$2EList, "setListData"));          var4.setMapData((Map)var3.convert(var5.getMapData(), CGLIB$load_class$java$2Eutil$2EMap, "setMapData"));          var4.setP1((Integer)var3.convert(var5.getP1(), CGLIB$load_class$java$2Elang$2EInteger, "setP1"));          var4.setP2((Long)var3.convert(var5.getP2(), CGLIB$load_class$java$2Elang$2ELong, "setP2"));          var4.setP3((Byte)var3.convert(var5.getP3(), CGLIB$load_class$java$2Elang$2EByte, "setP3"));          var4.setPattr1((String)var3.convert(var5.getPattr1(), CGLIB$load_class$java$2Elang$2EString, "setPattr1"));          var4.setSeq((Long)var3.convert(var5.getSeq(), CGLIB$load_class$java$2Elang$2ELong, "setSeq"));      }        static void CGLIB$STATICHOOK1() {          CGLIB$load_class$java$2Eutil$2EDate = Class.forName("java.util.Date");          CGLIB$load_class$beanmapper_compare$2Evo$2ESourceVO$24Inner = Class.forName("beanmapper_compare.vo.SourceVO$Inner");          CGLIB$load_class$java$2Eutil$2EList = Class.forName("java.util.List");          CGLIB$load_class$java$2Eutil$2EMap = Class.forName("java.util.Map");          CGLIB$load_class$java$2Elang$2EInteger = Class.forName("java.lang.Integer");          CGLIB$load_class$java$2Elang$2ELong = Class.forName("java.lang.Long");          CGLIB$load_class$java$2Elang$2EByte = Class.forName("java.lang.Byte");          CGLIB$load_class$java$2Elang$2EString = Class.forName("java.lang.String");      }        static {          CGLIB$STATICHOOK1();      }  }  

小结

BeanCopier性能的确很高,但从源码能够看出BeanCopier只会拷贝名称和类型都雷同的属性,而且如果一旦应用Converter,BeanCopier只应用Converter定义的规定去拷贝属性,所以在convert办法中要思考所有的属性。

Dozer

应用

下面提到的BeanUtils和BeanCopier都是性能比较简单的,须要属性名称一样,甚至类型也要一样。然而在大多数状况下这个要求就绝对刻薄了,要晓得有些VO因为各种起因不能批改,有些是内部接口SDK的对象,

有些对象的命名规定不同,例如有驼峰型的,有下划线的等等,各种什么状况都有。所以咱们更加须要的是更加灵便丰盛的性能,甚至能够做到定制化的转换。

Dozer就提供了这些性能,有反对同名隐式映射,反对根本类型相互转换,反对显示指定映射关系,反对exclude字段,反对递归匹配映射,反对深度匹配,反对Date to String的date-formate,反对自定义转换Converter,反对一次mapping定义多处应用,反对EventListener事件监听等等。不仅如此,Dozer在应用形式上,除了反对API,还反对XML和注解,满足大家的爱好。更多的性能能够参考这里

因为其性能很丰盛,不可能每个都演示,这里只是给个大略意识,更具体的性能,或者XML和注解的配置,请看官网文档。

private Mapper dozerMapper;        @Before      public void setup(){          dozerMapper = DozerBeanMapperBuilder.create()                  .withMappingBuilder(new BeanMappingBuilder() {                      @Override                      protected void configure() {                          mapping(SourceVO.class, TargetVO.class)                                  .fields("fullName", "name")                                  .exclude("in");                      }                  })                  .withCustomConverter(null)                  .withEventListener(null)                  .build();      }            @Test      public void dozerTest(){          SourceVO sourceVO = getSourceVO();          log.info("sourceVO={}", GsonUtil.toJson(sourceVO));          TargetVO map = dozerMapper.map(sourceVO, TargetVO.class);          log.info("map={}", GsonUtil.toJson(map));      }  

原理

Dozer的实现原理实质上还是用反射/Introspector那套,然而其丰盛的性能,以及反对多种实现形式(API、XML、注解)使得代码看上去有点简单,在翻阅代码时,咱们大可不必理睬这些类,只须要晓得它们大体的作用就行了,重点关注外围流程和代码的实现。上面咱们重点看看构建mapper的build办法和实现映射的map办法。

build办法很简略,它是一个初始化的动作,就是通过用户的配置来构建出一系列前面要用到的配置对象、上下文对象,或其余封装对象,咱们不用深究这些对象是怎么实现的,从名字上咱们大略能猜出这些对象是干嘛,负责什么就能够了。

DozerBeanMapper(List<String> mappingFiles,                  BeanContainer beanContainer,                  DestBeanCreator destBeanCreator,                  DestBeanBuilderCreator destBeanBuilderCreator,                  BeanMappingGenerator beanMappingGenerator,                  PropertyDescriptorFactory propertyDescriptorFactory,                  List<CustomConverter> customConverters,                  List<MappingFileData> mappingsFileData,                  List<EventListener> eventListeners,                  CustomFieldMapper customFieldMapper,                  Map<String, CustomConverter> customConvertersWithId,                  ClassMappings customMappings,                  Configuration globalConfiguration,                  CacheManager cacheManager) {      this.beanContainer = beanContainer;      this.destBeanCreator = destBeanCreator;      this.destBeanBuilderCreator = destBeanBuilderCreator;      this.beanMappingGenerator = beanMappingGenerator;      this.propertyDescriptorFactory = propertyDescriptorFactory;      this.customConverters = new ArrayList<>(customConverters);      this.eventListeners = new ArrayList<>(eventListeners);      this.mappingFiles = new ArrayList<>(mappingFiles);      this.customFieldMapper = customFieldMapper;      this.customConvertersWithId = new HashMap<>(customConvertersWithId);      this.eventManager = new DefaultEventManager(eventListeners);      this.customMappings = customMappings;      this.globalConfiguration = globalConfiguration;      this.cacheManager = cacheManager;  }  

map办法是映射对象的过程,其入口是MappingProcessor的mapGeneral办法

private <T> T mapGeneral(Object srcObj, final Class<T> destClass, final T destObj, final String mapId) {      srcObj = MappingUtils.deProxy(srcObj, beanContainer);      Class<T> destType;      T result;      if (destClass == null) {          destType = (Class<T>)destObj.getClass();          result = destObj;      } else {          destType = destClass;          result = null;      }      ClassMap classMap = null;      try {          //构建ClassMap          //ClassMap是包含src类和dest类和其余配置的一个封装          classMap = getClassMap(srcObj.getClass(), destType, mapId);          //注册事件          eventManager.on(new DefaultEvent(EventTypes.MAPPING_STARTED, classMap, null, srcObj, result, null));                    //看看有没有自定义converter          Class<?> converterClass = MappingUtils.findCustomConverter(converterByDestTypeCache, classMap.getCustomConverters(), srcObj                  .getClass(), destType);          if (destObj == null) {              // If this is a nested MapperAware conversion this mapping can be already processed              // but we can do this optimization only in case of no destObject, instead we must copy to the dest object              Object alreadyMappedValue = mappedFields.getMappedValue(srcObj, destType, mapId);              if (alreadyMappedValue != null) {                  return (T)alreadyMappedValue;              }          }          //优先应用自定义converter进行映射          if (converterClass != null) {              return (T)mapUsingCustomConverter(converterClass, srcObj.getClass(), srcObj, destType, result, null, true);          }          //也是对配置进行了封装          BeanCreationDirective creationDirective =                  new BeanCreationDirective(srcObj, classMap.getSrcClassToMap(), classMap.getDestClassToMap(), destType,                                            classMap.getDestClassBeanFactory(), classMap.getDestClassBeanFactoryId(), classMap.getDestClassCreateMethod(),                                            classMap.getDestClass().isSkipConstructor());          //持续进行映射          result = createByCreationDirectiveAndMap(creationDirective, classMap, srcObj, result, false, null);      } catch (Throwable e) {          MappingUtils.throwMappingException(e);      }      eventManager.on(new DefaultEvent(EventTypes.MAPPING_FINISHED, classMap, null, srcObj, result, null));      return result;  }  

个别状况下createByCreationDirectiveAndMap办法会始终调用到mapFromFieldMap办法,而在没有自定义converter的状况下会调用mapOrRecurseObject办法

大多数状况下字段的映射会在这个办法做个别的解析

private Object mapOrRecurseObject(Object srcObj, Object srcFieldValue, Class<?> destFieldType, FieldMap fieldMap, Object destObj) {      Class<?> srcFieldClass = srcFieldValue != null ? srcFieldValue.getClass() : fieldMap.getSrcFieldType(srcObj.getClass());      Class<?> converterClass = MappingUtils.determineCustomConverter(fieldMap, converterByDestTypeCache, fieldMap.getClassMap()              .getCustomConverters(), srcFieldClass, destFieldType);      //自定义converter的解决      if (converterClass != null) {          return mapUsingCustomConverter(converterClass, srcFieldClass, srcFieldValue, destFieldType, destObj, fieldMap, false);      }      if (srcFieldValue == null) {          return null;      }      String srcFieldName = fieldMap.getSrcFieldName();      String destFieldName = fieldMap.getDestFieldName();            if (!(DozerConstants.SELF_KEYWORD.equals(srcFieldName) && DozerConstants.SELF_KEYWORD.equals(destFieldName))) {          Object alreadyMappedValue = mappedFields.getMappedValue(srcFieldValue, destFieldType, fieldMap.getMapId());          if (alreadyMappedValue != null) {              return alreadyMappedValue;          }      }      //如果只是浅拷贝则间接返回(可配置)      if (fieldMap.isCopyByReference()) {          // just get the src and return it, no transformation.          return srcFieldValue;      }      //对Map类型的解决      boolean isSrcFieldClassSupportedMap = MappingUtils.isSupportedMap(srcFieldClass);      boolean isDestFieldTypeSupportedMap = MappingUtils.isSupportedMap(destFieldType);      if (isSrcFieldClassSupportedMap && isDestFieldTypeSupportedMap) {          return mapMap(srcObj, (Map<?, ?>)srcFieldValue, fieldMap, destObj);      }      if (fieldMap instanceof MapFieldMap && destFieldType.equals(Object.class)) {                    destFieldType = fieldMap.getDestHintContainer() != null ? fieldMap.getDestHintContainer().getHint() : srcFieldClass;      }      //对根本类型的映射解决      //PrimitiveOrWrapperConverter类反对兼容了根本类型之间的相互转换      if (primitiveConverter.accepts(srcFieldClass) || primitiveConverter.accepts(destFieldType)) {          // Primitive or Wrapper conversion          if (fieldMap.getDestHintContainer() != null) {              Class<?> destHintType = fieldMap.getDestHintType(srcFieldValue.getClass());              // if the destType is null this means that there was more than one hint.              // we must have already set the destType then.              if (destHintType != null) {                  destFieldType = destHintType;              }          }          //#1841448 - if trim-strings=true, then use a trimmed src string value when converting to dest value          Object convertSrcFieldValue = srcFieldValue;          if (fieldMap.isTrimStrings() && srcFieldValue.getClass().equals(String.class)) {              convertSrcFieldValue = ((String)srcFieldValue).trim();          }          DateFormatContainer dfContainer = new DateFormatContainer(fieldMap.getDateFormat());          if (fieldMap instanceof MapFieldMap && !primitiveConverter.accepts(destFieldType)) {                            return primitiveConverter.convert(convertSrcFieldValue, convertSrcFieldValue.getClass(), dfContainer);          } else {              return primitiveConverter.convert(convertSrcFieldValue, destFieldType, dfContainer, destFieldName, destObj);          }      }      //对汇合类型的映射解决      if (MappingUtils.isSupportedCollection(srcFieldClass) && (MappingUtils.isSupportedCollection(destFieldType))) {          return mapCollection(srcObj, srcFieldValue, fieldMap, destObj);      }      //对枚举类型的映射解决      if (MappingUtils.isEnumType(srcFieldClass, destFieldType)) {          return mapEnum((Enum)srcFieldValue, (Class<Enum>)destFieldType);      }      if (fieldMap.getDestDeepIndexHintContainer() != null) {          destFieldType = fieldMap.getDestDeepIndexHintContainer().getHint();      }      //其余简单对象类型的解决      return mapCustomObject(fieldMap, destObj, destFieldType, destFieldName, srcFieldValue);  }  

mapCustomObject办法。其实你会发现这个办法最重要的一点就是做递归解决,无论是最初调用createByCreationDirectiveAndMap还是mapToDestObject办法。

private Object mapCustomObject(FieldMap fieldMap, Object destObj, Class<?> destFieldType, String destFieldName, Object srcFieldValue) {      srcFieldValue = MappingUtils.deProxy(srcFieldValue, beanContainer);      // Custom java bean. Need to make sure that the destination object is not      // already instantiated.      Object result = null;      // in case of iterate feature new objects are created in any case      if (!DozerConstants.ITERATE.equals(fieldMap.getDestFieldType())) {          result = getExistingValue(fieldMap, destObj, destFieldType);      }      // if the field is not null than we don't want a new instance      if (result == null) {          // first check to see if this plain old field map has hints to the actual          // type.          if (fieldMap.getDestHintContainer() != null) {              Class<?> destHintType = fieldMap.getDestHintType(srcFieldValue.getClass());              // if the destType is null this means that there was more than one hint.              // we must have already set the destType then.              if (destHintType != null) {                  destFieldType = destHintType;              }          }          // Check to see if explicit map-id has been specified for the field          // mapping          String mapId = fieldMap.getMapId();          Class<?> targetClass;          if (fieldMap.getDestHintContainer() != null && fieldMap.getDestHintContainer().getHint() != null) {              targetClass = fieldMap.getDestHintContainer().getHint();          } else {              targetClass = destFieldType;          }          ClassMap classMap = getClassMap(srcFieldValue.getClass(), targetClass, mapId);          BeanCreationDirective creationDirective = new BeanCreationDirective(srcFieldValue, classMap.getSrcClassToMap(), classMap.getDestClassToMap(),                                                                              destFieldType, classMap.getDestClassBeanFactory(), classMap.getDestClassBeanFactoryId(),                                                                              fieldMap.getDestFieldCreateMethod() != null ? fieldMap.getDestFieldCreateMethod() :                                                                                      classMap.getDestClassCreateMethod(),                                                                              classMap.getDestClass().isSkipConstructor(), destObj, destFieldName);          result = createByCreationDirectiveAndMap(creationDirective, classMap, srcFieldValue, null, false, fieldMap.getMapId());      } else {          mapToDestObject(null, srcFieldValue, result, false, fieldMap.getMapId());      }      return result;  }  

小结

Dozer功能强大,但底层还是用反射那套,所以在性能测试中它的体现个别,仅次于Apache的BeanUtils。如果不谋求性能的话,能够应用。

Orika

Orika能够说是简直集成了上述几个工具的长处,不仅具备丰盛的性能,底层应用Javassist生成字节码,运行 效率很高的。

应用

Orika根本反对了Dozer反对的性能,这里我也是简略介绍一下Orika的应用,具体更具体的API能够参考User Guide。

private MapperFactory mapperFactory;    @Before  public void setup() {      mapperFactory = new DefaultMapperFactory.Builder().build();      ConverterFactory converterFactory = mapperFactory.getConverterFactory();      converterFactory.registerConverter(new TypeConverter());      mapperFactory.classMap(SourceVO.class, TargetVO.class)              .field("fullName", "name")              .field("type", "enumType")              .exclude("in")              .byDefault()              .register();  }    @Test  public void main() {      MapperFacade mapper = mapperFactory.getMapperFacade();      SourceVO sourceVO = getSourceVO();      log.info("sourceVO={}", GsonUtil.toJson(sourceVO));      TargetVO map = mapper.map(sourceVO, TargetVO.class);      log.info("map={}", GsonUtil.toJson(map));  }  

原理

在解说实现原理时,咱们先看看Orika在背地干了什么事件。

通过减少以下配置,咱们能够看到Orika在做映射过程中生成mapper的源码和字节码。

System.setProperty("ma.glasnost.orika.writeSourceFiles", "true");  System.setProperty("ma.glasnost.orika.writeClassFiles", "true");  System.setProperty("ma.glasnost.orika.writeSourceFilesToPath", "path");  System.setProperty("ma.glasnost.orika.writeClassFilesToPath", "path");  

用下面的例子,咱们看看Orika生成的java代码:

package ma.glasnost.orika.generated;    public class Orika_TargetVO_SourceVO_Mapper947163525829122$0 extends ma.glasnost.orika.impl.GeneratedMapperBase {     public void mapAtoB(java.lang.Object a, java.lang.Object b, ma.glasnost.orika.MappingContext mappingContext) {      super.mapAtoB(a, b, mappingContext);      // sourceType: SourceVO  beanmapper_compare.vo.SourceVO source = ((beanmapper_compare.vo.SourceVO)a);   // destinationType: TargetVO  beanmapper_compare.vo.TargetVO destination = ((beanmapper_compare.vo.TargetVO)b);       destination.setName(((java.lang.String)source.getFullName()));   if ( !(((java.lang.Integer)source.getType()) == null)){   destination.setEnumType(((beanmapper_compare.vo.TargetVO.EnumType)((ma.glasnost.orika.Converter)usedConverters[0]).convert(((java.lang.Integer)source.getType()), ((ma.glasnost.orika.metadata.Type)usedTypes[0]), mappingContext)));   } else {   destination.setEnumType(null);   }  if ( !(((java.util.Date)source.getDate1()) == null)){   destination.setDate1(((java.util.Date)((ma.glasnost.orika.Converter)usedConverters[1]).convert(((java.util.Date)source.getDate1()), ((ma.glasnost.orika.metadata.Type)usedTypes[1]), mappingContext)));   } else {   destination.setDate1(null);   }if ( !(((java.util.List)source.getListData()) == null)) {    java.util.List new_listData = ((java.util.List)new java.util.ArrayList());     new_listData.addAll(mapperFacade.mapAsList(((java.util.List)source.getListData()), ((ma.glasnost.orika.metadata.Type)usedTypes[2]), ((ma.glasnost.orika.metadata.Type)usedTypes[3]), mappingContext));   destination.setListData(new_listData);   } else {   if ( !(((java.util.List)destination.getListData()) == null)) {  destination.setListData(null);  };  }if ( !(((java.util.Map)source.getMapData()) == null)){    java.util.Map new_mapData = ((java.util.Map)new java.util.LinkedHashMap());   for( java.util.Iterator mapData_$_iter = ((java.util.Map)source.getMapData()).entrySet().iterator(); mapData_$_iter.hasNext(); ) {     java.util.Map.Entry sourceMapDataEntry = ((java.util.Map.Entry)mapData_$_iter.next());   java.lang.Integer newMapDataKey = null;   java.util.List newMapDataVal = null;   if ( !(((java.lang.Long)sourceMapDataEntry.getKey()) == null)){   newMapDataKey = ((java.lang.Integer)((ma.glasnost.orika.Converter)usedConverters[2]).convert(((java.lang.Long)sourceMapDataEntry.getKey()), ((ma.glasnost.orika.metadata.Type)usedTypes[3]), mappingContext));   } else {   newMapDataKey = null;   }  if ( !(((java.util.List)sourceMapDataEntry.getValue()) == null)) {    java.util.List new_newMapDataVal = ((java.util.List)new java.util.ArrayList());     new_newMapDataVal.addAll(mapperFacade.mapAsList(((java.util.List)sourceMapDataEntry.getValue()), ((ma.glasnost.orika.metadata.Type)usedTypes[2]), ((ma.glasnost.orika.metadata.Type)usedTypes[4]), mappingContext));   newMapDataVal = new_newMapDataVal;   } else {   if ( !(newMapDataVal == null)) {  newMapDataVal = null;  };  }  new_mapData.put(newMapDataKey, newMapDataVal);     }  destination.setMapData(new_mapData);   } else {   destination.setMapData(null);  }  destination.setP1(((java.lang.Integer)source.getP1()));   destination.setP2(((java.lang.Long)source.getP2()));   destination.setP3(((java.lang.Byte)source.getP3()));   destination.setPattr1(((java.lang.String)source.getPattr1()));   if ( !(((java.lang.String)source.getSeq()) == null)){   destination.setSeq(((java.lang.Long)((ma.glasnost.orika.Converter)usedConverters[3]).convert(((java.lang.String)source.getSeq()), ((ma.glasnost.orika.metadata.Type)usedTypes[2]), mappingContext)));   } else {   destination.setSeq(null);   }    if(customMapper != null) {       customMapper.mapAtoB(source, destination, mappingContext);    }   }     public void mapBtoA(java.lang.Object a, java.lang.Object b, ma.glasnost.orika.MappingContext mappingContext) {      super.mapBtoA(a, b, mappingContext);      // sourceType: TargetVO  beanmapper_compare.vo.TargetVO source = ((beanmapper_compare.vo.TargetVO)a);   // destinationType: SourceVO  beanmapper_compare.vo.SourceVO destination = ((beanmapper_compare.vo.SourceVO)b);       destination.setFullName(((java.lang.String)source.getName()));   if ( !(((beanmapper_compare.vo.TargetVO.EnumType)source.getEnumType()) == null)){   destination.setType(((java.lang.Integer)((ma.glasnost.orika.Converter)usedConverters[0]).convert(((beanmapper_compare.vo.TargetVO.EnumType)source.getEnumType()), ((ma.glasnost.orika.metadata.Type)usedTypes[3]), mappingContext)));   } else {   destination.setType(null);   }  if ( !(((java.util.Date)source.getDate1()) == null)){   destination.setDate1(((java.util.Date)((ma.glasnost.orika.Converter)usedConverters[1]).convert(((java.util.Date)source.getDate1()), ((ma.glasnost.orika.metadata.Type)usedTypes[1]), mappingContext)));   } else {   destination.setDate1(null);   }if ( !(((java.util.List)source.getListData()) == null)) {    java.util.List new_listData = ((java.util.List)new java.util.ArrayList());     new_listData.addAll(mapperFacade.mapAsList(((java.util.List)source.getListData()), ((ma.glasnost.orika.metadata.Type)usedTypes[3]), ((ma.glasnost.orika.metadata.Type)usedTypes[2]), mappingContext));   destination.setListData(new_listData);   } else {   if ( !(((java.util.List)destination.getListData()) == null)) {  destination.setListData(null);  };  }if ( !(((java.util.Map)source.getMapData()) == null)){    java.util.Map new_mapData = ((java.util.Map)new java.util.LinkedHashMap());   for( java.util.Iterator mapData_$_iter = ((java.util.Map)source.getMapData()).entrySet().iterator(); mapData_$_iter.hasNext(); ) {     java.util.Map.Entry sourceMapDataEntry = ((java.util.Map.Entry)mapData_$_iter.next());   java.lang.Long newMapDataKey = null;   java.util.List newMapDataVal = null;   if ( !(((java.lang.Integer)sourceMapDataEntry.getKey()) == null)){   newMapDataKey = ((java.lang.Long)((ma.glasnost.orika.Converter)usedConverters[2]).convert(((java.lang.Integer)sourceMapDataEntry.getKey()), ((ma.glasnost.orika.metadata.Type)usedTypes[2]), mappingContext));   } else {   newMapDataKey = null;   }  if ( !(((java.util.List)sourceMapDataEntry.getValue()) == null)) {    java.util.List new_newMapDataVal = ((java.util.List)new java.util.ArrayList());     new_newMapDataVal.addAll(mapperFacade.mapAsList(((java.util.List)sourceMapDataEntry.getValue()), ((ma.glasnost.orika.metadata.Type)usedTypes[4]), ((ma.glasnost.orika.metadata.Type)usedTypes[2]), mappingContext));   newMapDataVal = new_newMapDataVal;   } else {   if ( !(newMapDataVal == null)) {  newMapDataVal = null;  };  }  new_mapData.put(newMapDataKey, newMapDataVal);     }  destination.setMapData(new_mapData);   } else {   destination.setMapData(null);  }  destination.setP1(((java.lang.Integer)source.getP1()));   destination.setP2(((java.lang.Long)source.getP2()));   destination.setP3(((java.lang.Byte)source.getP3()));   destination.setPattr1(((java.lang.String)source.getPattr1()));   if ( !(((java.lang.Long)source.getSeq()) == null)){   destination.setSeq(((java.lang.String)((ma.glasnost.orika.Converter)usedConverters[4]).convert(((java.lang.Long)source.getSeq()), ((ma.glasnost.orika.metadata.Type)usedTypes[5]), mappingContext)));   } else {   destination.setSeq(null);   }    if(customMapper != null) {       customMapper.mapBtoA(source, destination, mappingContext);    }   }    }  

这个mapper类就两个办法mapAtoB和mapBtoA,从名字看猜到前者是负责src -> dest的映射,后者是负责dest -> src的映射。

好,咱们们看看实现的过程。

Orika的应用跟Dozer的相似,首先通过配置生成一个MapperFactory,再用MapperFacade来作为映射的对立入口,这里MapperFactoryMapperFacade都是单例的。mapperFactory在做配置类映射时,只是注册了ClassMap,还没有真正的生成mapper的字节码,是在第一次调用getMapperFacade办法时才初始化mapper。上面看看getMapperFacade。

(源码基于 ma.glasnost.orika:orika-core:1.5.4)

public MapperFacade getMapperFacade() {      if (!isBuilt) {          synchronized (mapperFacade) {              if (!isBuilt) {                  build();              }          }      }      return mapperFacade;  }  

利用注册的ClassMap信息和MappingContext上下文信息来结构mapper

public synchronized void build() {            if (!isBuilding && !isBuilt) {          isBuilding = true;                    MappingContext context = contextFactory.getContext();          try {              if (useBuiltinConverters) {                  BuiltinConverters.register(converterFactory);              }              converterFactory.setMapperFacade(mapperFacade);                            for (Map.Entry<MapperKey, ClassMap<Object, Object>> classMapEntry : classMapRegistry.entrySet()) {                  ClassMap<Object, Object> classMap = classMapEntry.getValue();                  if (classMap.getUsedMappers().isEmpty()) {                      classMapEntry.setValue(classMap.copyWithUsedMappers(discoverUsedMappers(classMap)));                  }              }              buildClassMapRegistry();                            Map<ClassMap<?, ?>, GeneratedMapperBase> generatedMappers = new HashMap<ClassMap<?, ?>, GeneratedMapperBase>();              //重点看这里              //在应用mapperFactory配置classMap时,会寄存在classMapRegistry里              for (ClassMap<?, ?> classMap : classMapRegistry.values()) {                  //对每个classMap生成一个mapper,重点看buildMapper办法                  generatedMappers.put(classMap, buildMapper(classMap, false, context));              }                            Set<Entry<ClassMap<?, ?>, GeneratedMapperBase>> generatedMapperEntries = generatedMappers.entrySet();              for (Entry<ClassMap<?, ?>, GeneratedMapperBase> generatedMapperEntry : generatedMapperEntries) {                  buildObjectFactories(generatedMapperEntry.getKey(), context);                  initializeUsedMappers(generatedMapperEntry.getValue(), generatedMapperEntry.getKey(), context);              }                        } finally {              contextFactory.release(context);          }                    isBuilt = true;          isBuilding = false;      }  }    public Set<ClassMap<Object, Object>> lookupUsedClassMap(MapperKey mapperKey) {      Set<ClassMap<Object, Object>> usedClassMapSet = usedMapperMetadataRegistry.get(mapperKey);      if (usedClassMapSet == null) {          usedClassMapSet = Collections.emptySet();      }      return usedClassMapSet;  }  

跟踪buildMapper办法

private GeneratedMapperBase buildMapper(ClassMap<?, ?> classMap, boolean isAutoGenerated, MappingContext context) {            register(classMap.getAType(), classMap.getBType(), isAutoGenerated);      register(classMap.getBType(), classMap.getAType(), isAutoGenerated);            final MapperKey mapperKey = new MapperKey(classMap.getAType(), classMap.getBType());      //调用mapperGenerator的build办法生成mapper      final GeneratedMapperBase mapper = mapperGenerator.build(classMap, context);      mapper.setMapperFacade(mapperFacade);      mapper.setFromAutoMapping(isAutoGenerated);      if (classMap.getCustomizedMapper() != null) {          final Mapper<Object, Object> customizedMapper = (Mapper<Object, Object>) classMap.getCustomizedMapper();          mapper.setCustomMapper(customizedMapper);      }      mappersRegistry.remove(mapper);      //生成的mapper寄存到mappersRegistry      mappersRegistry.add(mapper);      classMapRegistry.put(mapperKey, (ClassMap<Object, Object>) classMap);            return mapper;  }  

MapperGenerator的build办法

public GeneratedMapperBase build(ClassMap<?, ?> classMap, MappingContext context) {    StringBuilder logDetails = null;  try {      compilerStrategy.assureTypeIsAccessible(classMap.getAType().getRawType());      compilerStrategy.assureTypeIsAccessible(classMap.getBType().getRawType());            if (LOGGER.isDebugEnabled()) {          logDetails = new StringBuilder();          String srcName = TypeFactory.nameOf(classMap.getAType(), classMap.getBType());          String dstName = TypeFactory.nameOf(classMap.getBType(), classMap.getAType());          logDetails.append("Generating new mapper for (" + srcName + ", " + dstName + ")");      }            //构建用来生成源码及字节码的上下文      final SourceCodeContext mapperCode = new SourceCodeContext(classMap.getMapperClassName(), GeneratedMapperBase.class, context,              logDetails);            Set<FieldMap> mappedFields = new LinkedHashSet<FieldMap>();      //减少mapAtoB办法      mappedFields.addAll(addMapMethod(mapperCode, true, classMap, logDetails));      //减少mapBtoA办法      //addMapMethod办法根本就是手写代码的过程,有趣味的读者能够看看      mappedFields.addAll(addMapMethod(mapperCode, false, classMap, logDetails));            //生成一个mapper实例      GeneratedMapperBase instance = mapperCode.getInstance();      instance.setAType(classMap.getAType());      instance.setBType(classMap.getBType());      instance.setFavorsExtension(classMap.favorsExtension());            if (logDetails != null) {          LOGGER.debug(logDetails.toString());          logDetails = null;      }            classMap = classMap.copy(mappedFields);      context.registerMapperGeneration(classMap);            return instance;        } catch (final Exception e) {      if (logDetails != null) {          logDetails.append("\n<---- ERROR occurred here");          LOGGER.debug(logDetails.toString());      }      throw new MappingException(e);  }  

生成mapper实例

T instance = (T) compileClass().newInstance();    protected Class<?> compileClass() throws SourceCodeGenerationException {      try {          return compilerStrategy.compileClass(this);      } catch (SourceCodeGenerationException e) {          throw e;      }  }  

这里的compilerStrategy的默认是用Javassist(你也能够自定义生成字节码的策略)

JavassistCompilerStrategy的compileClass办法

这基本上就是一个应用Javassist的过程,通过后面的各种铺垫(通过配置信息、上下文信息、拼装java源代码等等),终于来到这一步

public Class<?> compileClass(SourceCodeContext sourceCode) throws SourceCodeGenerationException {            StringBuilder className = new StringBuilder(sourceCode.getClassName());      CtClass byteCodeClass = null;      int attempts = 0;      Random rand = RANDOM;      while (byteCodeClass == null) {          try {              //创立一个类              byteCodeClass = classPool.makeClass(className.toString());          } catch (RuntimeException e) {              if (attempts < 5) {                  className.append(Integer.toHexString(rand.nextInt()));              } else {                  // No longer likely to be accidental name collision;                  // propagate the error                  throw e;              }          }      }            CtClass abstractMapperClass;      Class<?> compiledClass;            try {          //把源码写到磁盘(通过下面提到的配置)          writeSourceFile(sourceCode);                    Boolean existing = superClasses.put(sourceCode.getSuperClass(), true);          if (existing == null || !existing) {              classPool.insertClassPath(new ClassClassPath(sourceCode.getSuperClass()));          }                    if (registerClassLoader(Thread.currentThread().getContextClassLoader())) {              classPool.insertClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));          }                    abstractMapperClass = classPool.get(sourceCode.getSuperClass().getCanonicalName());          byteCodeClass.setSuperclass(abstractMapperClass);                    //减少字段          for (String fieldDef : sourceCode.getFields()) {              try {                  byteCodeClass.addField(CtField.make(fieldDef, byteCodeClass));              } catch (CannotCompileException e) {                  LOG.error("An exception occurred while compiling: " + fieldDef + " for " + sourceCode.getClassName(), e);                  throw e;              }          }                    //减少办法,这里次要就是mapAtoB和mapBtoA办法          //间接用源码通过Javassist往类“加”办法          for (String methodDef : sourceCode.getMethods()) {              try {                  byteCodeClass.addMethod(CtNewMethod.make(methodDef, byteCodeClass));              } catch (CannotCompileException e) {                  LOG.error(                          "An exception occured while compiling the following method:\n\n " + methodDef + "\n\n for "                                  + sourceCode.getClassName() + "\n", e);                  throw e;              }                        }          //生成类          compiledClass = byteCodeClass.toClass(Thread.currentThread().getContextClassLoader(), this.getClass().getProtectionDomain());                    //字节码文件写磁盘          writeClassFile(sourceCode, byteCodeClass);                } catch (NotFoundException e) {          throw new SourceCodeGenerationException(e);      } catch (CannotCompileException e) {          throw new SourceCodeGenerationException("Error compiling " + sourceCode.getClassName(), e);      } catch (IOException e) {          throw new SourceCodeGenerationException("Could not write files for " + sourceCode.getClassName(), e);      }            return compiledClass;  }  

好,mapper类生成了,当初就看在调用MapperFacade的map办法是如何应用这个mapper类的。

其实很简略,还记得生成的mapper是放到mappersRegistry吗,跟踪代码,在resolveMappingStrategy办法依据typeA和typeB在mappersRegistry找到mapper,在调用mapper的mapAtoB或mapBtoA办法即可。

小结

总体来说,Orika是一个功能强大的而且性能很高的工具,举荐应用。

总结

通过对BeanUtils、BeanCopier、Dozer、Orika这几个工具的比照,咱们得悉了它们的性能以及实现原理。在应用时,咱们能够依据本人的理论状况抉择,举荐应用Orika。

近期热文举荐:

1.600+ 道 Java面试题及答案整顿(2021最新版)

2.终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞+转发哦!