Bevezetés a Lombok projektbe

1. Kerülje az ismétlődő kódot

A Java nagyszerű nyelv, de néha túl bőbeszédű azokhoz a dolgokhoz, amelyeket a kódban meg kell tennie a közös feladatokhoz vagy bizonyos keretrendszerekkel való megfeleléshez. Ezek gyakran nem jelentenek valódi értéket programjainak üzleti oldalán - és itt van a Lombok, hogy boldogabbá és produktívabbá tegye életét.

Úgy működik, hogy csatlakoztatja a build folyamatához, és automatikusan generálja a Java bytecode-ot a .osztály fájlokat a kódban bevezetett számos projekt annotáció szerint.

A beépítése a buildjeibe, bármelyik rendszert is használja, nagyon egyszerű. Projektoldaluk részletes utasításokat tartalmaz a konkrétumokkal kapcsolatban. A projektjeim nagy része maven alapú, ezért tipikusan eldobom függőségüket a biztosítani hatókör, és jó vagyok menni:

 ... org.projectlombok lombok 1.18.10 feltéve ... 

Itt tekintheti meg a legfrissebb elérhető verziót.

Ne feledje, hogy a Lomboktól függően nem teszi felhasználóivá .befőttes üvegs ettől is függenek, mivel ez tiszta felépítésű függőség, nem futásidejű.

2. Getterek / beállítók, kivitelezők - olyan ismétlődőek

Az objektumtulajdonságok nyilvános getter és setter módszerekkel történő beágyazása olyan általános gyakorlat a Java világban, és sok keret sokban támaszkodik erre a „Java Bean” mintázatra: egy osztály üres üres konstruktorral és a get / set metódusok a „tulajdonságokra”.

Ez annyira gyakori, hogy a legtöbb IDE támogatja ezeknek a mintáknak (és még sok másnak) az automatikus előállítását. Ennek a kódnak azonban a forrásaiban kell élnie, és akkor is fenn kell tartania, ha mondjuk új tulajdonságot adunk hozzá, vagy egy mezőt átnevezünk.

Tekintsük ezt az osztályt, amelyet JPA entitásként használni szeretnénk:

