Java cglib為實(shí)體類(javabean)動(dòng)態(tài)添加屬性方式
1.應(yīng)用場景
之前對接三方平臺(tái)遇到一個(gè)參數(shù)名稱是變化的,然后我就想到了動(dòng)態(tài)javabean怎么生成,其實(shí)是我想多了,用個(gè)map就輕易解決了,但還是記錄下動(dòng)態(tài)屬性添加的實(shí)現(xiàn)吧。
2.引入依賴
<!--使用cglib 為javabean動(dòng)態(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 { /** * 目標(biāo)對象 */ 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("動(dòng)態(tài)屬性名", "動(dòng)態(tài)屬性值"); }}; FinalPicBaseReqDto finalPicBaseReqVo = (FinalPicBaseReqDto) PicBeanAddPropertiesUtil.getTarget(entity, addProperties); System.out.println(JSON.toJSONString(finalPicBaseReqVo)); }
可以看到實(shí)體類只有兩個(gè)屬性,但是最終是動(dòng)態(tài)添加進(jìn)去了新的屬性。
聲明:代碼也是前人造的輪子,歡迎各位拿去使用,解決實(shí)際生產(chǎn)中遇到的相似場景問題
補(bǔ)充:JavaBean動(dòng)態(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兩個(gè)屬性
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之間的關(guān)系
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是怎么實(shí)現(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>();
其實(shí)是用HashMap來實(shí)現(xiàn)的.
3.總結(jié)
用cglib動(dòng)態(tài)刪除添加屬性時(shí),雖然obj里有g(shù)etUsername這個(gè)方法,卻不能obj.getUsername()這樣直接調(diào)用,想得到username的值只能通過beanMap.get("username")獲取.
org.apache.commons.beanutils從源碼來看是使用HashMap來實(shí)現(xiàn)的.
兩種方式從操作角度來說和使用Map的區(qū)別不大.只是它們都提供了復(fù)制屬性的工具方法.
相關(guān)文章
Java對象級別與類級別的同步鎖synchronized語法示例
這篇文章主要為大家介紹了Java對象級別與類級別的同步鎖synchronized語法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03Spring Boot 2.4版本前后的分組配置變化及對多環(huán)境配置結(jié)構(gòu)的影響(推薦)
這篇文章主要介紹了Spring Boot 2.4版本前后的分組配置變化及對多環(huán)境配置結(jié)構(gòu)的影響,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Spring?Cloud?OpenFeign?遠(yuǎn)程調(diào)用
這篇文章主要介紹了Spring?Cloud?OpenFeign?遠(yuǎn)程調(diào)用,本文通過遠(yuǎn)程調(diào)用的GitHub開放API用到的OpenFeign作為示例代碼作為入口進(jìn)行講解。然后以圖解+解讀源碼的方式深入剖析了OpenFeign的運(yùn)行機(jī)制和架構(gòu)設(shè)計(jì),需要的朋友可以參考一下2022-08-08新建Maven工程出現(xiàn)Process?Terminated的問題解決
當(dāng)Maven出現(xiàn)"Process terminated"錯(cuò)誤時(shí),這通常是由于配置文件或路徑錯(cuò)誤導(dǎo)致的,本文主要介紹了新建Maven工程出現(xiàn)Process?Terminated的問題解決,感興趣的可以了解一下2024-04-04Redis 訂閱發(fā)布_Jedis實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄猂edis 訂閱發(fā)布_Jedis實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06Java WebService 簡單實(shí)例(附實(shí)例代碼)
本篇文章主要介紹了Java WebService 簡單實(shí)例(附實(shí)例代碼), Web Service 是一種新的web應(yīng)用程序分支,他們是自包含、自描述、模塊化的應(yīng)用,可以發(fā)布、定位、通過web調(diào)用。有興趣的可以了解一下2017-01-01