Úvod do projektu Lombok

1. Nepoužívajte opakujúci sa kód

Java je vynikajúci jazyk, ale niekedy je príliš podrobný, čo musíte v kóde urobiť, aby ste mohli vykonať bežné úlohy alebo dodržať niektoré rámcové postupy. Tieto často neprinášajú skutočnú hodnotu pre obchodnú stránku vašich programov - a práve tu je Lombok, ktorý vám umožní šťastnejší život a zvýšenie produktivity vášho života.

Funguje to tak, že sa zapojíte do procesu vytvárania a automaticky sa generuje bajtový kód Java do vášho súboru .trieda súbory podľa počtu anotácií projektu, ktoré uvediete vo svojom kóde.

Zahrnutie do vašich zostáv, nech už používate akýkoľvek systém, je veľmi jednoduché. Ich projektová stránka obsahuje podrobné pokyny k podrobnostiam. Väčšina mojich projektov je založená na zákulisí, takže ich závislosť v systéme obyčajne klesám za predpokladu rozsah a je dobré ísť:

 ... org.projectlombok lombok 1.18.10 za predpokladu ... 

Tu nájdete najnovšiu dostupnú verziu.

Upozorňujeme, že v závislosti od Lomboku vás používatelia neurobia .jarzáleží tiež na tom, pretože ide o závislosť čistého zostavenia, nie za behu.

2. Getters / Setters, Constructors - So Repetitive

Zapuzdrenie vlastností objektu pomocou metód verejného getra a setra je vo svete Java taká bežná prax a veľa rámcov sa vo veľkej miere spolieha na tento vzor „Java Bean“: trieda s prázdnym konštruktorom a metódy get / set pre „vlastnosti“.

To je také bežné, že väčšina IDE podporuje autogeneračný kód pre tieto vzory (a ďalšie). Tento kód však musí byť vo vašich zdrojoch a musí sa tiež udržiavať, keď sa napríklad pridá nová vlastnosť alebo premenuje pole.

Zoberme si ako príklad túto triedu, ktorú chceme použiť ako entitu JPA:

