java反射机制及beanUtils的实现原理分析
1.反射机制说明
Java的反射机制允许程序在运行时检查和操作类、方法、字段等结构。通过反射,可以动态地创建对象、调用方法、获取/设置字段的值,而无需在编译时确定这些操作。
反射的核心类是java.lang.reflect
包中的Method
、Field
和Constructor
等。使用反射需要注意性能开销和安全性问题。
- 获取类的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的赋值操作。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
springboot+vue实现Token自动续期(双Token方案)
双Token方案通过访问令牌和刷新令牌提高用户登录安全性和体验,访问令牌有效期短,包含用户信息,用于请求校验,本文就来介绍一下springboot+vue实现Token自动续期(双Token方案),感兴趣的可以了解一下2024-10-10Java中synchronized关键字修饰方法同步的用法详解
synchronized可以用来同步静态和非静态方法,下面就具体来看一下Java中synchronized关键字修饰方法同步的用法详解:2016-06-06SpringBoot接受前台参数的6种方式以及统一响应代码示例
这篇文章主要给大家介绍了关于SpringBoot接受前台参数的6种方式以及统一响应的相关资料,前端负责展示页面和用户交互,而后端则负责处理业务逻辑和数据存储,在这种架构下前端需要将用户输入的数据发送给后端进行处理,需要的朋友可以参考下2023-12-12Spring Boot部署到Tomcat过程中遇到的问题汇总
这篇文章主要给大家分享了关于Spring Boot部署到Tomcat过程中遇到的一些问题,文中将解决的方法介绍非常详细,对同样遇到这个问题的朋友具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。2018-03-03Spring AbstractRoutingDatasource 动态数据源的实例讲解
本文介绍如何使用 Spring AbstractRoutingDatasource 基于上下文动态切换数据源,因此我们会让查找数据源逻辑独立于数据访问之外2021-07-07Spring Boot与Kotlin处理Web表单提交的方法
本篇文章主要介绍了Spring Boot 与 Kotlin 处理Web表单提交的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧2018-01-01
最新评论