Spring Boot integracija Lombok-a — anotacijama pojednostavite Java kod, npr. getter i setter
Spring Boot je još od verzije 2.1.x ugrađivao zavisnost ka Lombok-u u starter, a Intellij IDEA je još od verzije 2020.3 ugrađeno sadržao dodatak za Lombok. Zašto ga svi podržavaju? Šta je toliko moćno u Lombok-u? Danas ćemo nadoknaditi tu lekciju.
Lombok u nekoliko reči
Lombok se na zvaničnoj stranici predstavlja ovako:
Project Lombok makes java a spicier language by adding 'handlers' that know how to build and compile simple, boilerplate-free, not-quite-java code.
Uglavnom, Lombok je dobra biblioteka koja Java kodu dodaje neke "obrađivače" koji ga čine konciznijim i elegantnijim. Po mom mišljenju, najveća prednost Lombok-a jeste u tome što na način anotacija pojednostavljuje Java kod — do koje mere?
Kao Java programer sigurno ste napisali mnoštvo getter / setter metoda; iako se uz pomoć IDE-a mogu generisati automatski, kada Javabean ima mnogo polja, neminovno se stvara velika količina getter / setter koda koji izgleda nepregledno, poput dugog i nespretnog komada tkanine.
class Cmower {
private int age;
private String name;
private BigDecimal money;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
}Lombok pomoću anotacija, u trenutku kompilacije, automatski za polja Javabean-a generiše getter / setter; ne samo to, već može generisati i konstruktor, equals, hashCode i toString. Napomena: to se dešava u trenutku kompilacije; u izvornom kodu ne postoje getter / setter i slično.
@Getter
@Setter
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
}Pogledajte, izvorni kod izgleda mnogo mršavije, zar ne?
Integracija Lombok-a
Ako se projekat gradi pomoću Maven-a, dodavanje zavisnosti ka Lombok-u postaje prilično lako.
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>Pri čemu scope=provided ukazuje da Lombok deluje samo u fazi kompilacije. Drugim rečima, Lombok će tokom kompilacije, nečujno, izvorni fajl sa Lombok anotacijama pravilno prevesti u potpun class fajl.
Od verzije SpringBoot 2.1.x nadalje nije potrebno eksplicitno dodavati Lombok zavisnost. Nakon toga, potrebno je u Intellij IDEA instalirati dodatak Lombok, jer bez njega getter / setter Javabean-a ne mogu da se automatski kompajliraju i ne mogu da se pozivaju. Ipak, novija izdanja Intellij IDEA već ga ugrađuju, pa instalacija nije potrebna.

