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