首页 > 编程语言 >动态地给Java对象添加字段并赋值

动态地给Java对象添加字段并赋值

时间:2022-11-04 17:25:10浏览次数:36  
标签:Map Java name Object private 字段 import public 赋值

动态地给Java对象添加字段并赋值

一、场景

需求的叙述比较抽象难懂,总之,最后想要的结果就是动态的给对象添加属性,然后返回给前台。

二、思路

搜了一圈,还真有,基于cglib、commons-beanutils库实现
将原对象和扩展字段封装为字段map
基于字段map和原对象创建其子类对象
重新将原字段值和扩展字段值赋给子类对象
返回子类对象

三、实现

maven依赖
(必须显式添加)

<dependency>
 <groupId>commons-beanutils</groupId>
 <artifactId>commons-beanutils</artifactId>
 <version>1.9.3</version>
</dependency>
(用spring的间接依赖也可以,不必显式添加)
<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib-nodep</artifactId>
 <version>3.2.4</version>
</dependency>

代码实现
为了能集中管理,我将所有涉及的类都写在了同一个源文件中,如需测试,可以将下面代码整体拷入一个Java类,解决好依赖,直接执行main函数

package com.tt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class Kill {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static void main(String[] args) throws JsonProcessingException, InvocationTargetException, IllegalAccessException {

        User user = new User();
        user.setName("Daisy");
        System.out.println("User:" + MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(user));

        Map<String, Object> propertiesMap = new HashMap<>(1);
        propertiesMap.put("age", 18);

        Object obj = ReflectUtil.getObject(user, propertiesMap);
        System.err.println("动态为User添加age之后,User:" + MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj));
    }
}

class DynamicBean {

    private Object target;

    private BeanMap beanMap;

    public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
        this.target = generateBean(superclass, propertyMap);
        this.beanMap = BeanMap.create(this.target);
    }

    public void setValue(String property, Object value) {
        beanMap.put(property, value);
    }

    public Object getValue(String property) {
        return beanMap.get(property);
    }

    public Object getTarget() {
        return this.target;
    }

    /**
     * 根据属性生成对象
     */
    private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
        BeanGenerator generator = new BeanGenerator();
        if (null != superclass) {
            generator.setSuperclass(superclass);
        }
        BeanGenerator.addProperties(generator, propertyMap);
        return generator.create();
    }
}

@Slf4j
class ReflectUtil {

    public static Object getObject(Object dest, Map<String, Object> newValueMap) throws InvocationTargetException, IllegalAccessException {
        PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();

        //1.获取原对象的字段数组
        PropertyDescriptor[] descriptorArr = propertyUtilsBean.getPropertyDescriptors(dest);

        //2.遍历原对象的字段数组,并将其封装到Map
        Map<String, Class> oldKeyMap = new HashMap<>(4);
        for (PropertyDescriptor it : descriptorArr) {
            if (!"class".equalsIgnoreCase(it.getName())) {
                oldKeyMap.put(it.getName(), it.getPropertyType());
                newValueMap.put(it.getName(), it.getReadMethod().invoke(dest));
            }
        }

        //3.将扩展字段Map合并到原字段Map中
        newValueMap.forEach((k, v) -> oldKeyMap.put(k, v.getClass()));

        //4.根据新的字段组合生成子类对象
        DynamicBean dynamicBean = new DynamicBean(dest.getClass(), oldKeyMap);

        //5.放回合并后的属性集合
        newValueMap.forEach((k, v) -> {
            try {
                dynamicBean.setValue(k, v);
            } catch (Exception e) {
                log.error("动态添加字段【值】出错", e);
            }
        });
        return dynamicBean.getTarget();
    }
}

class User {

    private String name;

    public String getName() {
        return name;
    }

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

四、效果

User:{
  "name" : "Daisy"
}
动态为User添加age之后,User:{
  "name" : "Daisy",
  "age" : 18
}

五、总结

使用了反射机制
cglib的动态代理等技术(隐含意思就是被处理的类型不能被final关键字修饰)

六、持续优化

实践过程中我对上述代码进行了拆分与抽离,将主要逻辑封装到了一个工具类中,下面是代码:

测试类:

package com.tt;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tt.rest.PropertyAppender;
import com.tt.rest.User0;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class Kill0 {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    public static void main(String[] args) throws JsonProcessingException, InvocationTargetException, IllegalAccessException {

        User0 user = new User0();
        user.setName("Daisy");
        System.out.println(MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(user));

        System.out.println("=====================================");

        Map<String, Object> propertiesMap = new HashMap<>(1);
        propertiesMap.put("age", 18);

        Object obj = PropertyAppender.generate(user, propertiesMap);
        System.err.println(MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj));
    }

}

被添加字段的原始类:

package com.tt;

public class User0 {