Često korišćene Lombok anotacije
1)@Getter / @Setter
@Getter / @Setter se koriste veoma fleksibilno, na primer ovako:
class CmowerLombok {
@Getter @Setter private int age;
@Getter private String name;
@Setter private BigDecimal money;
}Sadržaj fajla bajtkoda nakon dekompilacije:
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
}2)@ToString
Anotacija @ToString može da generiše metod toString i odličan je pomoćnik pri ispisu logova.
@ToString
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
}Sadržaj fajla bajtkoda nakon dekompilacije:
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
public String toString() {
return "CmowerLombok(age=" + this.age + ", name=" + this.name + ", money=" + this.money + ")";
}
}3)@Val
Pri pisanju JavaScript koda uvek sam smatrao da je var tip deklaracije promenljive veoma zgodan. Lombok pruža nešto slično.
class CmowerLombok {
public void test() {
val names = new ArrayList<String>();
names.add("Chenmo Wang Er");
val name = names.get(0);
System.out.println(name);
}
}Sadržaj fajla bajtkoda nakon dekompilacije:
class CmowerLombok {
public void test() {
ArrayList<String> names = new ArrayList();
names.add("Chenmo Wang Er");
String name = (String) names.get(0);
System.out.println(name);
}
}4)@Data
Anotacija @Data može da generiše getter / setter, equals, hashCode i toString — sveobuhvatna je.
@Data
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
}Sadržaj fajla bajtkoda nakon dekompilacije:
class CmowerLombok {
private int age;
private String name;
private BigDecimal money;
public int getAge() {
return this.age;
}
public String getName() {
return this.name;
}
public BigDecimal getMoney() {
return this.money;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof CmowerLombok)) {
return false;
} else {
CmowerLombok other = (CmowerLombok) o;
if (!other.canEqual(this)) {
return false;
} else if (this.getAge() != other.getAge()) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name != null) {
return false;
}
} else if (!this$name.equals(other$name)) {
return false;
}
Object this$money = this.getMoney();
Object other$money = other.getMoney();
if (this$money == null) {
if (other$money != null) {
return false;
}
} else if (!this$money.equals(other$money)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof CmowerLombok;
}
public int hashCode() {
int PRIME = true;
int result = 1;
int result = result * 59 + this.getAge();
Object $name = this.getName();
result = result * 59 + ($name == null ? 43 : $name.hashCode());
Object $money = this.getMoney();
result = result * 59 + ($money == null ? 43 : $money.hashCode());
return result;
}
public String toString() {
return "CmowerLombok(age=" + this.getAge() + ", name=" + this.getName() + ", money=" + this.getMoney() + ")";
}
}5)@Slf4j
@Slf4j se može koristiti za generisanje objekta za logovanje; možete prema vlastitoj implementaciji logovanja izabrati različite anotacije, kao što su: @Log, @Log4j, @Log4j2, @Slf4j itd.
@Slf4j
public class Log4jDemo {
public static void main(String[] args) {
log.info("level:{}","info");
log.warn("level:{}","warn");
log.error("level:{}", "error");
}
}Sadržaj fajla bajtkoda nakon dekompilacije:
public class Log4jDemo {
private static final Logger log = LoggerFactory.getLogger(Log4jDemo.class);
public Log4jDemo() {
}
public static void main(String[] args) {
log.info("level:{}", "info");
log.warn("level:{}", "warn");
log.error("level:{}", "error");
}
}6)@Builder
Anotacija @Builder može se koristiti za kreiranje objekata preko obrazaca graditelja (builder), čime je dodela vrednosti lančanim pozivima veoma zgodna.
@Builder
@ToString
public class BuilderDemo {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
BuilderDemo demo = BuilderDemo.builder().age(18).name("Chenmo Wang Er").build();
System.out.println(demo);
}
}Sadržaj fajla bajtkoda nakon dekompilacije:
public class BuilderDemo {
private Long id;
private String name;
private Integer age;
public static void main(String[] args) {
BuilderDemo demo = builder().age(18).name("Chenmo Wang Er").build();
System.out.println(demo);
}
BuilderDemo(final Long id, final String name, final Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public static BuilderDemo.BuilderDemoBuilder builder() {
return new BuilderDemo.BuilderDemoBuilder();
}
public String toString() {
return "BuilderDemo(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
public static class BuilderDemoBuilder {
private Long id;
private String name;
private Integer age;
BuilderDemoBuilder() {
}
public BuilderDemo.BuilderDemoBuilder id(final Long id) {
this.id = id;
return this;
}
public BuilderDemo.BuilderDemoBuilder name(final String name) {
this.name = name;
return this;
}
public BuilderDemo.BuilderDemoBuilder age(final Integer age) {
this.age = age;
return this;
}
public BuilderDemo build() {
return new BuilderDemo(this.id, this.name, this.age);
}
public String toString() {
return "BuilderDemo.BuilderDemoBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
}
}
}Pored navedenih, Lombok pruža i anotaciju za sinhronizaciju @Synchronized, anotaciju za automatsko bacanje izuzetaka @SneakyThrows, nepromenljiv objekat @Value, anotaciju koja automatski generiše hashCode i equals @EqualsAndHashCode itd. Možete ih isprobati i pogledati dekompilovani bajtkod, kako biste iskusili kako Lombok radi.
Tok obrade u Lombok-u
Jedna slika vredi hiljadu reči — evo slike.

- javac analizira izvorni kod i generiše apstraktno sintaksno stablo (AST)
- tokom kompilacije javac poziva Lombok program koji implementira JSR 269
- Lombok obraduje AST, pronalazi sintaksno stablo (AST) klase koja sadrži Lombok anotacije, izmenjuje to stablo i dodaje odgovarajuće čvorove stabla koje definišu Lombok anotacije (tzv. kod)
- javac na osnovu izmenjenog apstraktnog sintaksnog stabla generiše fajl bajtkoda
Iako je Lombok prijatan za korišćenje, tim mora interno da se dogovori: ili ga svi koriste, ili niko — u suprotnom, kada neki članovi koriste Lombok, a neki ne, nastaje haos koji bitno utiče na ukupni stil koda. Pored toga, ako neko iz tima još uvek koristi Eclipse, i on mora instalirati dodatak za Lombok, jer u suprotnom projekt koji koristi Lombok anotacije ne može da se kompajlira.
Ako neka klasa koristi Lombok anotacije, preko strukture klase mogu se videti odgovarajući metodi, na primer toString i builder u donjoj slici.

Kada otvorite .class fajl u direktorijumu target, videćete dekompilovani fajl bajtkoda koji je Lombok generisao, što takođe potvrđuje da Lombok unapređuje Java kod u fazi kompilacije.

Kratak rezime
Lombok je popularna Java biblioteka koja ima za cilj da pojednostavi Java kod pomoću anotacija i smanji pisanje šablonskog koda. Automatskim generisanjem uobiačajenog Java koda tokom kompilacije (kao što su getters, setters, constructors, toString, equals, hashCode), izvorni kod postaje znatno pregledniji.
Skoro svaki ozbiljan projekat već koristi Lombok, jer zaista povećava efikasnost razvoja, smanjuje količinu koda i poboljšava čitljivost. Ipak, ako u timu ima nekoga ko ne zna da koristi Lombok, treba razmisliti o tome da li uvesti Lombok, jer takav član neće moći da čita kod koji Lombok generiše, što će uticati na efikasnost celog tima.
Još jedan dobar način jeste da mu prosledite ovaj tekst, da ga dobro prouči — haha.