@Entity verejná trieda Používateľ implementuje Serializable {private @Id Long id; // nastaví sa pri pretrvávaní súkromného reťazca firstName; private String priezvisko; súkromný int vek; public User () {} public User (String firstName, String lastName, int age) {this.firstName = firstName; this.lastName = priezvisko; this.age = vek; } // hľadači a zakladatelia: ~ 30 riadkov kódu navyše}

Toto je pomerne jednoduchá trieda, ale aj tak zvážte, že ak by sme pridali ďalší kód pre getry a settery, skončili by sme s definíciou, kde by sme mali viac štandardných kódov nulovej hodnoty ako príslušné obchodné informácie: „Používateľ má najskôr a priezviská a vek. “

Poďme teraz Lombok-ize táto trieda:

@Entity @Getter @Setter @NoArgsConstructor // <--- TOTO je to verejná trieda Používateľ implementuje Serializable {private @Id Long id; // nastaví sa pri pretrvávaní súkromného reťazca firstName; private String priezvisko; súkromný int vek; public User (String firstName, String lastName, int age) {this.firstName = firstName; this.lastName = priezvisko; this.age = vek; }}

Pridaním @Získajte a @Setter anotácie, ktoré sme povedali Lomboku, aby ich generoval pre všetky polia triedy. @NoArgsConstructor povedie k prázdnej generácii konštruktora.

Toto je celý kód triedy, nevynechávam nič, na rozdiel od verzie vyššie s // getre a setre komentovať. Pre triedu troch relevantných atribútov je to významná úspora kódu!

Ak do svojho súboru ďalej pridáte atribúty (vlastnosti) Používateľ triedy, stane sa to isté: anotácie ste aplikovali na samotný typ, aby im štandardne vadili všetky polia.

Čo keby ste chceli spresniť viditeľnosť niektorých vlastností? Napríklad by som rád ponechal id poľné modifikátory balíček alebo chránené sú viditeľné, pretože sa očakáva, že budú prečítané, ale nie sú explicitne nastavené v kóde aplikácie. Stačí použiť jemnejšie zrnité @Setter pre túto konkrétnu oblasť:

private @Id @Setter (AccessLevel.PROTECTED) Long id;

3. Lazy Getter

Aplikácie často musia vykonať nejakú nákladnú operáciu a uložiť výsledky pre ďalšie použitie.

Povedzme napríklad, že musíme načítať statické údaje zo súboru alebo databázy. Všeobecne je dobrým zvykom tieto dáta načítať jedenkrát a potom ich uložiť do medzipamäte, aby sa umožnilo čítanie v aplikácii. To šetrí aplikáciu pred opakovaním nákladnej operácie.

Ďalším bežným vzorom je načítať tieto údaje iba v prípade, že ich potrebujete prvýkrát. Inými slovami, údaje získajte až pri prvom volaní zodpovedajúceho kariérneho postupu. Toto sa volá lenivé nakladanie.

Predpokladajme, že sa tieto údaje ukladajú do medzipamäte ako pole vo vnútri triedy. Trieda sa teraz musí ubezpečiť, že akýkoľvek prístup k tomuto poľu vráti údaje z medzipamäte. Jedným z možných spôsobov implementácie takejto triedy je umožniť getrovej metóde načítať údaje, iba ak je toto pole nulový. Pre tento dôvod, hovoríme tomu a lenivý getter.

Lombok to umožňuje pomocou lenivý parameter v @Getter anotácia videli sme vyššie.

Zvážte napríklad túto jednoduchú triedu:

verejná trieda GetterLazy {@Getter (lazy = true) súkromné ​​konečné transakcie mapy = getTransactions (); private Map getTransactions () {final Map cache = new HashMap (); Zoznam txnRows = readTxnListFromFile (); txnRows.forEach (s -> {String [] txnIdValueTuple = s.split (DELIMETER); cache.put (txnIdValueTuple [0], Long.parseLong (txnIdValueTuple [1]));}); návratová keška; }}

Toto načíta niektoré transakcie zo súboru do a Mapa. Pretože sa údaje v súbore nemenia, uložíme ich do medzipamäte raz a umožníme prístup pomocou getra.

Ak sa teraz pozrieme na skompilovaný kód tejto triedy, uvidíme a metóda getra, ktorá aktualizuje vyrovnávaciu pamäť, ak bola nulový a potom vráti dáta uložené v pamäti:

verejná trieda GetterLazy {súkromné ​​konečné transakcie AtomicReference = nový AtomicReference (); public GetterLazy () {} // ďalšie metódy public Map getTransaction () {Object value = this.transactions.get (); if (value == null) {synchronized (this.transactions) {value = this.transactions.get (); if (value == null) {Map actualValue = this.readTxnsFromFile (); value = actualValue == null? this.transactions: actualValue; this.transactions.set (hodnota); }}} return (Map) ((Map) (value == this.transactions? null: value)); }}

Je zaujímavé poukázať na to Lombok zabalil dátové pole do Atómová referencia.To zaisťuje atómové aktualizácie transakcie lúka. The getTransactions () metóda tiež zaisťuje čítanie súboru, ak transakcie je nulový.

Využitie Transakcie AtomicReference pole priamo z triedy sa neodporúča. Odporúča sa používať getTransactions () metóda prístupu do poľa.

Z tohto dôvodu, ak použijeme inú anotáciu typu Lombok Natiahnuť v tej istej triede, použije getTransactions () namiesto priameho prístupu do poľa.

4. Hodnotové triedy / DTO

Existuje veľa situácií, v ktorých chceme definovať dátový typ s jediným účelom, ktorý predstavuje komplexné „hodnoty“ alebo ako „objekty prenosu údajov“, väčšinou vo forme nemenných dátových štruktúr, ktoré zostavíme raz a nikdy ich nebudeme chcieť zmeniť. .

Navrhujeme triedu, ktorá predstavuje úspešnú operáciu prihlásenia. Chceme, aby všetky polia nemali hodnotu null a aby objekty boli nemenné, aby sme mohli bezpečne získať prístup k jeho vlastnostiam:

verejná trieda LoginResult {súkromné ​​konečné okamžité prihlásenie; súkromný konečný reťazec authToken; súkromná konečná doba platnosti tokenPlatnosť; súkromná cieľová webová adresa tokenRefreshUrl; // konštruktor, ktorý berie každé pole a kontroluje hodnoty null // prístupový objekt iba na čítanie, nie nevyhnutne ako get * () form}

Opäť platí, že množstvo kódu, ktoré by sme museli napísať pre komentované časti, by malo oveľa väčší objem ako informácie, ktoré chceme zapuzdriť, a ktoré má pre nás skutočnú hodnotu. Na vylepšenie môžeme použiť Lombok znova:

@RequiredArgsConstructor @Accessors (fluent = true) @Getter verejná trieda LoginResult {súkromné ​​finále @NonNull Okamžité prihlásenieTs; súkromné ​​finále @NonNull Reťazec authToken; private final @NonNull Trvanie tokenValidity; súkromné ​​konečné @NonNull URL tokenRefreshUrl; }

Stačí pridať @RequiredArgsConstructor anotáciu a dostali by ste konštruktor pre všetky posledné polia v triede, rovnako ako ste ich deklarovali. Pridávanie @NonNull atribútov umožňuje nášmu konštruktoru skontrolovať, či je povolená null a vrhanie NullPointerExceptions podľa toho. Stalo by sa to aj vtedy, keby polia neboli konečné a pridali sme @Setter pre nich.

Nechceš byť nudný starý? získať * () formulár pre vaše nehnuteľnosti? Pretože sme pridali @Accessors (plynule = pravda) v tomto príklade by „getre“ mali rovnaký názov metódy ako vlastnosti: getAuthToken () jednoducho sa stáva authToken ().

Tento „plynulý“ formulár by sa vzťahoval na nedefinované polia pre nastavovateľov atribútov a rovnako by umožňoval reťazené volania:

// Predstavte si, že polia už nie sú konečné, teraz vracajte nové LoginResult () .loginTs (Instant.now ()) .authToken ("asdasd"). // a tak ďalej

5. Core Java Boilerplate

Ďalšou situáciou, v ktorej skončíme s písaním kódu, musíme udržiavať, je generovanie natiahnuť(), rovná sa () a hashCode () metódy. IDE sa snažia pomôcť so šablónami na ich automatické generovanie z hľadiska atribútov našej triedy.

Môžeme to automatizovať pomocou ďalších anotácií na úrovni triedy Lombok:

  • @Natiahnuť: vygeneruje a natiahnuť() metóda vrátane všetkých atribútov triedy. Nie je potrebné si jeden sami písať a udržiavať ho tak, ako obohacujeme náš dátový model.
  • @EqualsAndHashCode: vygeneruje oboje rovná sa () a hashCode () metódy štandardne berúc do úvahy všetky príslušné polia a podľa veľmi dobre sémantiky.

Tieto generátory poskytujú veľmi užitočné možnosti konfigurácie. Napríklad ak vaše anotované triedy tvoria časť hierarchie, môžete jednoducho použiť callSuper = pravda pri generovaní kódu metódy budú zohľadnené parametre a nadradené výsledky.

Viac k tomu: povedzme, že sme mali svoje Používateľ Príklad entity JPA obsahuje odkaz na udalosti spojené s týmto používateľom:

@OneToMany (mappedBy = "užívateľ") súkromné ​​udalosti zoznamu;

Neradi by sme, aby bol celý zoznam udalostí vyhodený, kedykoľvek zavoláme natiahnuť() metóda nášho Používateľa, len preto, že sme použili @Natiahnuť anotácia. Žiadny problém: jednoducho to parametrizujte takto: @ToString (vylúčiť = {„udalosti“}), a to sa nestane. To je tiež užitočné vyhnúť sa kruhovým odkazom, ak napríklad UserEvents mala odkaz na a Používateľ.

Pre Výsledok prihlásenia napríklad môžeme chcieť definovať rovnosť a výpočet hash kódu iba z hľadiska samotného tokenu a nie ostatných konečných atribútov v našej triede. Potom jednoducho napíšte niečo ako @EqualsAndHashCode (z = {„authToken“}).

Bonus: ak sa vám páčili funkcie z anotácií, ktoré sme doteraz skontrolovali, možno budete chcieť preskúmať @ Údaje a @Hodnota anotácie, pretože sa správajú, akoby ich skupina bola aplikovaná na naše triedy. Nakoniec, tieto diskutované zvyklosti sú v mnohých prípadoch veľmi často spojené.

5.1. (Ne) Používanie @EqualsAndHashCode S entitami JPA

Či sa má použiť predvolené nastavenie rovná sa () a hashCode () metód alebo vytvárať vlastné pre entity JPA, je často diskutovanou témou medzi vývojármi. Môžeme nasledovať niekoľko prístupov; každý má svoje klady a zápory.

Predvolene, @EqualsAndHashCode zahŕňa všetky nefinálne vlastnosti triedy entít. Môžeme sa to pokúsiť „opraviť“ pomocou onlyExplicitlyIncluded atribút @EqualsAndHashCode aby Lombok používal iba primárny kľúč entity. Stále však generované rovná sa () metóda môže spôsobiť určité problémy. Thorben Janssen vysvetľuje tento scenár podrobnejšie v jednom zo svojich blogových príspevkov.

Všeobecne, Mali by sme sa vyhnúť použitiu Lomboku na generovanie rovná sa () a hashCode () metódy pre naše subjekty JPA!

6. Staviteľský vzor

Nasledujúce položky môžu vytvoriť ukážkovú konfiguračnú triedu pre klienta REST API:

public class ApiClientConfiguration {private String host; súkromný int port; súkromné ​​booleovské použitieHttps; private long connectTimeout; private long readTimeout; súkromné ​​reťazcové používateľské meno; súkromné ​​reťazcové heslo; // Akékoľvek ďalšie možnosti, ktoré sa vám môžu páčiť. // Prázdny konštruktor? Všetky kombinácie? // zakladatelia ... a zakladatelia? }

Mohli by sme mať počiatočný prístup založený na použití predvoleného prázdneho konštruktora triedy a poskytnutia metód nastavenia pre každé pole. V ideálnom prípade by sme však chceli, aby sa konfigurácie opätovnenastaviť akonáhle boli postavené (inštancované), účinne ich robia nemennými. Chceme sa preto vyhnúť setterom, ale napísanie takéhoto potenciálne dlhého args konštruktora je anti-vzor.

Namiesto toho môžeme nástroju vygenerovať a staviteľ vzor, ​​ktorý nám bráni napísať navyše Staviteľ triedy a súvisiace plynulé metódy podobné nastavovaču jednoduchým pridaním anotácie @Builder do našej ApiClientConfiguration.

@Builder verejná trieda ApiClientConfiguration {// ... všetko ostatné zostáva rovnaké}

Ponechanie vyššie uvedenej definície triedy ako takej (bez deklaračných konštruktorov ani setterov + @Builder) môžeme to nakoniec použiť ako:

ApiClientConfiguration config = ApiClientConfiguration.builder () .host ("api.server.com") .port (443) .useHttps (true) .connectTimeout (15_000L) .readTimeout (5_000L) .username ("myusername") .password (" tajné ") .build ();

7. Zaťaženie kontrolovaných výnimiek

Veľa rozhraní Java API je navrhnutých tak, aby umožňovali použitie viacerých kontrolovaných výnimiek, ku ktorým je kód klienta nútený chytiť alebo vyhlásiť hodí. Koľkokrát ste zmenili tieto výnimky, o ktorých viete, že sa z nich nestane niečo také?

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 (). collect (Collectors.joining ("\ n")); } catch (IOException | UnsupportedCharsetException ex) {// Ak sa to niekedy stane, je to chyba. hodiť nový RuntimeException (ex); <--- zapuzdruje sa do modulu Runtime ex. }}

Ak sa chcete vyhnúť týmto vzorom kódu, pretože kompilátor nebude inak šťastný (a koniec koncov vy vedieť ku kontrolovaným chybám nemôže dôjsť), použite príhodne pomenované @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 (). collect (Collectors.joining ("\ n")); }}

8. Zaistite, aby boli vaše zdroje uvoľnené

Java 7 predstavila blok try-with-resources, aby zaistila, že vaše zdroje budú mať inštancie akýchkoľvek implementácií java.lang.Automatické uzatváranie sa pri výstupe uvoľnia.

Lombok poskytuje alternatívny spôsob, ako to dosiahnuť, a pružnejšie prostredníctvom služby @Cleanup. Použite ho pre každú lokálnu premennú, ktorej zdroje sa chcete uistiť, že sú uvoľnené. Nie je potrebné, aby implementovali nejaké konkrétne rozhranie, iba získate jeho Zavrieť() metóda tzv.

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

Vaša metóda uvoľnenia má iný názov? Žiadny problém, stačí prispôsobiť anotáciu:

@Cleanup ("dispose") JFrame mainFrame = nový JFrame ("hlavné okno");

9. Anotujte svoju triedu a získajte záznamníka

Mnoho z nás pridáva výpisy z protokolov do nášho kódu striedavo vytváraním inštancie a Logger z nášho rámca voľby. Povedz, SLF4J:

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

Toto je taký bežný vzor, ​​že vývojári spoločnosti Lombok sa snažili, aby nám to zjednodušili:

@ Slf4j // alebo: @Log @CommonsLog @ Log4j @ Log4j2 @ XSlf4j verejná trieda ApiClientConfiguration {// log.debug (), log.info (), ...}

Je podporovaných veľa protokolovacích rámcov a samozrejme môžete prispôsobiť názov inštancie, tému atď.

10. Napíšte metódy bezpečnejšie pre vlákno

V prostredí Java môžete použiť synchronizované kľúčové slovo na implementáciu kritických častí. Toto však nie je 100% bezpečný prístup: nakoniec sa môže synchronizovať vo vašej inštancii aj iný kód klienta, čo môže viesť k neočakávaným zablokovaniu.

Toto je kde @ Synchronizované prichádza dovnútra: anotujte svoje metódy (inštančné aj statické) a získate automaticky generované súkromné ​​neexponované pole, ktoré vaša implementácia použije na uzamknutie:

@ Synchronizované verejné / * lepšie ako: synchronizované * / void putValueInCache (kľúč reťazca, hodnota objektu) {// akýkoľvek kód bude bezpečný pre vlákna}

11. Automatizujte zloženie objektov

Java nemá konštrukty na jazykovej úrovni, ktoré by vyhladili prístup „zvýhodnenia zloženia“. Ostatné jazyky majú zabudované koncepty ako napr Rysy alebo Mixíny aby sme to dosiahli.

Ak chcete použiť tento programovací vzor, ​​program Lombok @Delegate je veľmi užitočný. Uvažujme príklad:

  • Chceme Používateľs a Zákazníks zdieľať niektoré spoločné atribúty pre pomenovanie a telefónne číslo
  • Pre tieto polia definujeme rozhranie aj triedu adaptéra
  • Necháme naše modely implementovať rozhranie a @ Delegát k ich adaptéru komponovanie im naše kontaktné informácie

Najskôr definujeme rozhranie:

verejné rozhranie HasContactInformation {String getFirstName (); void setFirstName (reťazec firstName); Reťazec getFullName (); Reťazec getLastName (); void setLastName (reťazec priezvisko); Reťazec getPhoneNr (); void setPhoneNr (String phoneNr); }

A teraz adaptér ako podpora trieda:

@ Dáta - verejná trieda ContactInformationSupport implementuje HasContactInformation {private String firstName; private String priezvisko; private String phoneNr; @Override public String getFullName () {return getFirstName () + "" + getLastName (); }}

Zaujímavá časť prichádza teraz a uvidíte, aké ľahké je teraz zostaviť kontaktné informácie do oboch modelových tried:

verejná trieda Používateľ implementuje HasContactInformation {// Akékoľvek ďalšie atribúty špecifické pre používateľa @Delegate (typy = {HasContactInformation.class}) súkromné ​​konečné ContactInformationSupport contactInformation = nové ContactInformationSupport (); // Používateľ sám implementuje všetky kontaktné informácie delegovaním}

Prípad pre Zákazník by boli také podobné, že by sme vynechali vzorku pre stručnosť.

12. Rolovanie Lomboku dozadu?

Krátka odpoveď: Vôbec nie.

Možno sa obávate, že existuje šanca, že Lombok použijete v jednom zo svojich projektov, ale neskôr budete chcieť toto rozhodnutie vrátiť späť. Potom by ste mohli mať k tomu komentovaný možno veľký počet tried ... čo by ste mohli robiť?

Nikdy som to skutočne neoľutoval, ale ktovie pre vás, váš tím alebo organizáciu. V týchto prípadoch ste krytí vďaka delombok nástroj z rovnakého projektu.

Autor: delombok-ing váš kód by ste dostali automaticky vygenerovaný zdrojový kód Java s úplne rovnakými vlastnosťami ako z postaveného bytecode Lombok. Takže potom môžete jednoducho nahradiť svoj pôvodný anotovaný kód týmito novými delombokovaný súbory a už na tom nezávisí.

To je niečo, čo môžete integrovať do svojej zostavy a ja som to v minulosti robil len kvôli štúdiu vygenerovaného kódu alebo k integrácii Lomboku s nejakým iným nástrojom založeným na zdrojovom kóde Java.

13. Záver

Existuje niekoľko ďalších funkcií, ktoré sme v tomto článku neprezentovali. Odporúčam vám, aby ste sa podrobnejšie oboznámili s prehľadom funkcií, kde nájdete ďalšie podrobnosti a prípady použitia.

Väčšina funkcií, ktoré sme ukázali, má tiež množstvo možností prispôsobenia, ktoré vám môžu pomôcť, aby nástroj generoval veci, ktoré sú najviac v súlade s vašimi tímovými postupmi pri pomenovávaní atď. K tomu vám môže pomôcť aj vstavaný konfiguračný systém.

Dúfam, že ste našli motiváciu dať Lomboku príležitosť vstúpiť do svojej vývojovej sady nástrojov Java. Vyskúšajte to a zvýšte svoju produktivitu!

Vzorový kód nájdete v projekte GitHub.