@Entity public class A felhasználó végrehajtja a Serializable {private @Id Long id; // akkor lesz beállítva, amikor a privát String firstName megmarad; privát karakterlánc vezetéknév; privát int kor; public Felhasználó () {} public Felhasználó (String keresztnév, String vezetéknév, int kor) {this.firstName = keresztnév; this.lastName = vezetékNév; ez.kor = életkor; } // getterek és beállítók: ~ 30 extra kódsor}

Ez egy meglehetősen egyszerű osztály, de még mindig fontolja meg, ha hozzáadnánk a getterekhez és a beállítókhoz tartozó extra kódot, akkor egy olyan definíciót kapnánk, ahol több kazánlemez nulla értékű kódunk lenne, mint a vonatkozó üzleti információk: „A felhasználónak először és vezetéknevek és életkor. ”

Most engedje meg Lombok-ize ez az osztály:

@Entity @Getter @Setter @NoArgsConstructor // <--- EZ a nyilvános osztály A felhasználó végrehajtja a Serializable {private @Id Long id; // akkor lesz beállítva, amikor a privát karakterlánc megőrzi a keresztnevet; privát karakterlánc vezetéknév; privát int kor; public Felhasználó (String keresztnév, String vezetéknév, int kor) {this.firstName = keresztnév; this.lastName = vezetékNév; ez.kor = életkor; }}

A @Getter és @Setter annotációkat, amelyeket a Lomboknak mondtunk, ezeket generálják az osztály minden területére. @NoArgsConstructor üres konstruktor generációhoz vezet.

Vegye figyelembe, hogy ez az egész osztály kódot, nem hagyok ki semmit, szemben a fenti verzióval a // getterek és beállítók megjegyzés. Három releváns attribútumosztály esetében ez jelentős megtakarítást jelent a kódban!

Ha további attribútumokat (tulajdonságokat) ad hozzá a Felhasználó osztályban, ugyanez fog történni: maga a típusra alkalmazta a kommentárokat, így alapértelmezés szerint az összes mezőt figyelembe veszik.

Mi lenne, ha finomítani akarná egyes tulajdonságok láthatóságát? Például szeretem megtartani az entitásaimat id mező módosítók csomag vagy védett látható, mert várhatóan elolvassák őket, de az alkalmazás kódja nem határozza meg őket kifejezetten. Csak használjon finomabb szemcséket @Setter erre a konkrét területre:

privát @Id @Setter (AccessLevel.VÉDETT) Hosszú azonosító;

3. Lusta Getter

Az alkalmazásoknak gyakran költséges műveletet kell végrehajtaniuk, és el kell menteniük az eredményeket későbbi használatra.

Tegyük fel például, hogy statikus adatokat kell olvasnunk egy fájlból vagy egy adatbázisból. Általában jó gyakorlat ezeket az adatokat egyszer lekérni, majd gyorsítótárba tenni, hogy lehetővé tegye a memóriában történő olvasást az alkalmazáson belül. Ez megmenti az alkalmazást a drága művelet megismétlésétől.

Egy másik gyakori minta az csak akkor szerezze be ezeket az adatokat, amikor először szükséges. Más szavakkal, csak akkor kapja meg az adatokat, amikor a megfelelő gettert először hívják meg. Ezt úgy hívják lusta-rakodó.

Tegyük fel, hogy ezeket az adatokat mezőként tárolják egy osztályon belül. Az osztálynak most meg kell győződnie arról, hogy a mezőhöz való bármilyen hozzáférés visszaadja-e a gyorsítótárazott adatokat. Az ilyen osztály megvalósításának egyik lehetséges módja az, hogy a getter metódust csak akkor kell lekérni, ha a mező van nulla. Emiatt, ezt hívjuk a lusta getter.

Lombok ezt a lusta paraméter a @Getter annotáció fentebb láttuk.

Vegyük például ezt az egyszerű osztályt:

nyilvános osztály GetterLazy {@Getter (lusta = igaz) privát végleges térképi tranzakciók = getTransaction (); privát térkép getTransactions () {final Map cache = new HashMap (); TxnRows = readTxnListFromFile () lista; txnRows.forEach (s -> {String [] txnIdValueTuple = s.split (DELIMETER); cache.put (txnIdValueTuple [0], Long.parseLong (txnIdValueTuple [1]);}); return cache; }}

Ez egyes tranzakciókat fájlból beolvas a Térkép. Mivel a fájl adatai nem változnak, egyszer tároljuk a gyorsítótárat, és engedélyezzük a hozzáférést egy getteren keresztül.

Ha most megnézzük ennek az osztálynak a fordított kódját, akkor a getter módszer, amely frissíti a gyorsítótárat, ha az volt nulla majd visszaadja a gyorsítótárazott adatokat:

nyilvános osztály GetterLazy {privát végleges AtomicReference tranzakciók = új AtomicReference (); public GetterLazy () {} // egyéb módszerek public Térkép getTransactions () {Object value = this.transactions.get (); if (érték == null) {szinkronizált (ez.tranzakciók) {érték = ez.tranzakciók.get (); if (value == null) {Map actualValue = this.readTxnsFromFile (); value = actualValue == null? this.tranzakciók: actualValue; this.tranzakciók.set (érték); }}} return (Térkép) ((Térkép) (érték == ez.tranzakciók? null: érték)); }}

Érdekes erre rámutatni A Lombok az adatmezőt egy AtomicReference.Ez biztosítja az atomfrissítést a tranzakciók terület. A getTransactions () metódus arról is gondoskodik, hogy elolvassa a fájlt, ha tranzakciók van nulla.

A AtomicReference tranzakciók mező közvetlenül az osztályon belül kerülendő. Javasoljuk a getTransactions () módszer a mező elérésére.

Ezért, ha egy másik Lombok-jelölést használunk, mint a ToString ugyanabban az osztályban, használni fogja getTransactions () ahelyett, hogy közvetlenül hozzáférne a mezőhöz.

4. Értékosztályok / DTO-k

Sok helyzetben szeretnénk meghatározni egy adattípust, amelynek kizárólagos célja komplex „értékek” képviselete vagy „adatátviteli objektumok”, legtöbbször megváltoztathatatlan adatstruktúrák formájában, amelyeket egyszer felépítünk, és soha nem akarunk változtatni .

Osztályt tervezünk a sikeres bejelentkezési művelet képviseletében. Szeretnénk, ha az összes mező nem null, és az objektumok megváltoztathatatlanok lesznek, hogy biztonságosan elérhessük a tulajdonságait:

public class LoginResult {privát végleges Azonnali bejelentkezésT; privát végleges String authToken; private final Duration tokenValidity; privát végső URL tokenRefreshUrl; // konstruktor minden mezőt felvesz és ellenőrzi a nullákat // csak olvasható hozzáférés, nem feltétlenül get * () formában

Ismételten a megjegyzéssel ellátott szakaszokhoz írnunk szükséges kód mennyisége sokkal nagyobb volumenű lenne, mint az az információ, amelyet be akarunk foglalni, és amelynek valódi értéke van számunkra. Újra felhasználhatjuk a Lombokot ennek javítására:

@RequiredArgsConstructor @Accessors (fluent = true) @Getter public class LoginResult {private final @NonNull Azonnali bejelentkezésT; privát döntő @NonNull String authToken; privát döntő @NonNull Duration tokenValidity; privát végleges @NonNull URL tokenRefreshUrl; }

Csak adja hozzá a @RequiredArgsConstructor annotációval és kapsz egy konstruktort az osztály összes utolsó mezőjéhez, ahogyan te is deklaráltad őket. Hozzáadás @NonNull attribútumokra készteti konstruktőreinket az érvénytelenség és a dobás ellenőrzésére NullPointerExceptions Eszerint. Ez akkor is megtörténne, ha a mezők nem véglegesek lennének, és hozzáadnánk @Setter nekik.

Nem akarsz unalmas öregeket kap*() űrlap az ingatlanjaihoz? Mert hozzátettük @Accessors (folyékonyan = igaz) ebben a példában a „getters” azonos nevű lenne a tulajdonságokkal: getAuthToken () egyszerűen válik authToken ().

Ez a „folyékony” forma az attribútum-beállítók nem végleges mezőire vonatkozik, és lehetővé teszi a láncolt hívásokat is:

// Képzelje el, hogy a mezők már nem voltak véglegesek, most új LoginResult () .loginTs-t adnak vissza (Instant.now ()) .authToken ("asdasd"). // stb

5. Core Java kazánlap

Egy másik helyzet, amikor végül kódot írunk, amelyet fenn kell tartanunk, az a generálás toString (), egyenlő () és hash kód() mód. Az IDE-k megpróbálnak segíteni ezek sablonjainak létrehozásában, az osztályattribútumaink szempontjából.

Ezt automatizálhatjuk más Lombok osztályszintű kommentárokkal:

  • @ToString: generál a toString () módszer, amely tartalmazza az összes osztályattribútumot. Nem kell magunknak írni és fenntartani, miközben gazdagítjuk az adatmodellünket.
  • @EqualsAndHashCode: mindkettőt generálja egyenlő () és hash kód() módszerek alapértelmezés szerint az összes releváns mezőt figyelembe véve, és szemantika szerint is.

Ezek a generátorok nagyon praktikus konfigurációs lehetőségeket szállítanak. Például, ha a kommentált osztályai részt vesznek egy hierarchiában, akkor egyszerűen használja a callSuper = igaz A paraméter és a szülő eredményeit figyelembe vesszük a módszer kódjának előállításakor.

További erről: mondjuk, hogy megvolt a miénk Felhasználó A JPA entitás példája hivatkozást tartalmaz a felhasználóhoz társított eseményekre:

@OneToMany (mappedBy = "user") privát lista események;

Nem szeretnénk, ha az események teljes listáját felszámolnánk, valahányszor felhívjuk toString () módszer, csak azért, mert a @ToString annotáció. Semmi gond: csak paraméterezd így: @ToString (kizár = {“események”}), és ez nem fog megtörténni. Ez szintén hasznos a körkörös hivatkozások elkerülése érdekében, ha például UserEvents volt utalása a Felhasználó.

A LoginResult Például érdemes meghatározni az egyenlőséget és a kivonatkód-számítást csak a token és nem az osztályunk többi végső attribútuma szempontjából. Ezután egyszerűen írj valami hasonlót @EqualsAndHashCode (/ = {“authToken”}).

Bónusz: ha tetszett az eddig áttekintett kommentárok jellemzői, érdemes megvizsgálnia @Adat és @Érték annotációk, mivel úgy viselkednek, mintha azok egy csoportját alkalmazták volna az osztályainkon. Végül is ezeket a megvitatott felhasználásokat sok esetben nagyon gyakran állítják össze.

5.1. (Nem) A @EqualsAndHashCode A JPA entitásokkal

Az alapértelmezett használatát egyenlő () és hash kód() módszerek vagy egyedi módszerek létrehozása a JPA-entitások számára, a fejlesztők körében gyakran tárgyalt téma. Többféle megközelítést követhetünk; mindegyiknek megvannak az előnyei és hátrányai.

Alapértelmezés szerint, @EqualsAndHashCode tartalmazza az entitásosztály összes nem végleges tulajdonságát. Megpróbálhatjuk ezt a "kijavítani" a onlyExplicitlyIncluded attribútuma @EqualsAndHashCode hogy a Lombok csak az entitás elsődleges kulcsát használja. Mégis, a generált egyenlő () módszer problémákat okozhat. Thorben Janssen az egyik blogbejegyzésében részletesebben kifejti ezt a forgatókönyvet.

Általánosságban, kerülnünk kell a Lombok használatát a egyenlő () és hash kód() módszerek a JPA-entitásaink számára!

6. Az építőminta

A következők készíthetnek egy REST API-ügyfél konfigurációs osztályának mintáját:

public class ApiClientConfiguration {private String host; privát int kikötő; privát logikai felhasználásHttps; privát hosszú connectTimeout; privát hosszú readTimeout; privát String felhasználónév; privát karakterlánc jelszó; // Bármi más lehetőség is van. // Üres kivitelező? Minden kombináció? // getters ... és setters? }

Lehetne egy kezdeti megközelítésünk, amely az osztály alapértelmezett üres konstruktorának használatán alapul, és szetter módszereket biztosít minden mezőhöz. Ideális esetben azonban azt szeretnénk, ha a konfigurációkat nemkészlet miután megépültek (példányosak), gyakorlatilag megváltoztathatatlanná teszik őket. Ezért szeretnénk elkerülni az ülepítőket, de egy ilyen potenciálisan hosszú args konstruktor megírása anti-minta.

Ehelyett elmondhatjuk az eszköznek az a létrehozását építész mintát, megakadályozva, hogy extrát írjunk Építész osztály és a hozzá kapcsolódó folyékony szetterszerű módszerek egyszerűen a @Builder kommentár hozzáadásával a mi ApiClientConfiguration.

@Builder public class ApiClientConfiguration {// ... minden más változatlan marad}

A fenti osztálydefiníciót önmagában elhagyva (ne deklaráljuk a konstruktorokat és a beállítókat + @Építész) a végén így használhatjuk:

ApiClientConfiguration config = ApiClientConfiguration.builder () .host ("api.server.com") .port (443) .useHttps (true) .connectTimeout (15_000L) .readTimeout (5_000L) .username ("felhasználónév"). Jelszó (" titok ") .építés ();

7. Ellenőrzött kivételek terhei

Sok Java API-t úgy alakítottak ki, hogy számos ellenőrzött kivételt dobhassanak, az ügyfélkód kényszerítve van rá fogás vagy kijelenteni dob. Hányszor változtatta meg ezeket a kivételeket, amelyekről tudja, hogy nem fognak történni, ilyesmivé?

public String resourceAsString () {try (InputStream is = this.getClass (). getResourceAsStream ("sure_in_my_jar.txt")) {BufferedReader br = new BufferedReader (new InputStreamReader (is, "UTF-8")); return br.lines (). gyűjt (Collectors.joining ("\ n")); } catch (IOException | UnsupportedCharsetException ex) {// Ha ez valaha is megtörténik, akkor az egy hiba. dobja új RuntimeException (ex); <--- Runtime ex-be kapszulázódik. }}

Ha el akarja kerülni ezeket a kódmintákat, mert a fordító különben nem lesz boldog (és végül is Ön tud az ellenőrzött hibák nem fordulhatnak elő), használja az illő nevet @ SneakyThrows:

@SneakyThrows public String resourceAsString () {try (InputStream is = this.getClass (). GetResourceAsStream ("sure_in_my_jar.txt")) {BufferedReader br = new BufferedReader (new InputStreamReader (is, "UTF-8")); return br.lines (). gyűjt (Collectors.joining ("\ n")); }}

8. Gondoskodjon az erőforrások felszabadításáról

A Java 7 bevezette az erőforrásokkal való próbálkozás blokkot annak biztosítására, hogy erőforrásait bármi megvalósító példány birtokolja java.lang.Automatikusan zárható kilépéskor szabadon engedik.

A Lombok ennek alternatív módját kínálja, és rugalmasabban a @Cleanup segítségével. Használja minden olyan helyi változóhoz, amelynek erőforrásait biztosítani kívánja. Nincs szükség arra, hogy egy adott felületet telepítsenek, csak megkapja Bezárás() nevű módszer.

A @Cleanup InputStream = this.getClass (). GetResourceAsStream ("res.txt");

A kiadási módszerének más neve van? Nem probléma, csak testre szabhatja a kommentárokat:

@Cleanup ("dispose") JFrame mainFrame = new JFrame ("Main Window");

9. Jegyezze fel osztályát, hogy naplózót szerezzen

Sokan a naplózási utasításokat kíméletesen adjuk hozzá a kódunkhoz az a példány létrehozásával Logger a választott keretünkből. Mondjuk, SLF4J:

public class ApiClientConfiguration {private static Logger LOG = LoggerFactory.getLogger (ApiClientConfiguration.class); // LOG.debug (), LOG.info (), ...}

Ez annyira gyakori minta, hogy a Lombok fejlesztői törődtek azzal, hogy egyszerűsítsék számunkra:

@ Slf4j // vagy: @Log @CommonsLog @ Log4j @ Log4j2 @ XSlf4j nyilvános osztály ApiClientConfiguration {// log.debug (), log.info (), ...}

Számos naplózási keretrendszer támogatott, és természetesen testreszabhatja a példány nevét, témáját stb.

10. Írjon szálbiztosabb módszereket

A Java-ban a szinkronizált kulcsszó a kritikus szakaszok megvalósításához. Ez azonban nem 100% -ban biztonságos megközelítés: más kliens kódok is szinkronizálódhatnak a példányon, ami váratlan holtpontokhoz vezethet.

Ez az, ahol @Szinkronizált bejön: jegyezze fel vele a metódusait (mind a példányokat, mind a statikusakat), és kap egy automatikusan generált privát, nem megvilágított mezőt, amelyet a megvalósítása a zároláshoz fog használni:

@Szinkronizált nyilvános / * jobb, mint: szinkronizált * / void putValueInCache (karakterlánc-kulcs, objektumérték) {// bármi itt lesz szálbiztos kód}

11. Automatizálja az objektumok összetételét

A Java-nak nincsenek nyelvi szintű konstrukciói a kompozíció-öröklődés szemléletének simítására. Más nyelvek beépített fogalmakkal rendelkeznek, mint pl Vonások vagy Mixins elérni ezt.

A Lombok @Delegate nagyon hasznos, ha ezt a programozási mintát szeretné használni. Vegyünk egy példát:

  • Mi akarunk Felhasználós és Vevős megosztani a név és a telefonszám néhány gyakori tulajdonságát
  • Ezekhez a mezőkhöz mind interfészt, mind adapter osztályt definiálunk
  • Majd modelljeink megvalósítják a kezelőfelületet és @Delegált az adapterükhöz, hatékonyan komponálás elérhetőségünkkel

Először határozzunk meg egy interfészt:

nyilvános felület HasContactInformation {String getFirstName (); void setFirstName (String keresztnév); Karakterlánc getFullName (); String getLastName (); void setLastName (karakterlánc vezetékneve); Karakterlánc getPhoneNr (); void setPhoneNr (karakterlánc-telefonszám); }

És most egy adapter, mint a támogatás osztály:

@Data public class ContactInformationSupport megvalósítja a HasContactInformation {private String firstName; privát karakterlánc vezetéknév; privát String telefonNr; @ Nyilvános karakterlánc felülírása getFullName () {return getFirstName () + "" + getLastName (); }}

Most jön az érdekes rész, nézze meg, milyen egyszerű most összeállítani az elérhetőségeket mindkét modellosztályban:

public class Felhasználó végrehajtja a HasContactInformation {// bármelyik másik felhasználóspecifikus attribútumot @Delegate (type = {HasContactInformation.class}) privát végleges ContactInformationSupport contactInformation = új ContactInformationSupport (); // A felhasználó maga az összes elérhetőséget delegálás útján valósítja meg}

Az ügy Vevő annyira hasonló lenne, hogy a rövidség érdekében kihagynánk a mintát.

12. Lombok visszagurítása?

Rövid válasz: Egyáltalán nem.

Aggódhat, hogy van esély arra, hogy a Lombokot használja valamelyik projektjében, de később vissza kívánja állítani ezt a döntést. Ezután talán nagy számú osztályt jegyeznél fel rá ... mit tehetnél?

Ezt még soha nem bántam meg, de ki tudja Ön, csapata vagy szervezete számára. Ezekben az esetekben a delombok eszköz ugyanabból a projektből.

Által delombok-ing a kódot, automatikusan generált Java forráskódot kapna, pontosan ugyanazokkal a tulajdonságokkal, mint a Lombok bájtkódjában. Tehát egyszerűen kicserélheti az eredeti jegyzetekkel ellátott kódot ezekkel az új kódokkal lebontva fájlokat, és már nem attól függenek.

Ezt integrálhatja a buildjébe, és ezt a múltban is megtettem, hogy csak tanulmányozzam a létrehozott kódot, vagy hogy integráljam a Lombokot más Java forráskód alapú eszközzel.

13. Következtetés

Van még néhány olyan funkció, amelyet ebben a cikkben nem ismertettünk. Azt javasoljuk, hogy mélyebben merüljön el a funkció áttekintésében további részletek és felhasználási esetek érdekében.

A legtöbb általunk bemutatott funkciónak számos testreszabási lehetősége van, amelyek hasznosak lehetnek annak érdekében, hogy az eszköz olyan dolgokat generáljon, amelyek a legjobban megfelelnek a névadásokra vonatkozó csapatgyakorlatának stb. A rendelkezésre álló beépített konfigurációs rendszer szintén segíthet ebben.

Remélem, megtalálta a motivációt, hogy esélyt adjon a Lomboknak a Java fejlesztői eszközkészletbe való belépéshez. Próbálja ki, és növelje a termelékenységet!

A példakód a GitHub projektben található.


$config[zx-auto] not found$config[zx-overlay] not found