Introduction to Project Lombok | Baeldung

文章推薦指數: 80 %
投票人數:10人

A comprehensive and very practical introduction to many useful usecases of Project Lombok on standard Java code. StartHereCourses ▼▲ RESTwithSpring(30%off) ThecanonicalreferenceforbuildingaproductiongradeAPIwithSpring LearnSpringSecurity(30%off) ▼▲ THEuniqueSpringSecurityeducationifyou’reworkingwithJavatoday LearnSpringSecurityCore(30%off) FocusontheCoreofSpringSecurity5 LearnSpringSecurityOAuth(30%off) FocusonthenewOAuth2stackinSpringSecurity5 LearnSpring(30%off) Fromnoexperiencetoactuallybuildingstuff​ LearnSpringDataJPA(30%off) ThefullguidetopersistencewithSpringDataJPA Guides ▼▲ Persistence ThePersistencewithSpringguides REST TheguidesonbuildingRESTAPIswithSpring Security TheSpringSecurityguides About ▼▲ FullArchive Thehighleveloverviewofallthearticlesonthesite. BaeldungEbooks DiscoverallofoureBooks AboutBaeldung AboutBaeldung. MarchDiscountLaunch2022 We’refinallyrunningaspringlaunch.AllCoursesare30%offuntilnextFriday: >>GETACCESSNOW 1.AvoidRepetitiveCode Javaisagreatlanguage,butitcansometimesgettooverboseforcommontaskswehavetodoinourcodeorcompliancewithsomeframeworkpractices.Thisoftendoesn'tbringanyrealvaluetothebusinesssideofourprograms,andthat'swhereLombokcomesintomakeusmoreproductive. Thewayitworksisbypluggingintoourbuildprocessandauto-generatingJavabytecodeintoour.classfilesasperanumberofprojectannotationsweintroduceinourcode. Furtherreading:LombokBuilderwithDefaultValueLearnhowtocreateabuilderdefaultpropertyvaluesusingLombokReadmore→SettingupLombokwithEclipseandIntellijLearnhowtosetupLombokwithpopularIDEsReadmore→ Includingitinourbuilds,inwhicheversystemwe'reusing,isverystraightforward.ProjectLombok'sprojectpagehasdetailedinstructionsonthespecifics.Mostofmyprojectsaremavenbased,soIjusttypicallydroptheirdependencyintheprovidedscopeandI'mgoodtogo: ... org.projectlombok lombok 1.18.20 provided ... Wecancheckforthemostrecentavailableversionhere. NotethatdependingonLombokwon'tmakeusersofour.jarsdependonitaswell,asitisapurebuilddependency,notruntime. 2.Getters/Setters,Constructors–SoRepetitive EncapsulatingobjectpropertiesviapublicgetterandsettermethodsissuchacommonpracticeintheJavaworld,andlotsofframeworksrelyonthis“JavaBean”patternextensively(aclasswithanemptyconstructorandget/setmethodsfor“properties”). ThisissocommonthatmostIDE'ssupportauto-generatingcodeforthesepatterns(andmore).However,thiscodeneedstoliveinoursourcesandbemaintainedwhenanewpropertyisaddedorafieldrenamed. Let'sconsiderthisclasswewanttouseasaJPAentity: @Entity publicclassUserimplementsSerializable{ private@IdLongid;//willbesetwhenpersisting privateStringfirstName; privateStringlastName; privateintage; publicUser(){ } publicUser(StringfirstName,StringlastName,intage){ this.firstName=firstName; this.lastName=lastName; this.age=age; } //gettersandsetters:~30extralinesofcode } Thisisarathersimpleclass,butimagineifwehadaddedtheextracodeforgettersandsetters.Wewouldhaveendedupwithadefinitionwheretherewouldbemoreboilerplatezero-valuecodethantherelevantbusinessinformation:“aUserhasfirstandlastnames,andage.” Let'snowLombok-izethisclass: @Entity @Getter@Setter@NoArgsConstructor//transactions=getTransactions(); privateMapgetTransactions(){ finalMapcache=newHashMap<>(); ListtxnRows=readTxnListFromFile(); txnRows.forEach(s->{ String[]txnIdValueTuple=s.split(DELIMETER); cache.put(txnIdValueTuple[0],Long.parseLong(txnIdValueTuple[1])); }); returncache; } } ThisreadssometransactionsfromafileintoaMap.Sincethedatainthefiledoesn'tchange,we'llcacheitonceandallowaccessviaagetter. Ifwenowlookatthecompiledcodeofthisclass,we'llseeagettermethodwhichupdatesthecacheifitwasnullandthenreturnsthecacheddata: publicclassGetterLazy{ privatefinalAtomicReferencetransactions=newAtomicReference(); publicGetterLazy(){ } //othermethods publicMapgetTransactions(){ Objectvalue=this.transactions.get(); if(value==null){ synchronized(this.transactions){ value=this.transactions.get(); if(value==null){ MapactualValue=this.readTxnsFromFile(); value=actualValue==null?this.transactions:actualValue; this.transactions.set(value); } } } return(Map)((Map)(value==this.transactions?null:value)); } } It'sinterestingtopointoutthatLombokwrappedthedatafieldinanAtomicReference.Thisensuresatomicupdatestothetransactionsfield.ThegetTransactions()methodalsomakessuretoreadthefileiftransactionsisnull. WediscourageusingtheAtomicReferencetransactionsfielddirectlyfromwithintheclass.WerecommendusingthegetTransactions()methodforaccessingthefield. Forthisreason,ifweuseanotherLombokannotationlikeToStringinthesameclass,itwillusegetTransactions()insteadofdirectlyaccessingthefield. 4.ValueClasses/DTO's Therearemanysituationsinwhichwewanttodefineadatatypewiththesolepurposeofrepresentingcomplex“values”as“DataTransferObjects,”mostofthetimeintheformofimmutabledatastructureswebuildonceandneverwanttochange. Wedesignaclasstorepresentasuccessfulloginoperation.Wewantallfieldstobenon-nullandobjectsbeimmutablesothatwecanthread-safelyaccessitsproperties: publicclassLoginResult{ privatefinalInstantloginTs; privatefinalStringauthToken; privatefinalDurationtokenValidity; privatefinalURLtokenRefreshUrl; //constructortakingeveryfieldandcheckingnulls //read-onlyaccessor,notnecessarilyasget*()form } Again,theamountofcodewewouldhavetowriteforthecommentedsectionswouldbeofamuchlargervolumethantheinformationwewanttoencapsulateneedstobe.WecanuseLomboktoimprovethis: @RequiredArgsConstructor @Accessors(fluent=true)@Getter publicclassLoginResult{ privatefinal@NonNullInstantloginTs; privatefinal@NonNullStringauthToken; privatefinal@NonNullDurationtokenValidity; privatefinal@NonNullURLtokenRefreshUrl; } Onceweaddthe@RequiredArgsConstructorannotation,we'llgetaconstructorforallthefinalfieldsinttheclass,justaswedeclaredthem.Adding@NonNulltoattributesmakesourconstructorcheckfornullabilityandthrowNullPointerExceptionsaccordingly.Thiswouldalsohappenifthefieldswerenon-finalandweadded@Setterforthem. Dowewanttheboringoldget*()formforourproperties?Sinceweadded@Accessors(fluent=true)inthisexample,“getters”wouldhavethesamemethodnameastheproperties; getAuthToken()simplybecomesauthToken(). This“fluent”formwouldapplytonon-finalfieldsforattributesetters,aswellasallowforchainedcalls: //Imaginefieldswerenolongerfinalnow returnnewLoginResult() .loginTs(Instant.now()) .authToken("asdasd") .//andsoon 5.CoreJavaBoilerplate AnothersituationwhereweendupwritingcodeweneedtomaintainiswhengeneratingthetoString(),equals()andhashCode()methods.IDEstrytohelpwithtemplatesforauto-generatingtheseintermsofourclassattributes. WecanautomatethisbymeansofotherLombokclass-levelannotations: @ToString:willgenerateatoString()methodincludingallclassattributes.Noneedtowriteoneourselvesandmaintainitasweenrichourdatamodel. @EqualsAndHashCode:willgeneratebothequals()andhashCode()methodsbydefaultconsideringallrelevantfields,andaccordingtoverywellthoughsemantics. Thesegeneratorsshipveryhandyconfigurationoptions.Forexample,ifourannotatedclassestakepartofahierarchy,wecanjustusethecallSuper=trueparameterandparentresultswillbeconsideredwhengeneratingthemethod'scode. Todemonstratethis,let'ssaywehadourUserJPAentityexampleincludeareferencetoeventsassociatedtothisuser: @OneToMany(mappedBy="user") privateListevents; Wewouldn'twanttohavethewholelistofeventsdumpedwheneverwecallthetoString()methodofourUser,[email protected],wecanparameterizeitlikethis, @ToString(exclude={“events”}),andthatwon'thappen.Thisisalsohelpfultoavoidcircularreferencesif,forexample,UserEventshadareferencetoaUser. FortheLoginResultexample,wemaywanttodefineequalityandhashcodecalculationjustintermsofthetokenitselfandnottheotherfinalattributesinourclass.Thenwecansimplywritesomethinglike@EqualsAndHashCode(of={“authToken”}). Ifthefeaturesfromtheannotationswe'vereviewedsofarareofinterest,wemaywanttoexamine@Dataand@Valueannotationstoo,astheybehaveasifasetofthemhadbeenappliedtoourclasses.Afterall,thesediscussedusagesareverycommonlyputtogetherinmanycases. 5.1.(Not)Usingthe@EqualsAndHashCodeWithJPAEntities Whetherweshouldusethedefaultequals()andhashCode()methods,orcreatecustomonesfortheJPAentities,isanoftendiscussedtopicamongdevelopers.Therearemultipleapproacheswecanfollow,eachhavingitsprosandcons. Bydefault,@EqualsAndHashCodeincludesallnon-finalpropertiesoftheentityclass.Wecantryto“fix”thisbyusingtheonlyExplicitlyIncludedattributeofthe@EqualsAndHashCodetomakeLombokuseonlytheentity'sprimarykey.Still,thegeneratedequals()methodcancausesomeissues.ThorbenJanssenexplainsthisscenarioingreaterdetailinoneofhisblogposts. Ingeneral,weshouldavoidusingLomboktogeneratetheequals()andhashCode()methodsforourJPAentities. 6.TheBuilderPattern ThefollowingcouldmakeforasampleconfigurationclassforaRESTAPIclient: publicclassApiClientConfiguration{ privateStringhost; privateintport; privatebooleanuseHttps; privatelongconnectTimeout; privatelongreadTimeout; privateStringusername; privateStringpassword; //Whateverotheroptionsyoumaything. //Emptyconstructor?Allcombinations? //getters...andsetters? } Wecouldhaveaninitialapproachbasedonusingtheclassdefaultemptyconstructorandprovidingsettermethodsforeveryfield;however,weideallywantconfigurationsnottobere-setoncethey'vebeenbuilt(instantiated),effectivelymakingthemimmutable.Therefore,wewanttoavoidsetters,butwritingsuchapotentiallylongargsconstructorisananti-pattern. Instead,wecantellthetooltogenerateabuilderpattern,whichnegatesusfromhavingtowriteanextraBuilderclassandtheassociatedfluentsetter-likemethodsbysimplyaddingthe@BuilderannotationtoourApiClientConfiguration: @Builder publicclassApiClientConfiguration{ //...everythingelseremainsthesame } Leavingtheclassdefinitionsuchasabove(nodeclareconstructorsorsetters+@Builder),wecanendupusingitas: ApiClientConfigurationconfig= ApiClientConfiguration.builder() .host("api.server.com") .port(443) .useHttps(true) .connectTimeout(15_000L) .readTimeout(5_000L) .username("myusername") .password("secret") .build(); 7.CheckedExceptionsBurden LotsofJavaAPIsaredesignedsothattheycanthrowanumberofcheckedexceptions;clientcodeisforcedtoeithercatchordeclaretothrows.Howmanytimeshaveweturnedtheseexceptionsweknowwon'thappenintosomethinglikethis?: publicStringresourceAsString(){ try(InputStreamis=this.getClass().getResourceAsStream("sure_in_my_jar.txt")){ BufferedReaderbr=newBufferedReader(newInputStreamReader(is,"UTF-8")); returnbr.lines().collect(Collectors.joining("\n")); }catch(IOException|UnsupportedCharsetExceptionex){ //Ifthiseverhappens,thenitsabug. thrownewRuntimeException(ex);>GETACCESSNOW Genericfooterbanner LearningtobuildyourAPIwithSpring? DownloadtheE-book Commentsareclosedonthisarticle! LaunchDiscount30% OnAllCourses VIEWDETAILS



請為這篇文章評分?