日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人文社科 > 生活经验 >内容正文

生活经验

java signature 性能_Java常见bean mapper的性能及原理分析

發(fā)布時(shí)間:2023/11/27 生活经验 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java signature 性能_Java常见bean mapper的性能及原理分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

背景

在分層的代碼架構(gòu)中,層與層之間的對(duì)象避免不了要做很多轉(zhuǎn)換、賦值等操作,這些操作重復(fù)且繁瑣,于是乎催生出很多工具來(lái)優(yōu)雅,高效地完成這個(gè)操作,有BeanUtils、BeanCopier、Dozer、Orika等等,本文將講述上面幾個(gè)工具的使用、性能對(duì)比及原理分析。

性能分析

其實(shí)這幾個(gè)工具要做的事情很簡(jiǎn)單,而且在使用上也是類似的,所以我覺(jué)得先給大家看看性能分析的對(duì)比結(jié)果,讓大家有一個(gè)大概的認(rèn)識(shí)。我是使用JMH來(lái)做性能分析的,代碼如下:

要復(fù)制的對(duì)象比較簡(jiǎn)單,包含了一些基本類型;有一次warmup,因?yàn)橐恍┕ぞ呤切枰邦A(yù)編譯”和做緩存的,這樣做對(duì)比才會(huì)比較客觀;分別復(fù)制1000、10000、100000個(gè)對(duì)象,這是比較常用數(shù)量級(jí)了吧。

