java反射机制及beanUtils的实现原理分析

 更新时间:2024年12月10日 14:53:07   作者:linab112  
本文介绍了Java的反射机制、VO、DTO、PO的概念以及BeanUtils的实现原理和简单示例,通过反射可以在运行时动态操作类、方法和字段,BeanUtils用于在不同bean之间进行属性复制

1.反射机制说明

Java的反射机制允许程序在运行时检查和操作类、方法、字段等结构。通过反射,可以动态地创建对象、调用方法、获取/设置字段的值,而无需在编译时确定这些操作。

反射的核心类是java.lang.reflect包中的MethodFieldConstructor等。使用反射需要注意性能开销和安全性问题。

  • 获取类的Class对象
Class<?> clazz = MyClass.class;
  • 实例化对象
MyClass myObject = (MyClass) clazz.getDeclaredConstructor().newInstance();
  • 获取和调用方法
Method method = clazz.getDeclaredMethod("methodName", parameterTypes);
method.setAccessible(true); // 如果方法是private的,需要设置accessible为true
Object result = method.invoke(myObject, args);
  • 获取和设置字段值
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true); // 如果字段是private的,需要设置accessible为true
Object value = field.get(myObject);
field.set(myObject, newValue);
  • 操作构造函数
Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true); // 如果构造函数是private的,需要设置accessible为true
MyClass myObject = (MyClass) constructor.newInstance(args);

2.VO,DTO,PO的说明

VO(Value Object)值对象

VO就是展示用的数据,不管展示方式是网页,还是客户端,还是APP,只要是这个东西是让人看到的,这就叫VO,这个大家都很理解,反正就是我们的接口返回给前端的对象都是用VO来返回,跟DTO不一样的是,VO是我们返回给前端,DTO是我们从前端接收的时候用的,即一个是入参,一个是返回结果

DTO(Data Transfer Object)数据传输对象

这个传输通常指的前后端之间的传输

DTO是一个比较特殊的对象,他有两种存在形式:

  • 一种是前端和后端交互所使用的对象
  • 另一种是微服务之间的一种传输对象,我们一般也是用DTO来进行传输

PO(Persistant Object)持久对象

PO比较好理解,简单说PO就是数据库中的记录,一个PO的数据结构对应着库中表的结构,表中的一条记录就是一个PO对象,通常PO里面除了get,set之外没有别的方法,对于PO来说,数量是相对固定的,一定不会超过数据库表的数量,等同于BO,这俩概念是一致的

3.beanUtils的实现原理

在后端的各个层中进行数据传输时,经常使用beanUtils进行bean的拷贝,其实现原理就是通过java的放射机制实现。

package org.springframework.beans;

    private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable 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;
        }

        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null) {
                        ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
                        ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
                        boolean isAssignable = !sourceResolvableType.hasUnresolvableGenerics() && !targetResolvableType.hasUnresolvableGenerics() ? targetResolvableType.isAssignableFrom(sourceResolvableType) : ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType());
                        if (isAssignable) {
                            try {
                                if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                    readMethod.setAccessible(true);
                                }

                                Object value = readMethod.invoke(source);
                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                    writeMethod.setAccessible(true);
                                }

                                writeMethod.invoke(target, value);
                            } catch (Throwable var18) {
                                throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var18);
                            }
                        }
                    }
                }
            }
        }

    }

4.beanUtils的简单示例

public class BeanToolUtils {

    public static void copy(Object source, Object target) throws Exception {
        Class<?> sourceClass = source.getClass();
        Class<?> targeteClass = target.getClass();
        Field[] fields = targeteClass.getDeclaredFields();
        // 输出字段信息
        for (Field field : fields) {
            String name = field.getName();
            if ("serialVersionUID".equals(name)) {
                continue;
            }
            String getterName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
            String setterName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);

            Method getMethod = sourceClass.getMethod(getterName);
            if(!ObjectUtils.isEmpty(getMethod)){
                Object val = getMethod.invoke(source);

                Method setMethod = targeteClass.getMethod(setterName,field.getType());
                setMethod.invoke(target, val);
            }

        }
    }
}

说明:

获取目标bean的class对象,通过class对象获取目标bean的所有属性,循环属性信息,获取属性的get和set方法,执行来源bean的get方法获取属性值,执行目标bean的set方法,设置属性值,完成bean的赋值操作。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java中静态导入机制用法实例详解

    java中静态导入机制用法实例详解

    这篇文章主要介绍了java中静态导入机制用法实例详解的相关资料,需要的朋友可以参考下
    2017-07-07
  • SpringCloud组件性能优化的技巧

    SpringCloud组件性能优化的技巧

    这篇文章主要介绍了SpringCloud组件性能优化的技巧,Springcloud 原始的配置,性能是很低的,大家可以使用 Jmeter 测试一下,QPS 不会到 50,要做到高并发,需要做不少的配置优化,需要的朋友可以参考下
    2023-09-09
  • Seata AT模式前后镜像是如何生成详解

    Seata AT模式前后镜像是如何生成详解

    这篇文章主要为大家介绍了Seata AT模式前后镜像是如何生成的方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • java几种排序算法的实现及简单分析

    java几种排序算法的实现及简单分析

    这篇文章主要介绍了java几种排序算法的实现及简单分析,实例分析了插入排序、希尔排序、选择排序等常用排序算法,并分析了各个算法的优劣,需要的朋友可以参考下
    2015-05-05
  • MyBatis的模糊查询mapper.xml的写法讲解

    MyBatis的模糊查询mapper.xml的写法讲解

    这篇文章主要介绍了MyBatis的模糊查询mapper.xml的写法讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 解决MyEclipse10.7部署报错抛空指针异常问题的方法

    解决MyEclipse10.7部署报错抛空指针异常问题的方法

    这篇文章主要介绍了解决MyEclipse10.7部署报错抛空指针异常问题的方法,需要的朋友可以参考下
    2015-12-12
  • 解决javaBean规范导致json传参首字母大写将永远获取不到问题

    解决javaBean规范导致json传参首字母大写将永远获取不到问题

    这篇文章主要介绍了解决javaBean规范导致json传参首字母大写将永远获取不到问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Spring JDK动态代理实现过程详解

    Spring JDK动态代理实现过程详解

    这篇文章主要介绍了Spring JDK动态代理实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Java fastdfs客户端实现上传下载文件

    Java fastdfs客户端实现上传下载文件

    这篇文章主要介绍了Java fastdfs客户端实现上传下载文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Spring中事务传播行为的介绍

    Spring中事务传播行为的介绍

    今天小编就为大家分享一篇关于Spring中事务传播行为的介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01

最新评论