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

1.应用场景

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

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

2.引入依赖

 <!--使用cglib 为javabean动态添加属性-->
 <dependency>
  <groupId>commons-beanutils</groupId>
  <artifactId>commons-beanutils</artifactId>
  <version>1.9.3</version>
 </dependency>
 <dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib-nodep</artifactId>
  <version>3.2.4</version>
 </dependency>

3.代码如下

 import com.freemud.waimai.menu.dpzhcto.dto.DynamicBean;
 import com.google.common.collect.Maps;
 import org.apache.commons.beanutils.PropertyUtilsBean;
 import java.beans.PropertyDescriptor;
 import java.util.Map;
  public class PicBeanAddPropertiesUtil {
  public static Object getTarget(Object dest, Map<String, Object> addProperties) {
  // get property map
  PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
  PropertyDescriptor[] descriptors = propertyUtilsBean.getPropertyDescriptors(dest);
  Map<String, Class> propertyMap = Maps.newHashMap();
  for (PropertyDescriptor d : descriptors) {
   if (!"class".equalsIgnoreCase(d.getName())) {
    propertyMap.put(d.getName(), d.getPropertyType());
   }
  }
  // add extra properties
  addProperties.forEach((k, v) -> propertyMap.put(k, v.getClass()));
  // new dynamic bean
   DynamicBean dynamicBean = new DynamicBean(dest.getClass(), propertyMap);
  // add old value
  propertyMap.forEach((k, v) -> {
   try {
    // filter extra properties
    if (!addProperties.containsKey(k)) {
     dynamicBean.setValue(k, propertyUtilsBean.getNestedProperty(dest, k));
    }
   } catch (Exception e) {
    e.printStackTrace();
   }
  });
  // add extra value
  addProperties.forEach((k, v) -> {
   try {
    dynamicBean.setValue(k, v);
   } catch (Exception e) {
    e.printStackTrace();
   }
  });
  Object target = dynamicBean.getTarget();
  return target;
  }
  }
import net.sf.cglib.beans.BeanGenerator;
import net.sf.cglib.beans.BeanMap;
import java.util.Map;
public 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);
 }
 /**
 * bean 添加属性和值
 *
 * @param property
 * @param value
 */
 public void setValue(String property, Object value) {
  beanMap.put(property, value);
 }
 /**
 * 获取属性值
 *
 * @param property
 * @return
 */
 public Object getValue(String property) {
  return beanMap.get(property);
 }
 /**
 * 获取对象
 *
 * @return
 */
 public Object getTarget() {
  return this.target;
 }
 /**
 * 根据属性生成对象
 *
 * @param superclass
 * @param propertyMap
 * @return
 */
 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 void main(String[] args) {
  FinalPicBaseReqDto entity = new FinalPicBaseReqDto();
  entity.setAppKey("eee");
  entity.setContent("222");
  Map<String, Object> addProperties = new HashMap() {{
   put("动态属性名", "动态属性值");
  }};
  FinalPicBaseReqDto finalPicBaseReqVo = (FinalPicBaseReqDto) PicBeanAddPropertiesUtil.getTarget(entity, addProperties);
  System.out.println(JSON.toJSONString(finalPicBaseReqVo));
 }

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

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

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

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

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

1.cglib

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

输出结果:

name1
getId
getUsername
setId
setUsername

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

2.org.apache.commons.beanutils

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

输入结果:

id:1username:name1

查看BasicDynaBean与BasicDynaClass之间的关系

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

DynaBean的源码

public interface DynaBean {
public boolean contains(String name, String key);
public Object get(String name);
public Object get(String name, int index);
public Object get(String name, String key);
public DynaClass getDynaClass();
public void remove(String name, String key);
public void set(String name, Object value);
public void set(String name, int index, Object value);
public void set(String name, String key, Object value);
}

主要是接口的定义

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

/**
* Return the value of a simple property with the specified name.
*
* @param name Name of the property whose value is to be retrieved
* @return The property's value
*
* @exception IllegalArgumentException if there is no property
* of the specified name
*/public Object get(String name) { // Return any non-null value for the specified property
Object value = values.get(name); if (value != null) { return (value);
} // Return a null value for a non-primitive property
Class<?> type = getDynaProperty(name).getType(); if (!type.isPrimitive()) { return(value);
} // Manufacture default values for primitive properties
if (type == Boolean.TYPE) { return (Boolean.FALSE);
} else if (type == Byte.TYPE) { return (new Byte((byte) 0));
} else if (type == Character.TYPE) { return (new Character((char) 0));
} else if (type == Double.TYPE) { return (new Double(0.0));
} else if (type == Float.TYPE) { return (new Float((float) 0.0));
} else if (type == Integer.TYPE) { return (new Integer(0));
} else if (type == Long.TYPE) { return (new Long(0));
} else if (type == Short.TYPE) { return (new Short((short) 0));
} else { return (null);
}
}

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

/**
* The set of property values for this DynaBean, keyed by property name.
*/
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的区别不大.只是它们都提供了复制属性的工具方法.