Java cglib為實體類(javabean)動態(tài)添加屬性方式
1.應用場景

之前對接三方平臺遇到一個參數(shù)名稱是變化的,然后我就想到了動態(tài)javabean怎么生成,其實是我想多了,用個map就輕易解決了,但還是記錄下動態(tài)屬性添加的實現(xiàn)吧。
2.引入依賴
<!--使用cglib 為javabean動態(tài)添加屬性--> <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;
}
/**
* 根據(jù)屬性生成對象
*
* @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("動態(tài)屬性名", "動態(tài)屬性值");
}};
FinalPicBaseReqDto finalPicBaseReqVo = (FinalPicBaseReqDto) PicBeanAddPropertiesUtil.getTarget(entity, addProperties);
System.out.println(JSON.toJSONString(finalPicBaseReqVo));
}


可以看到實體類只有兩個屬性,但是最終是動態(tài)添加進去了新的屬性。
聲明:代碼也是前人造的輪子,歡迎各位拿去使用,解決實際生產(chǎn)中遇到的相似場景問題
補充:JavaBean動態(tài)添加刪除屬性
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());
}
輸出結(jié)果:
name1 getId getUsername setId setUsername
從輸出結(jié)果可以看出最后生成的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));
}
輸入結(jié)果:
id:1username:name1
查看BasicDynaBean與BasicDynaClass之間的關系

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是怎么實現(xiàn)的,直接看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來實現(xiàn)的.
3.總結(jié)
用cglib動態(tài)刪除添加屬性時,雖然obj里有g(shù)etUsername這個方法,卻不能obj.getUsername()這樣直接調(diào)用,想得到username的值只能通過beanMap.get("username")獲取.
org.apache.commons.beanutils從源碼來看是使用HashMap來實現(xiàn)的.
兩種方式從操作角度來說和使用Map的區(qū)別不大.只是它們都提供了復制屬性的工具方法.
相關文章
Java對象級別與類級別的同步鎖synchronized語法示例
這篇文章主要為大家介紹了Java對象級別與類級別的同步鎖synchronized語法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-03-03
Spring Boot 2.4版本前后的分組配置變化及對多環(huán)境配置結(jié)構(gòu)的影響(推薦)
這篇文章主要介紹了Spring Boot 2.4版本前后的分組配置變化及對多環(huán)境配置結(jié)構(gòu)的影響,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12
Spring?Cloud?OpenFeign?遠程調(diào)用
這篇文章主要介紹了Spring?Cloud?OpenFeign?遠程調(diào)用,本文通過遠程調(diào)用的GitHub開放API用到的OpenFeign作為示例代碼作為入口進行講解。然后以圖解+解讀源碼的方式深入剖析了OpenFeign的運行機制和架構(gòu)設計,需要的朋友可以參考一下2022-08-08
新建Maven工程出現(xiàn)Process?Terminated的問題解決
當Maven出現(xiàn)"Process terminated"錯誤時,這通常是由于配置文件或路徑錯誤導致的,本文主要介紹了新建Maven工程出現(xiàn)Process?Terminated的問題解決,感興趣的可以了解一下2024-04-04
Redis 訂閱發(fā)布_Jedis實現(xiàn)方法
下面小編就為大家?guī)硪黄猂edis 訂閱發(fā)布_Jedis實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06

