摘要:關(guān)于它的數(shù)據(jù)轉(zhuǎn)換使用了如下兩種機制隸屬于規(guī)范。這種類中的方法主要用于訪問私有的字段,且方法名符合某種命名規(guī)則。如果在兩個模塊之間傳遞信息,可以將信息封裝進中,這種對象稱為值對象,或。
每篇一句
千古以來要飯的沒有要早飯的,知道為什么嗎?相關(guān)閱讀
【小家Spring】聊聊Spring中的數(shù)據(jù)轉(zhuǎn)換:Converter、ConversionService、TypeConverter、PropertyEditor
【小家Spring】聊聊Spring中的數(shù)據(jù)綁定 --- 屬性訪問器PropertyAccessor和實現(xiàn)類DirectFieldAccessor的使用
這篇文章需要依賴于對屬性訪問器PropertyAccessor的理解,也就是上篇文章的內(nèi)容:【小家Spring】聊聊Spring中的數(shù)據(jù)綁定 --- 屬性訪問器PropertyAccessor和實現(xiàn)類DirectFieldAccessor的使用
如果說上篇文章所說的PropertyAccessor你沒有接觸過和聽過,那么本文即將要說的重點:BeanWrapper你應(yīng)該多少有所耳聞吧~
BeanWrapper可以簡單的把它理解為:一個方便開發(fā)人員使用字符串來對Java Bean的屬性執(zhí)行g(shù)et、set操作的工具。關(guān)于它的數(shù)據(jù)轉(zhuǎn)換使用了如下兩種機制:
PropertyEditor:隸屬于Java Bean規(guī)范。PropertyEditor只提供了String <-> Object的轉(zhuǎn)換。
ConversionService:Spring自3.0之后提供的替代PropertyEditor的機制(BeanWrapper在Spring的第一個版本就存在了~)
按照Spring官方文檔的說法,當容器內(nèi)沒有注冊ConversionService的時候,會退回使用PropertyEditor機制。言外之意:首選方案是ConversionServiceBeanWrapper
其實了解的伙伴應(yīng)該知道,這不是BeanWrapper的內(nèi)容,而是父接口PropertyAccessor的內(nèi)容~
官方解釋:Spring低級JavaBeans基礎(chǔ)設(shè)施的中央接口。通常來說并不直接使用BeanWrapper,而是借助BeanFactory或者DataBinder來一起使用~
//@since 13 April 2001 很清晰的看到,它也是個`PropertyAccessor`屬性訪問器 public interface BeanWrapper extends ConfigurablePropertyAccessor { // @since 4.1 void setAutoGrowCollectionLimit(int autoGrowCollectionLimit); int getAutoGrowCollectionLimit(); Object getWrappedInstance(); Class> getWrappedClass(); // 獲取屬性們的PropertyDescriptor 獲取屬性們 PropertyDescriptor[] getPropertyDescriptors(); // 獲取具體某一個屬性~ PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException; }
BeanWrapper相當于一個代理器,Spring委托BeanWrapper完成Bean屬性的填充工作。關(guān)于此接口的實現(xiàn)類,簡單的說它只有唯一實現(xiàn)類:BeanWrapperImpl
BeanWrapperImpl它作為BeanWrapper接口的默認實現(xiàn),它足以滿足所有的典型應(yīng)用場景,它會緩存Bean的內(nèi)省結(jié)果而提高效率。
在Spring2.5之前,此實現(xiàn)類是非public的,但在2.5之后給public了并且還提供了工廠:PropertyAccessorFactory幫助第三方框架能快速獲取到一個實例~
public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper { // 緩存內(nèi)省結(jié)果~ @Nullable private CachedIntrospectionResults cachedIntrospectionResults; // The security context used for invoking the property methods. @Nullable private AccessControlContext acc; // 構(gòu)造方法都是沿用父類的~ public BeanWrapperImpl() { this(true); } ... private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent) { super(object, nestedPath, parent); setSecurityContext(parent.acc); } // @since 4.3 設(shè)置目標對象~~~ public void setBeanInstance(Object object) { this.wrappedObject = object; this.rootObject = object; this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); // 設(shè)置內(nèi)省的clazz setIntrospectionClass(object.getClass()); } // 復(fù)寫父類的方法 增加內(nèi)省邏輯 @Override public void setWrappedInstance(Object object, @Nullable String nestedPath, @Nullable Object rootObject) { super.setWrappedInstance(object, nestedPath, rootObject); setIntrospectionClass(getWrappedClass()); } // 如果cachedIntrospectionResults它持有的BeanClass并不是傳入的clazz 那就清空緩存 重新來~~~ protected void setIntrospectionClass(Class> clazz) { if (this.cachedIntrospectionResults != null && this.cachedIntrospectionResults.getBeanClass() != clazz) { this.cachedIntrospectionResults = null; } } private CachedIntrospectionResults getCachedIntrospectionResults() { if (this.cachedIntrospectionResults == null) { // forClass此方法:生成此clazz的類型結(jié)果,并且緩存了起來~~ this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass()); } return this.cachedIntrospectionResults; } ... // 獲取到此屬性的處理器。此處是個BeanPropertyHandler 內(nèi)部類~ @Override @Nullable protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName); return (pd != null ? new BeanPropertyHandler(pd) : null); } @Override protected BeanWrapperImpl newNestedPropertyAccessor(Object object, String nestedPath) { return new BeanWrapperImpl(object, nestedPath, this); } @Override public PropertyDescriptor[] getPropertyDescriptors() { return getCachedIntrospectionResults().getPropertyDescriptors(); } // 獲取具體某一個屬性的PropertyDescriptor @Override public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException { BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName); String finalPath = getFinalPath(nestedBw, propertyName); PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath); if (pd == null) { throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName, "No property "" + propertyName + "" found"); } return pd; } ... // 此處理器處理的是PropertyDescriptor private class BeanPropertyHandler extends PropertyHandler { private final PropertyDescriptor pd; // 是否可讀、可寫 都是由PropertyDescriptor 去決定了~ // java.beans.PropertyDescriptor~~ public BeanPropertyHandler(PropertyDescriptor pd) { super(pd.getPropertyType(), pd.getReadMethod() != null, pd.getWriteMethod() != null); this.pd = pd; } ... @Override @Nullable public Object getValue() throws Exception { ... ReflectionUtils.makeAccessible(readMethod); return readMethod.invoke(getWrappedInstance(), (Object[]) null); } ... } }
從繼承體系上,首先我們應(yīng)該能看出來BeanWrapperImpl的三重身份:
Bean包裹器
屬性訪問器(PropertyAccessor)
屬性編輯器注冊表(PropertyEditorRegistry)
從源碼中繼續(xù)分析還能再得出如下兩個結(jié)論:
它給屬性賦值調(diào)用的是Method方法,如readMethod.invoke和writeMethod.invoke
它對Bean的操作,大都委托給CachedIntrospectionResults去完成~
因此若想了解它,必然主要是要先了解java.beans.PropertyDescriptor和org.springframework.beans.CachedIntrospectionResults,首當其沖的自然還有Java內(nèi)省。
Java內(nèi)省Introspector首先可以先了解下JavaBean的概念:一種特殊的類,主要用于傳遞數(shù)據(jù)信息。這種類中的方法主要用于訪問私有的字段,且方法名符合某種命名規(guī)則。如果在兩個模塊之間傳遞信息,可以將信息封裝進JavaBean中,這種對象稱為“值對象”(Value Object),或“VO”。
因此JavaBean都有如下幾個特征:
屬性都是私有的;
有無參的public構(gòu)造方法;
對私有屬性根據(jù)需要提供公有的getXxx方法以及setXxx方法;
getters必須有返回值沒有方法參數(shù);setter值沒有返回值,有方法參數(shù);
符合這些特征的類,被稱為JavaBean;JDK中提供了一套API用來訪問某個屬性的getter/setter方法,這些API存放在java.beans中,這就是內(nèi)省(Introspector)。
反射:Java反射機制是在運行中,對任意一個類,能夠獲取得到這個類的所有屬性和方法;它針對的是任意類
內(nèi)省(Introspector):是Java語言對JavaBean類屬性、事件的處理方法
反射可以操作各種類的屬性,而內(nèi)省只是通過反射來操作JavaBean的屬性
內(nèi)省設(shè)置屬性值肯定會調(diào)用seter方法,反射可以不用(反射可直接操作屬性Field)
反射就像照鏡子,然后能看到.class的所有,是客觀的事實。內(nèi)省更像主觀的判斷:比如看到getName()內(nèi)省就會認為這個類中有name字段,但事實上并不一定會有name;通過內(nèi)省可以獲取bean的getter/setter
既然反射比內(nèi)省比內(nèi)省強大這么多,那內(nèi)省用在什么時候場景呢?下面給出一個示例來說明它的用武之地:
// 就這樣簡單幾步,就完成了表單到User對象的封裝~ public void insertUser(HttpServletRequest request) throws Exception { User user = new User(); // 遍歷:根據(jù)字段名去拿值即可(此處省略判空、類型轉(zhuǎn)換等細節(jié),不在本文討論范圍) PropertyDescriptor[] pds = Introspector.getBeanInfo(User.class).getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { pd.getWriteMethod().invoke(user, request.getParameter(pd.getName())); } }
通過內(nèi)省可以很輕松的將form表單的內(nèi)容填充進對象里面,比反射輕松省力多了。其實像MyBatis這種框架,底層都用到了Java的內(nèi)省機制。
內(nèi)省的API主要有Introspector、BeanInfo、PropertyDescriptor等,下面就以他三為例來操作一個JavaBean:
@Getter @Setter @ToString public class Child { private String name; private Integer age; }
public static void main(String[] args) throws IntrospectionException { BeanInfo beanInfo = Introspector.getBeanInfo(Child.class); BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor(); MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors(); PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); // 打印 System.out.println(beanDescriptor); System.out.println("------------------------------"); Arrays.stream(methodDescriptors).forEach(x -> System.out.println(x)); System.out.println("------------------------------"); Arrays.stream(propertyDescriptors).forEach(x -> System.out.println(x)); System.out.println("------------------------------"); }
輸入內(nèi)容如下:
java.beans.BeanDescriptor[name=Child; beanClass=class com.fsx.bean.Child] ------------------------------ java.beans.MethodDescriptor[name=getClass; method=public final native java.lang.Class java.lang.Object.getClass()] java.beans.MethodDescriptor[name=getName; method=public java.lang.String com.fsx.bean.Child.getName()] java.beans.MethodDescriptor[name=setAge; method=public void com.fsx.bean.Child.setAge(java.lang.Integer)] java.beans.MethodDescriptor[name=setName; method=public void com.fsx.bean.Child.setName(java.lang.String)] java.beans.MethodDescriptor[name=getAge; method=public java.lang.Integer com.fsx.bean.Child.getAge()] java.beans.MethodDescriptor[name=wait; method=public final void java.lang.Object.wait() throws java.lang.InterruptedException] java.beans.MethodDescriptor[name=notifyAll; method=public final native void java.lang.Object.notifyAll()] java.beans.MethodDescriptor[name=notify; method=public final native void java.lang.Object.notify()] java.beans.MethodDescriptor[name=wait; method=public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException] java.beans.MethodDescriptor[name=hashCode; method=public native int java.lang.Object.hashCode()] java.beans.MethodDescriptor[name=wait; method=public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException] java.beans.MethodDescriptor[name=equals; method=public boolean java.lang.Object.equals(java.lang.Object)] java.beans.MethodDescriptor[name=toString; method=public java.lang.String com.fsx.bean.Child.toString()] ------------------------------ java.beans.PropertyDescriptor[name=age; propertyType=class java.lang.Integer; readMethod=public java.lang.Integer com.fsx.bean.Child.getAge(); writeMethod=public void com.fsx.bean.Child.setAge(java.lang.Integer)] java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] java.beans.PropertyDescriptor[name=name; propertyType=class java.lang.String; readMethod=public java.lang.String com.fsx.bean.Child.getName(); writeMethod=public void com.fsx.bean.Child.setName(java.lang.String)] ------------------------------
可以看到getMethodDescriptors()它把父類的MethodDescriptor也拿出來了。
而PropertyDescriptor中比較特殊的是因為有getClass()方法,因此class也算是一個PropertyDescriptor,但是它沒有writeMethod哦~
關(guān)于BeanInfo,Spring在3.1提供了一個類ExtendedBeanInfo繼承自它實現(xiàn)了功能擴展,并且提供了BeanInfoFactory來專門生產(chǎn)它~~~(實現(xiàn)類為:ExtendedBeanInfoFactory)
但是如果只想拿某一個屬性的話,使用Introspector就不是那么方便了,下面介紹更為常用的PropertyDescriptor來處理某一個屬性~
PropertyDescriptor 屬性描述器屬性描述符描述了Java bean通過一對訪問器方法導出的一個屬性。上面的示例此處用PropertyDescriptor試試:
public static void main(String[] args) throws IntrospectionException { PropertyDescriptor age = new PropertyDescriptor("age", Child.class); System.out.println(age.getPropertyType()); //class java.lang.Integer System.out.println(age.getDisplayName()); //age // 最重要的兩個方法~~~ System.out.println(age.getReadMethod()); //public java.lang.Integer com.fsx.bean.Child.getAge() System.out.println(age.getWriteMethod()); //public void com.fsx.bean.Child.setAge(java.lang.Integer) }
可以看到它可以實現(xiàn)更加細粒度的控制。將PropertyDescriptor類的一些主要方法描述如下:
getPropertyType(),獲得屬性的Class對象;
getReadMethod(),獲得用于讀取屬性值的方法;
getWriteMethod(),獲得用于寫入屬性值的方法;
setReadMethod(Method readMethod),設(shè)置用于讀取屬性值的方法;
setWriteMethod(Method writeMethod),設(shè)置用于寫入屬性值的方法。
CachedIntrospectionResultsSpring如果需要依賴注入那么就必須依靠Java內(nèi)省這個特性了,說到Spring IOC與JDK內(nèi)省的結(jié)合那么就不得不說一下Spring中的CachedIntrospectionResults這個類了。
它是Spring提供的專門用于緩存JavaBean的PropertyDescriptor描述信息的類,不能被應(yīng)用代碼直接使用。
它的緩存信息是被靜態(tài)存儲起來的(應(yīng)用級別),因此對于同一個類型的被操作的JavaBean并不會都創(chuàng)建一個新的CachedIntrospectionResults,因此,這個類使用了工廠模式,使用私有構(gòu)造器和一個靜態(tài)的forClass工廠方法來獲取實例。
public final class CachedIntrospectionResults { // 它可以通過在spring.properties里設(shè)置這個屬性,來關(guān)閉內(nèi)省的緩存~~~ public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore"; private static final boolean shouldIntrospectorIgnoreBeaninfoClasses = SpringProperties.getFlag(IGNORE_BEANINFO_PROPERTY_NAME); // 此處使用了SpringFactoriesLoader這個SPI來加載BeanInfoFactory,唯一實現(xiàn)類是ExtendedBeanInfoFactory /** Stores the BeanInfoFactory instances. */ private static ListbeanInfoFactories = SpringFactoriesLoader.loadFactories( BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader()); static final Set acceptedClassLoaders = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); static final ConcurrentMap , CachedIntrospectionResults> strongClassCache = new ConcurrentHashMap<>(64); static final ConcurrentMap , CachedIntrospectionResults> softClassCache = new ConcurrentReferenceHashMap<>(64); // 被包裹類的BeanInfo~~~也就是目標類 private final BeanInfo beanInfo; // 它緩存了被包裹類的所有屬性的屬性描述器PropertyDescriptor。 private final Map propertyDescriptorCache; ... // 其它的都是靜態(tài)方法 // 只有它會返回一個實例,此類是單例的設(shè)計~ 它保證了每個beanClass都有一個CachedIntrospectionResults 對象,然后被緩存起來~ static CachedIntrospectionResults forClass(Class> beanClass) throws BeansException { ... } }
本處理類的核心內(nèi)容是Java內(nèi)省getBeanInfo()以及PropertyDescriptor~注意:為了使此內(nèi)省緩存生效,有個前提條件請保證了:
確保將Spring框架的Jar包和你的應(yīng)用類使用的是同一個ClassLoader加載的,這樣在任何情況下會允許隨著應(yīng)用的生命周期來清楚緩存。
因此對于web應(yīng)用來說,Spring建議給web容器注冊一個IntrospectorCleanupListener監(jiān)聽器來防止多ClassLoader布局,這樣也可以有效的利用caching從而提高效率~
監(jiān)聽器的配置形如這樣(此處以web.xml里配置為例):
org.springframework.web.util.IntrospectorCleanupListener
說明:請保證此監(jiān)聽器配置在第一個位置,比ContextLoaderListener還靠前~ 此監(jiān)聽器能有效的防止內(nèi)存泄漏問題~~~(因為內(nèi)省的緩存是應(yīng)用級別的全局緩存,很容易造成泄漏的~)
其實流行框架比如struts, Quartz等在使用JDK的內(nèi)省時,存在沒有釋的內(nèi)存泄漏問題~
說完了BeanWrapperImpl,可以看看它的子類DirectFieldAccessFallbackBeanWrapper,他就像BeanWrapperImpl和DirectFieldAccessor的結(jié)合體。它先用BeanWrapperImpl.getPropertyValue(),若拋出異常了(畢竟內(nèi)省不是十分靠譜,哈哈)再用DirectFieldAccessor~~~此子類在JedisClusterConnection有被使用到過,比較簡單沒啥太多好說的~
PropertyAccessorFactorySpring2.5后提供的快速獲取PropertyAccessor兩個重要實現(xiàn)類的工廠。
public final class PropertyAccessorFactory { private PropertyAccessorFactory() { } // 生產(chǎn)一個BeanWrapperImpl(最為常用) public static BeanWrapper forBeanPropertyAccess(Object target) { return new BeanWrapperImpl(target); } // 生產(chǎn)一個DirectFieldAccessor public static ConfigurablePropertyAccessor forDirectFieldAccess(Object target) { return new DirectFieldAccessor(target); } }BeanWrapper使用Demo
說了這么多,是時候?qū)崙?zhàn)一把了~
// 省略Apple類和Size類,有需要的請參照上篇文章(加上@Getter、@Setter即可 public static void main(String[] args) { Apple apple = new Apple(); BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(apple); // ================當作一個普通的PropertyAccessor來使用 默認情況下字段也都必須有初始值才行~=================== // 設(shè)置普通屬性 beanWrapper.setPropertyValue("color", "紅色"); //請保證對應(yīng)字段有set方法才行,否則拋錯:Does the parameter type of the setter match the return type of the getter? // 設(shè)置嵌套屬性(注意:此處能夠正常work是因為有= new Size(), // 否則報錯:Value of nested property "size" is null 下同~) beanWrapper.setPropertyValue("size.height", 10); // 設(shè)置集合/數(shù)組屬性 beanWrapper.setPropertyValue("arrStr[0]", "arrStr"); beanWrapper.setPropertyValue("arrStr[1]", "arrStr1"); // 注意:雖然初始化時初始化過數(shù)組了,但是仍以此處的為準 // =========打印輸出 System.out.println(apple); //Apple(color=紅色, size=Size(height=10, width=null), arrStr=[arrStr, arrStr1], listStr=[], map={}, listList=[[]], listMap=[{}]) // 當作BeanWrapper使用 PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors(); PropertyDescriptor color = beanWrapper.getPropertyDescriptor("color"); System.out.println(propertyDescriptors.length); // 8 System.out.println(color); //org.springframework.beans.GenericTypeAwarePropertyDescriptor[name=color] System.out.println(beanWrapper.getWrappedClass()); //class com.fsx.bean.Apple System.out.println(beanWrapper.getWrappedInstance()); //Apple(color=紅色, size=Size(height=10... }
上面代碼能夠清晰的表示了通過BeanWrapper來操作JavaBean還是非常之簡便的。
最后,上一張比較丑的結(jié)構(gòu)圖,畫一畫屬性編輯器、類型轉(zhuǎn)換器、屬性解析器、屬性訪問器大致的一個關(guān)系(此圖不喜勿碰):
BeanWrapper接口,作為Spring內(nèi)部的一個核心接口,正如其名,它是bean的包裹類,即在內(nèi)部中將會保存該bean的實例,提供其它一些擴展功能。
Spring對Bean的屬性存取都是通過BeanWrapperImpl實現(xiàn)的,BeanWrapperImpl和Bean是一對一的關(guān)系,BeanWrapperImpl通過屬性的讀方法和寫方法來存取Bean屬性的。為了更加深刻的了解BeanWrapper,下篇文章會深入分析Spring BeanFactory對它的應(yīng)用~
知識交流==The last:如果覺得本文對你有幫助,不妨點個贊唄。當然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的~==
**若對技術(shù)內(nèi)容感興趣可以加入wx群交流:Java高工、架構(gòu)師3群。
若群二維碼失效,請加wx號:fsx641385712(或者掃描下方wx二維碼)。并且備注:"java入群" 字樣,會手動邀請入群**
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/75480.html
摘要:從層層委托的依賴關(guān)系可以看出,的依賴注入給屬性賦值是層層委托的最終給了內(nèi)省機制,這是框架設(shè)計精妙處之一。當然分享到你的朋友圈讓更多小伙伴看到也是被作者本人許可的若對技術(shù)內(nèi)容感興趣可以加入群交流高工架構(gòu)師群。 每篇一句 具備了技術(shù)深度,遇到問題可以快速定位并從根本上解決。有了技術(shù)深度之后,學習其它技術(shù)可以更快,再深入其它技術(shù)也就不會害怕 相關(guān)閱讀 【小家Spring】聊聊Spring中的...
摘要:對中的數(shù)據(jù)綁定場景,小伙伴們就再熟悉不過了。比如包下大名鼎鼎的源碼分析的源碼相對來說還是頗為復(fù)雜的,它提供的能力非常強大,也注定了它的方法非常多屬性也非常多。并且備注入群字樣,會手動邀請入群 每篇一句 唯有熱愛和堅持,才能讓你在程序人生中屹立不倒,切忌跟風什么語言或就學什么去~ 相關(guān)閱讀 【小家Spring】聊聊Spring中的數(shù)據(jù)綁定 --- 屬性訪問器PropertyAccesso...
private static String[] getNullPropertyNames(Object source) { final BeanWrapper src = new BeanWrapperImpl(source); java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); ...
摘要:最近在寫一個小玩意的時候,需要在兩個對象之間拷貝屬性使用的是可是,有一個問題就是當對象的鍵值為時就會把對象的對應(yīng)鍵值覆蓋成空了這不科學所以找了下面的這個方式來解決 最近在寫一個小玩意的時候,需要在兩個對象之間拷貝屬性 使用的是 BeanUtils.copyProperties 可是,有一個問題 就是當src對象的鍵值為Null時 就會把target對象的對應(yīng)鍵值覆蓋成空了 ...
1. BeanUtils.copyProperties(Object source, Object target) 用法: 講source的屬性值復(fù)制到target,屬性為null時也會進行復(fù)制。 需求:排除null值進行復(fù)制 public class CopyObjectUtil { public static String[] getNullPropertyNames(Object...
閱讀 922·2021-10-13 09:48
閱讀 3907·2021-09-22 10:53
閱讀 3114·2021-08-30 09:41
閱讀 1943·2019-08-30 15:55
閱讀 2920·2019-08-30 15:55
閱讀 1839·2019-08-30 14:11
閱讀 2205·2019-08-29 13:44
閱讀 764·2019-08-26 12:23