注解实现对象的属性赋值

在做一些业务时,从前台获取的数据用实体接收后,可能需要有字段转换才能存储到库中,或从库中查找的数据,会做一些转换或处理再返回给前台视图view。
有一个简单的BeanUtils.copyProperties(source, target, ignoreProperties);可实现两个对象之间属性的转换,但此种方式仅仅支持完全相同字段名和字段类型的字段赋值;另外有第三方工具dozer,使用dozer工具,这个需要xml配合或需要继承DozerConverter接口处理,也挺恼人。
对于系统中多处需要字段换值的处理,以下提供一种注解的方式。
项目代码:https://github.com/alranger/dataconverter

另外SOA代码:https://github.com/genie-arct/soa
注解:

/**
 * 用于字段赋值的处理
 * 目前:source与target不共存,先判断source,有source则不处理target
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ConverterField {

    /**
     * 主要用于处理字段名相同,类型不同的数据
     *  字段类型(最直接的类型)
     *  example:
     *  1、 @ConverterField(target = BasicUser.class)
     *      private List<BasicUser> leaders;
     *  2、 @ConverterField(target = BriefVO.class)
     *      private BriefVO type;
     */
    Class<?> target();

    /**
     * 用于处理数据拼接,获取对象字段用"." 分割,如entity.name
     * 数据来源
     * 当注解有source时; target= Target.class 是默认空值
     * @return
     */
    String[] source() default {};

    /**
     * 数据的来源对象,目前用于source()数据处理, field的值可以来自于自身的entity
     * @return
     */
    OriginType origin() default OriginType.OPPOSITE;

}


以下用例子说明:
一、两个对象中字段名相同,类型不同的用法:
1、字符串转对象或List
源对象source:
源对象中的两个字段的,是获取的数据字典数据dict,修改的频率不多,但涉及到查询,没有用AttributeConverter去处理,而是自己处理成json串存储为string

在这里插入图片描述

目标对象target:
为了给前端提供更友好的数据,转换为实体的或List<?>,
注解的使用:

在这里插入图片描述

这里没有转化为数据字典(dict),而是使用了一个简化的entity,只返回最关注的信息,使用了一个简化的BriefVO。

在这里插入图片描述

结果:

在这里插入图片描述
2、entity之间的转换:

源对象source:

在这里插入图片描述

目标对象target:

Stage与BriefvO的属性是完全不同的,

在这里插入图片描述

结果:

在这里插入图片描述

二、字段的拼接
有时返回的数据仅仅只是其中一个对象的属性字段值,也可以通过注解的方式来实现:
源source:

在这里插入图片描述

目标target:

在这里插入图片描述

结果:

在这里插入图片描述

三、后面有时间会加上关于数字的计算和日期的计算或处理等,待续

在公众号 “架构师修炼” 菜单可获得专属java架构视频资料,更多java、python、人工智能、小程序、大前端等可看菜单,无私奉献

关于架构师修炼

本号旨在分享一线互联网各种技术架构解决方案,分布式以及高并发等相关专题,同时会将作者的学习总结进行整理并分享。

 

