我有一个简单的实体用户。
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();
}
}
}
}
但这会很昂贵,因为每次转换都会使用注释。
Byte-buddy可以读取注释并创建一个类转换器,其反编译代码如下所示:
public class TransformerImpl implements ITransformer{
public Object toEntity(Object dto){
User user = new User();
user.setName(dto.getName());
}
}
更新 @Rafael Winterhalter,有这样的事吗?
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;
}
}
答案 0 :(得分:1)
回答你的问题:是的,这是可能的。您可以要求Byte Buddy为您创建ITransformer
的实例,您可以在其中实现唯一的方法来执行您想要的操作。但是,您需要实现自己的Implementation
实例。
但是,我不建议你这样做。我经常告诉用户,Byte Buddy不应该用于性能工作,对于大多数用例,这是事实。您的用例就是其中之一。
如果您实现了类,则必须为任何映射缓存这些类。否则,类生成成本将占很大比例。相反,您宁愿维护一个转换器来缓存反射API(reflective lookups are the expensive part of your operation, reflective invocation is not so problematic)的对象并重用以前查找过的值。这样,您可以获得性能,而无需将代码生成拖动为应用程序的另一个(复杂)元素。