@BenchmarkMode(Mode.AverageTime)??@OutputTimeUnit(TimeUnit.MICROSECONDS)??@Fork(1)??@Warmup(iterations?=1)??@State(Scope.Benchmark)publicclassBeanMapperBenchmark{@Param({"1000","10000","100000"})privateinttimes;privateinttime;privatestaticMapperFactory?mapperFactory;privatestaticMapper?mapper;static{??????????mapperFactory?=newDefaultMapperFactory.Builder().build();??????????mapperFactory.classMap(SourceVO.class,?TargetVO.class)??????????????????.byDefault()??????????????????.register();????????????mapper?=?DozerBeanMapperBuilder.create()??????????????????.withMappingBuilder(newBeanMappingBuilder()?{??????????????????????@Overrideprotectedvoidconfigure()?{??????????????????????????mapping(SourceVO.class,?TargetVO.class)??????????????????????????????????.fields("fullName","name")??????????????????????????????????.exclude("in");??????????????????????}??????????????????}).build();??????}publicstaticvoidmain(String[]?args)throws?Exception{??????????Options?options?=newOptionsBuilder()??????????????????.include(BeanMapperBenchmark.class.getName()).measurementIterations(3)??????????????????.build();newRunner(options).run();??????}????????@Setuppublicvoidprepare(){this.time?=?times;??????}????????@BenchmarkpublicvoidspringBeanUtilTest(){??????????SourceVO?sourceVO?=?getSourceVO();for(inti?=0;?i?

在我macbook下運(yùn)行后的結(jié)果如下:

圖片

Score表示的是平均運(yùn)行時(shí)間,單位是微秒。從執(zhí)行效率來(lái)看,可以看出 beanCopier > orika > springBeanUtil > dozer > apacheBeanUtil。這樣的結(jié)果跟它們各自的實(shí)現(xiàn)原理有很大的關(guān)系,

下面將詳細(xì)每個(gè)工具的使用及實(shí)現(xiàn)原理。

Spring的BeanUtils

使用

這個(gè)工具可能是大家日常使用最多的,因?yàn)槭荢pring自帶的,使用也簡(jiǎn)單:BeanUtils.copyProperties(sourceVO, targetVO);

原理

Spring BeanUtils的實(shí)現(xiàn)原理也比較簡(jiǎn)答,就是通過(guò)Java的Introspector獲取到兩個(gè)類的PropertyDescriptor,對(duì)比兩個(gè)屬性具有相同的名字和類型,如果是,則進(jìn)行賦值(通過(guò)ReadMethod獲取值,通過(guò)WriteMethod賦值),否則忽略。

為了提高性能Spring對(duì)BeanInfo和PropertyDescriptor進(jìn)行了緩存。

(源碼基于:org.springframework:spring-beans:4.3.9.RELEASE)

/**????*?Copy?the?property?values?of?the?given?source?bean?into?the?given?target?bean.????*

Note:?The?source?and?target?classes?do?not?have?to?match?or?even?be?derived????*?from?each?other,?as?long?as?the?properties?match.?Any?bean?properties?that?the????*?source?bean?exposes?but?the?target?bean?does?not?will?silently?be?ignored.????*@param?source?the?source?bean????*@param?target?the?target?bean????*@param?editable?the?class?(or?interface)?to?restrict?property?setting?to????*@param?ignoreProperties?array?of?property?names?to?ignore????*@throws?BeansException?if?the?copying?failed????*@see?BeanWrapper????*/privatestaticvoidcopyProperties(Objectsource,Objecttarget,?Class?editable,String...?ignoreProperties)?????throws?BeansException?{??????Assert.notNull(source,"Source?must?not?be?null");????Assert.notNull(target,"Target?must?not?be?null");??????Class?actualEditable?=?target.getClass();if(editable?!=null)?{if(!editable.isInstance(target))?{thrownewIllegalArgumentException("Target?class?["+?target.getClass().getName()?+"]?not?assignable?to?Editable?class?["+?editable.getName()?+"]");?????}?????actualEditable?=?editable;????}//獲取target類的屬性(有緩存)??PropertyDescriptor[]?targetPds?=?getPropertyDescriptors(actualEditable);????List?ignoreList?=?(ignoreProperties?!=null??Arrays.asList(ignoreProperties)?:null);for(PropertyDescriptor?targetPd?:?targetPds)?{?????Method?writeMethod?=?targetPd.getWriteMethod();if(writeMethod?!=null&&?(ignoreList?==null||?!ignoreList.contains(targetPd.getName())))?{//獲取source類的屬性(有緩存)??PropertyDescriptor?sourcePd?=?getPropertyDescriptor(source.getClass(),?targetPd.getName());if(sourcePd?!=null)?{???????Method?readMethod?=?sourcePd.getReadMethod();if(readMethod?!=null&&//判斷target的setter方法入?yún)⒑蛃ource的getter方法返回類型是否一致??ClassUtils.isAssignable(writeMethod.getParameterTypes()[0],?readMethod.getReturnType()))?{try{if(!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers()))?{??????????readMethod.setAccessible(true);?????????}//獲取源值??Objectvalue?=?readMethod.invoke(source);if(!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()))?{??????????writeMethod.setAccessible(true);?????????}//賦值到target??writeMethod.invoke(target,?value);????????}catch(Throwable?ex)?{thrownewFatalBeanException("Could?not?copy?property?'"+?targetPd.getName()?+"'?from?source?to?target",?ex);????????}???????}??????}?????}????}???}

小結(jié)

Spring BeanUtils的實(shí)現(xiàn)就是這么簡(jiǎn)潔,這也是它性能比較高的原因。

不過(guò),過(guò)于簡(jiǎn)潔就失去了靈活性和可擴(kuò)展性了,Spring BeanUtils的使用限制也比較明顯,要求類屬性的名字和類型一致,這點(diǎn)在使用時(shí)要注意。

Apache的BeanUtils

使用

Apache的BeanUtils和Spring的BeanUtils的使用是一樣的:

BeanUtils.copyProperties(targetVO,sourceVO);

要注意,source和target的入?yún)⑽恢貌煌?/p>

原理

Apache的BeanUtils的實(shí)現(xiàn)原理跟Spring的BeanUtils一樣,也是主要通過(guò)Java的Introspector機(jī)制獲取到類的屬性來(lái)進(jìn)行賦值操作,對(duì)BeanInfo和PropertyDescriptor同樣有緩存,但是Apache BeanUtils加了一些不那么使用的特性(包括支持Map類型、支持自定義的DynaBean類型、支持屬性名的表達(dá)式等等)在里面,使得性能相對(duì)Spring的BeanUtils來(lái)說(shuō)有所下降。

(源碼基于:commons-beanutils:commons-beanutils:1.9.3)

publicvoidcopyProperties(finalObjectdest,?finalObjectorig)??????????throws?IllegalAccessException,?InvocationTargetException?{if(dest?==null)?{thrownewIllegalArgumentException??????????????????????("No?destination?bean?specified");??????????}if(orig?==null)?{thrownewIllegalArgumentException("No?origin?bean?specified");??????????}if(log.isDebugEnabled())?{??????????????log.debug("BeanUtils.copyProperties("+?dest?+",?"+????????????????????????orig?+")");??????????}//?Apache?Common自定義的DynaBean??if(originstanceofDynaBean)?{??????????????final?DynaProperty[]?origDescriptors?=??????????????????((DynaBean)?orig).getDynaClass().getDynaProperties();for(DynaProperty?origDescriptor?:?origDescriptors)?{??????????????????finalStringname?=?origDescriptor.getName();//?Need?to?check?isReadable()?for?WrapDynaBean??//?(see?Jira?issue#?BEANUTILS-61)??if(getPropertyUtils().isReadable(orig,?name)?&&??????????????????????getPropertyUtils().isWriteable(dest,?name))?{??????????????????????finalObjectvalue?=?((DynaBean)?orig).get(name);??????????????????????copyProperty(dest,?name,?value);??????????????????}??????????????}//?Map類型??}elseif(originstanceofMap)?{??????????????@SuppressWarnings("unchecked")??????????????final//?Map?properties?are?always?of?type???Map?propMap?=?(Map)?orig;for(finalMap.Entry?entry?:?propMap.entrySet())?{??????????????????finalStringname?=?entry.getKey();if(getPropertyUtils().isWriteable(dest,?name))?{??????????????????????copyProperty(dest,?name,?entry.getValue());??????????????????}??????????????}//?標(biāo)準(zhǔn)的JavaBean??}else{??????????????final?PropertyDescriptor[]?origDescriptors?=//獲取PropertyDescriptor??getPropertyUtils().getPropertyDescriptors(orig);for(PropertyDescriptor?origDescriptor?:?origDescriptors)?{??????????????????finalStringname?=?origDescriptor.getName();if("class".equals(name))?{continue;//?No?point?in?trying?to?set?an?object's?class??}//是否可讀和可寫??if(getPropertyUtils().isReadable(orig,?name)?&&??????????????????????getPropertyUtils().isWriteable(dest,?name))?{try{//獲取源值??finalObjectvalue?=??????????????????????????????getPropertyUtils().getSimpleProperty(orig,?name);//賦值操作??copyProperty(dest,?name,?value);??????????????????????}catch(final?NoSuchMethodException?e)?{//?Should?not?happen??}??????????????????}??????????????}??????????}????????}

小結(jié)

Apache BeanUtils的實(shí)現(xiàn)跟Spring BeanUtils總體上類似,但是性能卻低很多,這個(gè)可以從上面性能比較看出來(lái)。阿里的Java規(guī)范是不建議使用的。

BeanCopier

使用

BeanCopier在cglib包里,它的使用也比較簡(jiǎn)單:

@TestpublicvoidbeanCopierSimpleTest(){??????????SourceVO?sourceVO?=?getSourceVO();??????????log.info("source={}",?GsonUtil.toJson(sourceVO));??????????TargetVO?targetVO?=newTargetVO();??????????BeanCopier?bc?=?BeanCopier.create(SourceVO.class,TargetVO.class,false);??????????bc.copy(sourceVO,?targetVO,null);??????????log.info("target={}",?GsonUtil.toJson(targetVO));??????}

只需要預(yù)先定義好要轉(zhuǎn)換的source類和target類就好了,可以選擇是否使用Converter,這個(gè)下面會(huì)說(shuō)到。

在上面的性能測(cè)試中,BeanCopier是所有中表現(xiàn)最好的,那么我們分析一下它的實(shí)現(xiàn)原理。

原理

BeanCopier的實(shí)現(xiàn)原理跟BeanUtils截然不同,它不是利用反射對(duì)屬性進(jìn)行賦值,而是直接使用cglib來(lái)生成帶有的get/set方法的class類,然后執(zhí)行。由于是直接生成字節(jié)碼執(zhí)行,所以BeanCopier的性能接近手寫

get/set。

BeanCopier.create方法

publicstaticBeanCopiercreate(Class?source,?Class?target,booleanuseConverter){??????????Generator?gen?=newGenerator();??????????gen.setSource(source);??????????gen.setTarget(target);??????????gen.setUseConverter(useConverter);returngen.create();??????}publicBeanCopiercreate(){??????????????Object?key?=?KEY_FACTORY.newInstance(source.getName(),?target.getName(),?useConverter);return(BeanCopier)super.create(key);??????????}

這里的意思是用KEY_FACTORY創(chuàng)建一個(gè)BeanCopier出來(lái),然后調(diào)用create方法來(lái)生成字節(jié)碼。

KEY_FACTORY其實(shí)就是用cglib通過(guò)BeanCopierKey接口生成出來(lái)的一個(gè)類

privatestaticfinalBeanCopierKey?KEY_FACTORY?=????????(BeanCopierKey)KeyFactory.create(BeanCopierKey.class);interfaceBeanCopierKey{publicObjectnewInstance(String?source,?String?target,booleanuseConverter);??????}

通過(guò)設(shè)置

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,?"path");

可以讓cglib輸出生成類的class文件,我們可以反復(fù)譯看看里面的代碼

下面是KEY_FACTORY的類

publicclassBeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fdextendsKeyFactoryimplementsBeanCopierKey{privatefinalString?FIELD_0;privatefinalString?FIELD_1;privatefinalbooleanFIELD_2;publicBeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd()?{??????}publicObjectnewInstance(String?var1,?String?var2,booleanvar3){returnnewBeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd(var1,?var2,?var3);??????}publicBeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd(String?var1,?String?var2,booleanvar3)?{this.FIELD_0?=?var1;this.FIELD_1?=?var2;this.FIELD_2?=?var3;??????}//省去hashCode等方法。。。??}

繼續(xù)跟蹤Generator.create方法,由于Generator是繼承AbstractClassGenerator,這個(gè)AbstractClassGenerator是cglib用來(lái)生成字節(jié)碼的一個(gè)模板類,Generator的super.create其實(shí)調(diào)用

AbstractClassGenerator的create方法,最終會(huì)調(diào)用到Generator的模板方法generateClass方法,我們不去細(xì)究AbstractClassGenerator的細(xì)節(jié),重點(diǎn)看generateClass。

這個(gè)是一個(gè)生成java類的方法,理解起來(lái)就好像我們平時(shí)寫代碼一樣。

publicvoidgenerateClass(ClassVisitor?v)?{??????????????Type?sourceType?=?Type.getType(source);??????????????Type?targetType?=?Type.getType(target);??????????????ClassEmitter?ce?=?new?ClassEmitter(v);//開(kāi)始“寫”類,這里有修飾符、類名、父類等信息??ce.begin_class(Constants.V1_2,?????????????????????????????Constants.ACC_PUBLIC,?????????????????????????????getClassName(),?????????????????????????????BEAN_COPIER,?????????????????????????????null,?????????????????????????????Constants.SOURCE_FILE);//沒(méi)有構(gòu)造方法??EmitUtils.null_constructor(ce);//開(kāi)始“寫”一個(gè)方法,方法名是copy??CodeEmitter?e?=?ce.begin_method(Constants.ACC_PUBLIC,?COPY,?null);//通過(guò)Introspector獲取source類和target類的PropertyDescriptor??PropertyDescriptor[]?getters?=?ReflectUtils.getBeanGetters(source);??????????????PropertyDescriptor[]?setters?=?ReflectUtils.getBeanSetters(target);????????????????????????????Map?names?=?new?HashMap();for(inti?=0;?i?

即使沒(méi)有使用過(guò)cglib也能讀懂生成代碼的流程吧,我們看看沒(méi)有使用useConverter的情況下生成的代碼:

publicclassObject$$BeanCopierByCGLIB$$d1d970c8extendsBeanCopier?{publicObject$$BeanCopierByCGLIB$$d1d970c8()?{??????}publicvoidcopy(Objectvar1,Objectvar2,?Converter?var3)?{??????????TargetVO?var10000?=?(TargetVO)var2;??????????SourceVO?var10001?=?(SourceVO)var1;??????????var10000.setDate1(((SourceVO)var1).getDate1());??????????var10000.setIn(var10001.getIn());??????????var10000.setListData(var10001.getListData());??????????var10000.setMapData(var10001.getMapData());??????????var10000.setP1(var10001.getP1());??????????var10000.setP2(var10001.getP2());??????????var10000.setP3(var10001.getP3());??????????var10000.setPattr1(var10001.getPattr1());??????}??}

在對(duì)比上面生成代碼的代碼是不是豁然開(kāi)朗了。

再看看使用useConverter的情況:

public?class?Object$$BeanCopierByCGLIB$$d1d970c7?extends?BeanCopier?{??????private?static?final?Class?CGLIB$load_class$java$2Eutil$2EDate;??????private?static?final?Class?CGLIB$load_class$beanmapper_compare$2Evo$2ESourceVO$24Inner;??????private?static?final?Class?CGLIB$load_class$java$2Eutil$2EList;??????private?static?final?Class?CGLIB$load_class$java$2Eutil$2EMap;??????private?static?final?Class?CGLIB$load_class$java$2Elang$2EInteger;??????private?static?final?Class?CGLIB$load_class$java$2Elang$2ELong;??????private?static?final?Class?CGLIB$load_class$java$2Elang$2EByte;??????private?static?final?Class?CGLIB$load_class$java$2Elang$2EString;????????public?Object$$BeanCopierByCGLIB$$d1d970c7()?{??????}????????public?void?copy(Object?var1,?Object?var2,?Converter?var3)?{??????????TargetVO?var4?=?(TargetVO)var2;??????????SourceVO?var5?=?(SourceVO)var1;??????????var4.setDate1((Date)var3.convert(var5.getDate1(),?CGLIB$load_class$java$2Eutil$2EDate,"setDate1"));??????????var4.setIn((Inner)var3.convert(var5.getIn(),?CGLIB$load_class$beanmapper_compare$2Evo$2ESourceVO$24Inner,"setIn"));??????????var4.setListData((List)var3.convert(var5.getListData(),?CGLIB$load_class$java$2Eutil$2EList,"setListData"));??????????var4.setMapData((Map)var3.convert(var5.getMapData(),?CGLIB$load_class$java$2Eutil$2EMap,"setMapData"));??????????var4.setP1((Integer)var3.convert(var5.getP1(),?CGLIB$load_class$java$2Elang$2EInteger,"setP1"));??????????var4.setP2((Long)var3.convert(var5.getP2(),?CGLIB$load_class$java$2Elang$2ELong,"setP2"));??????????var4.setP3((Byte)var3.convert(var5.getP3(),?CGLIB$load_class$java$2Elang$2EByte,"setP3"));??????????var4.setPattr1((String)var3.convert(var5.getPattr1(),?CGLIB$load_class$java$2Elang$2EString,"setPattr1"));??????????var4.setSeq((Long)var3.convert(var5.getSeq(),?CGLIB$load_class$java$2Elang$2ELong,"setSeq"));??????}????????static?void?CGLIB$STATICHOOK1()?{??????????CGLIB$load_class$java$2Eutil$2EDate?=?Class.forName("java.util.Date");??????????CGLIB$load_class$beanmapper_compare$2Evo$2ESourceVO$24Inner?=?Class.forName("beanmapper_compare.vo.SourceVO$Inner");??????????CGLIB$load_class$java$2Eutil$2EList?=?Class.forName("java.util.List");??????????CGLIB$load_class$java$2Eutil$2EMap?=?Class.forName("java.util.Map");??????????CGLIB$load_class$java$2Elang$2EInteger?=?Class.forName("java.lang.Integer");??????????CGLIB$load_class$java$2Elang$2ELong?=?Class.forName("java.lang.Long");??????????CGLIB$load_class$java$2Elang$2EByte?=?Class.forName("java.lang.Byte");??????????CGLIB$load_class$java$2Elang$2EString?=?Class.forName("java.lang.String");??????}????????static?{??????????CGLIB$STATICHOOK1();??????}??}

小結(jié)

BeanCopier性能確實(shí)很高,但從源碼可以看出BeanCopier只會(huì)拷貝名稱和類型都相同的屬性,而且如果一旦使用Converter,BeanCopier只使用Converter定義的規(guī)則去拷貝屬性,所以在convert方法中要考慮所有的屬性。

Dozer

使用

上面提到的BeanUtils和BeanCopier都是功能比較簡(jiǎn)單的,需要屬性名稱一樣,甚至類型也要一樣。但是在大多數(shù)情況下這個(gè)要求就相對(duì)苛刻了,要知道有些VO由于各種原因不能修改,有些是外部接口SDK的對(duì)象,

有些對(duì)象的命名規(guī)則不同,例如有駝峰型的,有下劃線的等等,各種什么情況都有。所以我們更加需要的是更加靈活豐富的功能,甚至可以做到定制化的轉(zhuǎn)換。

Dozer就提供了這些功能,有支持同名隱式映射,支持基本類型互相轉(zhuǎn)換,支持顯示指定映射關(guān)系,支持exclude字段,支持遞歸匹配映射,支持深度匹配,支持Date to String的date-formate,支持自定義轉(zhuǎn)換Converter,支持一次mapping定義多處使用,支持EventListener事件監(jiān)聽(tīng)等等。不僅如此,Dozer在使用方式上,除了支持API,還支持XML和注解,滿足大家的喜好。更多的功能可以參考這里

由于其功能很豐富,不可能每個(gè)都演示,這里只是給個(gè)大概認(rèn)識(shí),更詳細(xì)的功能,或者XML和注解的配置,請(qǐng)看官方文檔。

privateMapper?dozerMapper;????????@Beforepublicvoidsetup(){??????????dozerMapper?=?DozerBeanMapperBuilder.create()??????????????????.withMappingBuilder(newBeanMappingBuilder()?{??????????????????????@Overrideprotectedvoidconfigure()?{??????????????????????????mapping(SourceVO.class,?TargetVO.class)??????????????????????????????????.fields("fullName","name")??????????????????????????????????.exclude("in");??????????????????????}??????????????????})??????????????????.withCustomConverter(null)??????????????????.withEventListener(null)??????????????????.build();??????}????????????@TestpublicvoiddozerTest(){??????????SourceVO?sourceVO?=?getSourceVO();log.info("sourceVO={}",?GsonUtil.toJson(sourceVO));??????????TargetVOmap=?dozerMapper.map(sourceVO,?TargetVO.class);log.info("map={}",?GsonUtil.toJson(map));??????}

總結(jié)

以上是生活随笔為你收集整理的java signature 性能_Java常见bean mapper的性能及原理分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。