    private String name;

    public String getName() {
        return name;
    }

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

工具类:

package com.tt;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public final class PropertyAppender {

    private static final class DynamicBean {

        private Object target;

        private BeanMap beanMap;

        private DynamicBean(Class superclass, Map<String, Class> propertyMap) {
            this.target = generateBean(superclass, propertyMap);
            this.beanMap = BeanMap.create(this.target);
        }

        private void setValue(String property, Object value) {
            beanMap.put(property, value);
        }

        private Object getValue(String property) {
            return beanMap.get(property);
        }

        private Object getTarget() {
            return this.target;
        }

        /**
         * 根据属性生成对象
         */
        private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
            BeanGenerator generator = new BeanGenerator();
            if (null != superclass) {
                generator.setSuperclass(superclass);
            }
            BeanGenerator.addProperties(generator, propertyMap);
            return generator.create();
        }
    }

    public static Object generate(Object dest, Map<String, Object> newValueMap) throws InvocationTargetException, IllegalAccessException {
        PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();

        //1.获取原对象的字段数组
        PropertyDescriptor[] descriptorArr = propertyUtilsBean.getPropertyDescriptors(dest);

        //2.遍历原对象的字段数组,并将其封装到Map
        Map<String, Class> oldKeyMap = new HashMap<>(4);
        for (PropertyDescriptor it : descriptorArr) {
            if (!"class".equalsIgnoreCase(it.getName())) {
                oldKeyMap.put(it.getName(), it.getPropertyType());
                newValueMap.put(it.getName(), it.getReadMethod().invoke(dest));
            }
        }

        //3.将扩展字段Map合并到原字段Map中
        newValueMap.forEach((k, v) -> oldKeyMap.put(k, v.getClass()));

        //4.根据新的字段组合生成子类对象
        DynamicBean dynamicBean = new DynamicBean(dest.getClass(), oldKeyMap);

        //5.放回合并后的属性集合
        newValueMap.forEach((k, v) -> {
            try {
                dynamicBean.setValue(k, v);
            } catch (Exception e) {
                log.error("动态添加字段【值】出错", e);
            }
        });
        return dynamicBean.getTarget();
    }
}

执行效果是这样的

{
"name" : "Daisy"
}
=====================================
{
"name" : "Daisy",
"age" : 18
}

标签:Map,Java,name,Object,private,字段,import,public,赋值
From: https://www.cnblogs.com/fuqian/p/16858476.html

相关文章

  • JAVA遍历Map所有元素
    ```c//JDK1.5Mapmap=newHashMap();for(Objectobj:map.keySet()){Objectkey=obj;Objectvalue=map.get(obj);}``` ``......
  • java面向对象 黑马程序员学习笔记(5)
    多个对象公用的一个不变的值,使用static来修饰static是一个修饰符只能修饰成员,成员变量,成员函数,静态修饰内容被对象所共享当成员被静态修饰后,就多了一个条用方式,除了可以被......
  • java网络编程 tcp 黑马程序员学习笔记(11)
    importjava.io.*;importjava.net.*;//步奏//建立socket对象publicclasstcpclient{publicstaticvoidmain(Stringargs[])throwsException{//创建客户端的s......
  • java 异常处理 黑马程序员学习笔记(6)
    throw和throws的区别throw使用在函数内throws后跟异常类使用,号隔开lruntime类的子类,在函数内抛出,函数上不用抛出、运行时异常,如果在函数内抛出异常/只所以不用在函......
  • 如何通过Java将PDF转为Excel
    当您收到一份PDF格式的表格后,却又想要对表格内容进行某项操作时(例如更改数据、改变表格样式等),将其转换成Excel文档格式可能会更加便捷。FreeSpire.PDFforJava就可以实......
  • 原生javascript清空table表格
    本文主要分享一下原生javascript清空table表格的方法,仅供参考:lettable=document.getElementById("tableId");varlen=table.childNodes.length;for(leti=len-......
  • javaweb期中考试
    bean类packageBean;publicclassbean{privateStringzhuti;privateStringmudi;privateStringhuodongleixing;privateStringtime;privateStringdidian;privat......
  • Java常见错误种类
    算术异常类:ArithmeticExecption空指针异常类:NullPointerException类型强制转换异常:ClassCastException数组负下标异常:NegativeArrayException数组下标越界异常:ArrayInd......
  • Java入门与进阶
    面向对象与Java基础知识体系系统性梳理 Java入门:你可能会注意到,面向对象与Java基础 这个章节写的非常简单,为什么呢?因为就语法本身而言,大多数人入门一门语言只需......
  • Stream通过某个字段过滤重复元素
    List<Book>list=newArrayList<>();list.add(newBook("apple"));list.add(newBook("apple"));list.add(newBook("pear"));lis......