Java cglib为实体类(javabean)动态添加属性方式

时间:2022-03-11 18:17:43

1.应用场景

Java cglib为实体类(javabean)动态添加属性方式

之前对接三方平台遇到一个参数名称是变化的,然后我就想到了动态javabean怎么生成,其实是我想多了,用个map就轻易解决了,但还是记录下动态属性添加的实现吧。

2.引入依赖

  1. <!--使用cglib 为javabean动态添加属性-->
  2. <dependency>
  3. <groupId>commons-beanutils</groupId>
  4. <artifactId>commons-beanutils</artifactId>
  5. <version>1.9.3</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>cglib</groupId>
  9. <artifactId>cglib-nodep</artifactId>
  10. <version>3.2.4</version>
  11. </dependency>

3.代码如下

  1. import com.freemud.waimai.menu.dpzhcto.dto.DynamicBean;
  2. import com.google.common.collect.Maps;
  3. import org.apache.commons.beanutils.PropertyUtilsBean;
  4. import java.beans.PropertyDescriptor;
  5. import java.util.Map;
  6. public class PicBeanAddPropertiesUtil {
  7. public static Object getTarget(Object dest, Map<String, Object> addProperties) {
  8. // get property map
  9. PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
  10. PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
  11. Map<String, Class> propertyMap = Maps.newHashMap();
  12. for (PropertyDescriptor d : descriptors) {
  13. if (!"class".equalsIgnoreCase(d.getName())) {
  14. propertyMap.put(d.getName(), d.getPropertyType());
  15. }
  16. }
  17. // add extra properties
  18. addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass()));
  19. // new dynamic bean
  20. DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap);
  21. // add old value
  22. propertyMap.forEach((k, v) -> {
  23. try {
  24. // filter extra properties
  25. if (!addProperties.containsKey(k)) {
  26. dynamicBean.setValue(k, propertyUtilsBean.getNestedProperty(dest, k));
  27. }
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. });
  32. // add extra value
  33. addProperties.forEach((k, v) -> {
  34. try {
  35. dynamicBean.setValue(k, v);
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. });
  40. Object target = dynamicBean.getTarget();
  41. return target;
  42. }
  43. }
  1. import net.sf.cglib.beans.BeanGenerator;
  2. import net.sf.cglib.beans.BeanMap;
  3. import java.util.Map;
  4. public class DynamicBean {
  5. /**
  6. * 目标对象
  7. */
  8. private Object target;
  9. /**
  10. * 属性集合
  11. */
  12. private BeanMap beanMap;
  13. public DynamicBean(Class superclass, Map<String, Class> propertyMap) {
  14. this.target = generateBean(superclass, propertyMap);
  15. this.beanMap = BeanMap.create(this.target);
  16. }
  17. /**
  18. * bean 添加属性和值
  19. *
  20. * @param property
  21. * @param value
  22. */
  23. public void setValue(String property, Object value) {
  24. beanMap.put(property, value);
  25. }
  26. /**
  27. * 获取属性值
  28. *
  29. * @param property
  30. * @return
  31. */
  32. public Object getValue(String property) {
  33. return beanMap.get(property);
  34. }
  35. /**
  36. * 获取对象
  37. *
  38. * @return
  39. */
  40. public Object getTarget() {
  41. return this.target;
  42. }
  43. /**
  44. * 根据属性生成对象
  45. *
  46. * @param superclass
  47. * @param propertyMap
  48. * @return
  49. */
  50. private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
  51. BeanGenerator generator = new BeanGenerator();
  52. if (null != superclass) {
  53. generator.setSuperclass(superclass);
  54. }
  55. BeanGenerator.addProperties(generator, propertyMap);
  56. return generator.create();
  57. }
  58. }
  59. public static void main(String[] args) {
  60. FinalPicBaseReqDto entity = new FinalPicBaseReqDto();
  61. entity.setAppKey("eee");
  62. entity.setContent("222");
  63. Map<String, Object> addProperties = new HashMap() {{
  64. put("动态属性名", "动态属性值");
  65. }};
  66. FinalPicBaseReqDto finalPicBaseReqVo = (FinalPicBaseReqDto) PicBeanAddPropertiesUtil.getTarget(entity, addProperties);
  67. System.out.println(JSON.toJSONString(finalPicBaseReqVo));
  68. }

Java cglib为实体类(javabean)动态添加属性方式

Java cglib为实体类(javabean)动态添加属性方式

可以看到实体类只有两个属性,但是最终是动态添加进去了新的属性。

