2015-09-23 26 views
0

我有一個簡單的實體用戶。使用Byte Buddy將DTO轉換爲實體的註釋

public class User { 

    String name; 

    public String getName() { 
    return name; 
    } 

    public void setName(String name) { 
    this.name = name; 
    } 

} 

而他相應的DTO

public class UsuarioDTO { 

    String name; 

    String getName(){ 
    return this.name; 
    } 

    public void setName(String name) { 
    this.name = name; 
    } 

} 

我要實現類似我下面展示,以避免多類變壓器。

@Dto(entity = "Usuario") 
public class UsuarioDTO { 

    @BasicElement(name = "name") 
    String name; 

    String getName(){ 
    return this.name; 
    } 

    public void setName(String name) { 
    this.name = name; 
    } 

} 

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.FIELD) 
public @interface BasicElement { 

    String name(); 

} 

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.TYPE) 
public @interface Dto { 

    String entity() default ""; 

} 

有了這個例子類,我可以這樣做:

public class Transformer { 

    public static void main(String[] args) { 
    UserDTO usuarioDTO = new UserDTO("Gabriel"); 

    Class<UserDTO> obj = UserDTO.class; 

    if (obj.isAnnotationPresent(Dto.class)) { 

     Dto annotation = obj.getAnnotation(Dto.class); 

     Class<?> clazz; 
     try { 
     clazz = Class.forName(annotation.entity()); 
     Constructor<?> constructor = clazz.getConstructor(); 
     Object instance = constructor.newInstance(); 

     for (Field originField : UserDTO.class.getDeclaredFields()) { 
      originField.setAccessible(true); 
      if (originField.isAnnotationPresent(BasicElement.class)) { 
      BasicElement basicElement = originField.getAnnotation(BasicElement.class); 
      Field destinationField = instance.getClass().getDeclaredField(basicElement.name()); 
      destinationField.setAccessible(true); 
      destinationField.set(instance, originField.get(usuarioDTO)); 

      } 
     } 
     System.out.println(((User) instance).getName()); 

     } catch (Exception e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
     } 

    } 
    } 
} 

但是,這將是昂貴的,因爲消耗在每個改造的註解。

有可能通過以字節爲好友閱讀註釋,並創建一個一流的變壓器,其反編譯的代碼如下所示:

public class TransformerImpl implements ITransformer{ 
    public Object toEntity(Object dto){ 
    User user = new User(); 
    user.setName(dto.getName()); 
    } 
} 

UPDATE: @Rafael溫特,這樣的事情?

public class Transformer<D,E> { 

    List<Field> dtoFields = new ArrayList<Field>(); 

    Constructor<D> dtoConstructor; 

    List<Field> entityFields = new ArrayList<Field>(); 

    Constructor<E> entityConstructor; 

    public Transformer(Class<D> dtoClass){ 
     try { 
      Dto annotation = dtoClass.getAnnotation(Dto.class); 
      Class<E> entityClass = (Class<E>) annotation.entity(); 
      //entityConstructor = entityClass.getConstructor(); 
      entityConstructor = entityClass.getDeclaredConstructor(); 
      entityConstructor.setAccessible(true); 
      dtoConstructor = dtoClass.getConstructor(); 
      dtoConstructor.setAccessible(true); 
      lookupFields(entityClass, dtoClass); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    private void lookupFields(Class<E> entityClass, Class<D> dtoClass) throws NoSuchFieldException { 
     for (Field dtoField : dtoClass.getDeclaredFields()) { 
      if (dtoField.isAnnotationPresent(BasicElement.class)) { 
       BasicElement basicElement = dtoField.getAnnotation(BasicElement.class); 
       String entityFieldName = (basicElement.name().equals("")) ? dtoField.getName() : basicElement.name(); 
       Field entityField = entityClass.getDeclaredField(entityFieldName); 
       dtoField.setAccessible(true); 
       entityField.setAccessible(true); 
       dtoFields.add(dtoField); 
       entityFields.add(entityField); 
      } 
     } 
    } 

    public E toEntity(D dto) throws ReflectiveOperationException { 
     E entity = entityConstructor.newInstance(); 
     for (int i = 0; i < entityFields.size(); i++){ 
      Field destination = entityFields.get(i); 
      Field origin = dtoFields.get(i); 
      destination.set(entity, origin.get(dto)); 
     } 
     return entity; 
    } 

    public D toDto(E entity) throws ReflectiveOperationException { 
     D dto = dtoConstructor.newInstance(); 
     for (int i = 0; i < entityFields.size(); i++){ 
      Field origin = entityFields.get(i); 
      Field destination = dtoFields.get(i); 
      destination.set(dto, origin.get(entity)); 
     } 
     return dto; 
    } 
} 

回答

1

回答你的問題:是的,這是可能的。您可以要求Byte Buddy爲您創建ITransformer的實例,並在其中實施您所需的唯一方法。但是,您需要實施您自己的Implementation實例。

但是,我不會建議你這樣做。我通常告訴用戶,Byte Buddy不應該用於表演工作,對於大多數用例,這是真的。你的用例就是其中之一。

如果你實現了類,你將不得不緩存這些類的任何映射。否則,課堂生成費用將成爲重要份額。相反,您寧願要維護一個轉換器來緩存反射API的對象(reflective lookups are the expensive part of your operation, reflective invocation is not so problematic)並重新使用以前查找的值。這樣,您可以獲得性能,而不會將代碼生成拖入應用程序的另一個(複雜)元素中。

+0

首先,基準是一個很好的文檔,謝謝。雖然我理解你緩存Field對象的提議,但我無法完全理解你的意思,「如果你實現了類,你將不得不緩存這些類的任何映射,否則,類生成 - 成本將是重要的份額」 。 ITransformer實現的類生成在服務器啓動時只發生一次,然後創建這些類的Spring bean。 – gabrielgiussi

+0

現在仍然存在的問題是,如果減少樣板代碼的數量(不再有多個Transformer類),因爲像基準測試顯示的那樣,reflectionAccessible需要的時間是正常字段訪問的兩倍。我們需要考慮到這種轉換髮生在對我的服務器的每個請求中。這實際上可能會影響性能? – gabrielgiussi