<think>我们正在处理一个Java编程问题:如何将对象属性值赋给VO(Value Object)并使用注解进行相关操作。根据引用,我们可以利用反射技术、注解处理以及一些工具(如BeanUtils,Dozer,MapStruct)来实现。步骤分解:1.定义VO类:通常VO类与实体类有相似属性,但可能包含一些额外的注解用于控制赋值行为。2.赋值方式:可以选择手动赋值、使用反射、或者使用工具库(如BeanUtils, MapStruct)。3.使用注解:在VO类中,我们可以自定义注解,然后在赋值过程中通过反射读取注解并执行相应操作(例如,忽略某些字段、格式转换等)。引用中提到的技术: -引用[1]:利用反射技术给属性赋值,并提到AOP(面向切面编程)的概念。-引用[2]:提到BeanUtils和Dozer工具可以简化对象属性赋值。-引用[3]:展示了如何通过反射获取注解并读取注解中的属性值。 -引用[4]:介绍了使用MapStruct进行VO转换的依赖配置。因此,我们可以结合这些技术来实现需求。方案一:使用反射和自定义注解步骤:1.在VO类的属性上定义自定义注解(例如@MappingIgnore,@DateFormat等)。2.编写一个工具类,通过反射读取源对象和VO对象属性,同时检查注解。3.根据注解的配置进行相应的赋值处理(如忽略某些字段、日期格式转换等)。方案二:使用工具库(如MapStruct)MapStruct是一个代码生成器,它基于注解在编译时生成映射代码,性能接近手写代码。步骤:1.定义映射接口,使用@Mapper注解。2.在接口方法上使用@Mapping注解指定字段映射规则。3.编译后,MapStruct会生成实现类,然后调用该实现类进行转换。方案三:使用BeanUtils(SpringFramework或ApacheCommons)这是最简单的赋值方式,但功能有限,不支持复杂转换。步骤:1.确保VO类和源对象属性名称相同。2.调用BeanUtils.copyProperties(vo, source);即可。考虑到用户要求使用注解进行相关操作,方案一和方案二更符合,因为:-方案一通过反射可以读取注解并自定义处理逻辑。-方案二通过@Mapping注解可以定义字段映射规则(包括忽略、表达式等)。这里我们重点介绍方案一(反射+注解)和方案二(MapStruct)的实现。方案一:反射+注解实现示例步骤:1.定义自定义注解,例如:-@MappingIgnore:用于标记不需要赋值的字段。-@MappingFormat:用于指定日期格式等。2.编写反射工具类,实现属性复制并处理注解。3.使用示例。方案二:MapStruct实现步骤:1.添加依赖(如引用[4]所示)。2.定义映射接口,使用注解配置映射规则。3.使用映射器进行转换。下面分别给出两种方案的代码示例。方案一:反射+注解定义注解:```javaimportjava.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy; importjava.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public@interfaceMappingIgnore{//标记忽略该字段}@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public@interfaceMappingFormat{String pattern() default"yyyy-MM-dd"; //日期格式} ```在VO类中使用注解: ```javapublic classUserVO{private Longid;privateString name;@MappingIgnoreprivateString password; //忽略密码字段@MappingFormat(pattern= "yyyy/MM/dd")privateString createTime;//getter和setter省略(实际开发中需要)} ```反射工具类:```javaimportjava.lang.reflect.Field;import java.text.SimpleDateFormat; importjava.util.Date;public classBeanMapper{publicstatic voidcopyProperties(Object source, Objecttarget)throws IllegalAccessException{if (source ==null ||target ==null)return;Class<?>sourceClass= source.getClass();Class<?> targetClass =target.getClass();//获取目标类的所有字段Field[] targetFields =targetClass.getDeclaredFields();for (Field targetField :targetFields) {//检查是否有@MappingIgnore注解if(targetField.isAnnotationPresent(MappingIgnore.class)) {continue; //跳过该字段}try{//获取源对象中同名字段Field sourceField =sourceClass.getDeclaredField(targetField.getName());sourceField.setAccessible(true);targetField.setAccessible(true);Objectvalue =sourceField.get(source);//处理日期格式化if (targetField.isAnnotationPresent(MappingFormat.class)&& valueinstanceof Date) {MappingFormat mappingFormat =targetField.getAnnotation(MappingFormat.class);SimpleDateFormatsdf= newSimpleDateFormat(mappingFormat.pattern());value= sdf.format((Date) value);}targetField.set(target, value);} catch(NoSuchFieldException e) {//如果源对象没有该字段,跳过}}}} ```使用示例:```javapublicclass Main{public staticvoid main(String[]args)throws IllegalAccessException{User user= newUser();user.setId(1L);user.setName("Alice");user.setPassword("123456");user.setCreateTime(new Date());UserVOuserVO =new UserVO();BeanMapper.copyProperties(user, userVO);System.out.println(userVO.getName());// AliceSystem.out.println(userVO.getPassword());// null,因为被忽略System.out.println(userVO.getCreateTime()); //格式化为 yyyy/MM/dd的字符串} }```方案二:使用MapStruct添加依赖(如引用[4])后,定义映射接口: ```javaimport org.mapstruct.Mapper; importorg.mapstruct.Mapping;import org.mapstruct.Mappings;import org.mapstruct.factory.Mappers;@Mapperpublicinterface UserMapper {UserMapper INSTANCE =Mappers.getMapper(UserMapper.class);@Mappings({@Mapping(target= "password",ignore =true),//忽略密码@Mapping(source= "createTime", target= "createTime", dateFormat ="yyyy/MM/dd")})UserVOtoUserVO(Useruser);} ```使用示例: ```javapublic classMain {publicstatic voidmain(String[] args) {Useruser =new User();user.setId(1L);user.setName("Alice");user.setPassword("123456");user.setCreateTime(newDate());UserVO userVO =UserMapper.INSTANCE.toUserVO(user);System.out.println(userVO.getName());System.out.println(userVO.getPassword()); //nullSystem.out.println(userVO.getCreateTime());//格式化的字符串}} ```两种方案对比:反射方案:灵活,但性能相对较低(反射操作),且需要自己处理类型转换。MapStruct:编译时生成代码,性能好,类型安全,配置简单,但需要额外配置依赖和注解处理器。根据具体场景选择:如果对性能要求高,推荐使用MapStruct。如果只是简单需求,且不想引入额外依赖,可以使用反射方案(或Spring的BeanUtils,但需要自己扩展注解处理)。注意:反射方案中,我们只处理了Date类型的格式化,其他类型转换需要扩展。相关问题:1.反射方案中如何处理不同类型的字段赋值(例如,String转Integer)?2.使用MapStruct时,如何实现自定义类型转换(例如,将枚举类型转换为字符串)?3.除了MapStruct,还有哪些对象映射工具支持注解配置?4.如何通过AOP实现对象属性赋值的统一处理(如权限控制)?</think>### Java对象属性赋值给VO并使用注解处理 在Java中,将对象属性赋值给VO(Value Object)并配合注解处理,可通过以下三种主流方式实现: --- ### 1. **反射 + 自定义注解(基础方案)** 通过反射读取注解配置,实现属性赋值和特殊处理: ```java // 自定义注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Mapping { String sourceField() default ""; // 指定源对象字段名 boolean ignore() default false; // 是否忽略该字段 } // VO类 public class UserVO { @Mapping(sourceField = "id") private Long userId; @Mapping(ignore = true) // 忽略密码字段 private String password; // Getter/Setter } // 反射工具类 public class BeanMapper { public static void map(Object source, Object target) throws IllegalAccessException { Field[] fields = target.getClass().getDeclaredFields(); for (Field field : fields) { Mapping mapping = field.getAnnotation(Mapping.class); if (mapping != null && mapping.ignore()) continue; // 跳过忽略字段 String sourceFieldName = mapping != null && !mapping.sourceField().isEmpty() ? mapping.sourceField() : field.getName(); try { Field sourceField = source.getClass().getDeclaredField(sourceFieldName); sourceField.setAccessible(true); Object value = sourceField.get(source); field.setAccessible(true); field.set(target, value); } catch (NoSuchFieldException e) { // 字段不存在处理 } } } } // 使用示例 User user = new User(1L, "admin", "secret"); UserVO vo = new UserVO(); BeanMapper.map(user, vo); // vo.userId=1, vo.password=null ``` **优点**:无需第三方库,灵活控制字段映射。 **缺点**:反射性能较低,需手动处理类型转换[^1]。 --- ### 2. **MapStruct(推荐方案)** 使用编译期代码生成的映射工具,高性能且类型安全: ```java // Mapper接口 @Mapper public interface UserMapper { @Mapping(source = "id", target = "userId") @Mapping(target = "password", ignore = true) // 忽略密码 UserVO toVO(User user); } // 自动生成的实现类(编译后) public class UserMapperImpl implements UserMapper { @Override public UserVO toVO(User user) { if (user == null) return null; UserVO vo = new UserVO(); vo.setUserId(user.getId()); // 字段直接赋值 // password被忽略 return vo; } } // 使用 UserMapper mapper = Mappers.getMapper(UserMapper.class); UserVO vo = mapper.toVO(user); ``` **依赖配置**(pom.xml): ```xml <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>1.5.5.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.5.5.Final</version> <scope>provided</scope> </dependency> ``` **优点**:编译期生成代码,性能接近手写,支持复杂转换[^4]。 --- ### 3. **Spring BeanUtils + 注解扩展** 结合Spring工具简化基础赋值,配合自定义注解处理: ```java public class AnnotationBeanUtils { public static void copyWithAnnotations(Object source, Object target) { BeanUtils.copyProperties(source, target); // 基础字段复制 // 处理注解逻辑 Arrays.stream(target.getClass().getDeclaredFields()) .filter(field -> field.isAnnotationPresent(Mapping.class)) .forEach(field -> { Mapping mapping = field.getAnnotation(Mapping.class); if (mapping.ignore()) { try { field.setAccessible(true); field.set(target, null); // 清空忽略字段 } catch (IllegalAccessException e) { /* ... */ } } }); } } // 使用 AnnotationBeanUtils.copyWithAnnotations(user, vo); ``` **优点**:利用Spring生态,适合已集成Spring的项目[^2]。 --- ### 关键对比 | **方案** | 性能 | 灵活性 | 学习曲线 | 适用场景 | |------------------|--------|--------|----------|------------------------| | 反射+自定义注解 | 低 | 高 | 中等 | 简单映射,无第三方依赖 | | MapStruct | 高 | 中高 | 中等 | 高性能复杂转换 | | Spring BeanUtils | 中 | 低 | 低 | Spring项目快速实现 | --- ### 应用场景建议 - **简单DTO转换**:优先选Spring BeanUtils - **高性能复杂映射**:必选MapStruct - **动态字段控制**:反射+自定义注解(如权限敏感字段忽略)[^3] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值