声明:代码也是前人造的*,欢迎各位拿去使用,解决实际生产中遇到的相似场景问题

补充:JavaBean动态添加删除属性

1.cglib

  1. BeanGenerator beanGenerator = new BeanGenerator();
  2. beanGenerator.addProperty("id", Long.class);
  3. beanGenerator.addProperty("username", String.class);
  4. Object obj = beanGenerator.create();
  5. BeanMap beanMap = BeanMap.create(obj);
  6. BeanCopier copier = BeanCopier.create(User.class, obj.getClass(), false);
  7. User user = new User();
  8. user.setId(1L);
  9. user.setUsername("name1");
  10. user.setPassword("123");
  11. copier.copy(user, obj, null);
  12. System.out.println(beanMap.get("username"));Class clazz = obj.getClass();
  13. Method[] methods = clazz.getDeclaredMethods();for (int i = 0; i < methods.length; i++) {
  14. System.out.println(methods[i].getName());
  15. }

输出结果:

  1. name1
  2. getId
  3. getUsername
  4. setId
  5. setUsername

从输出结果可以看出最后生成的obj只有id和username两个属性

2.org.apache.commons.beanutils

  1. DynaProperty property = new DynaProperty("id", Long.class);
  2. DynaProperty property1 = new DynaProperty("username", String.class);
  3. BasicDynaClass basicDynaClass = new BasicDynaClass("user", null, newDynaProperty[]{property, property1});
  4. BasicDynaBean basicDynaBean = new BasicDynaBean(basicDynaClass);
  5. User user = new User();
  6. user.setId(1L);
  7. user.setUsername("name1");
  8. user.setPassword("123");
  9. BeanUtils.copyProperties(basicDynaBean, user);Map<String, Object> map = basicDynaBean.getMap();
  10. Iterator<String> it = map.keySet().iterator();while (it.hasNext()) { String key = it.next();
  11. System.out.println(key + ":" + map.get(key));
  12. }

输入结果:

  1. id:1username:name1

查看BasicDynaBean与BasicDynaClass之间的关系

Java cglib为实体类(javabean)动态添加属性方式

DynaBean的源码

  1. public interface DynaBean {
  2. public boolean contains(String name, String key);
  3. public Object get(String name);
  4. public Object get(String name, int index);
  5. public Object get(String name, String key);
  6. public DynaClass getDynaClass();
  7. public void remove(String name, String key);
  8. public void set(String name, Object value);
  9. public void set(String name, int index, Object value);
  10. public void set(String name, String key, Object value);
  11. }

主要是接口的定义

再来看看BasicDynaBean是怎么实现的,直接看public Object get(String name);

  1. /**
  2. * Return the value of a simple property with the specified name.
  3. *
  4. * @param name Name of the property whose value is to be retrieved
  5. * @return The property's value
  6. *
  7. * @exception IllegalArgumentException if there is no property
  8. * of the specified name
  9. */public Object get(String name) { // Return any non-null value for the specified property
  10. Object value = values.get(name); if (value != null) { return (value);
  11. } // Return a null value for a non-primitive property
  12. Class<?> type = getDynaProperty(name).getType(); if (!type.isPrimitive()) { return(value);
  13. } // Manufacture default values for primitive properties
  14. if (type == Boolean.TYPE) { return (Boolean.FALSE);
  15. } else if (type == Byte.TYPE) { return (new Byte((byte) 0));
  16. } else if (type == Character.TYPE) { return (new Character((char) 0));
  17. } else if (type == Double.TYPE) { return (new Double(0.0));
  18. } else if (type == Float.TYPE) { return (new Float((float) 0.0));
  19. } else if (type == Integer.TYPE) { return (new Integer(0));
  20. } else if (type == Long.TYPE) { return (new Long(0));
  21. } else if (type == Short.TYPE) { return (new Short((short) 0));
  22. } else { return (null);
  23. }
  24. }

从以上代码可以看出是在values里取值的

  1. /**
  2. * The set of property values for this DynaBean, keyed by property name.
  3. */
  4. protected HashMap<String, Object> values = new HashMap<String, Object>();

其实是用HashMap来实现的.

3.总结

用cglib动态删除添加属性时,虽然obj里有getUsername这个方法,却不能obj.getUsername()这样直接调用,想得到username的值只能通过beanMap.get("username")获取.

org.apache.commons.beanutils从源码来看是使用HashMap来实现的.

两种方式从操作角度来说和使用Map的区别不大.只是它们都提供了复制属性的工具方法.

原文链接:https://www.jianshu.com/p/cc1014e71e8a