Reducing Boilerplate Code (Lombok) ================================================================================ .. only:: html .. contents:: Index :depth: 3 :local: .. _LombokAbout: Lombok -------------------------------------------------------------------------------- `Lombok `_ is a library used for reducing the boilerplate code from Java source code. Boilerplate code is a typical source code that cannot be omitted by language specification. Basically Boilerplate code does not have a specific logic hence it becomes redundant code in implementation. Following are the typical Boilerplate source code in Java language. * getter / setter methods for accessing the member variables * \ ``equals``\ /\ ``hashCode``\ methods * \ ``toString``\ methods * Constructors * Closing process of resources (input and output stream, etc.) * Generation of logger instance In Lombok, boilerplate code gets generated at the time of compilation thereby providing a mechanism to remove redundant code from the source code developed by the developer. .. tip:: For removing the Boilerplate code, the language specification for closing the resources (input and output stream, etc.) are improved by newly added [try-with-resources] statement in Java SE7. Java language itself is improving in every version-up for removing the redundant code. Lambda support in Java SE8 is also called as typical language specification improvement. | .. _LombokEffect: Efficiency of Lombok -------------------------------------------------------------------------------- Below, JavaBean source code is created using Lombok. .. code-block:: java package com.example.domain.model; @lombok.Data public class User { private String userId; private String password; } In Lombok, required methods for JavaBean gets created by only assigning \ ``@lombok.Data``\ annotation at class level. .. figure:: ./images_Lombok/LombokGeneratedClassStructure.png :alt: Generated Class Structure by Lombok :align: center **Class structure generated by Lombok** By only assigning \ ``@Data``\ annotation of Lombok, it is possible to obtain the same effect as classes generated by (the source code output using the auto-generation function of Eclipse) which has about 60 lines of code given below instead of 10 lines of code. .. code-block:: java package com.example.domain.model; public class User { private String userId; private String password; public User() { } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((password == null) ? 0 : password.hashCode()); result = prime * result + ((userId == null) ? 0 : userId.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (password == null) { if (other.password != null) return false; } else if (!password.equals(other.password)) return false; if (userId == null) { if (other.userId != null) return false; } else if (!userId.equals(other.userId)) return false; return true; } @Override public String toString() { return "User [userId=" + userId + ", password=" + password + "]"; } } | .. _LombokSetup: Lombok setup -------------------------------------------------------------------------------- .. _LombokSetupAddDependency: Inclusion of dependent library ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order to use a class that is offered by Lombok, add Lombok as dependency library. .. code-block:: xml org.projectlombok lombok provided .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr.No - Description * - | (1) - Add Lombok dependent library in the Lombok targeted project's :file:`pom.xml` . * - | (2) - Since Lombok library is not required at the time of application execution, appropriate scope is \ ``provided``\ . .. note:: In the above configuration example, it is prerequisite that the version of dependent library is to be managed by the parent project. Therefore, \ ````\ element is not specified. | .. _LombokSetupIDE: IDE Integration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you want to use Lombok on IDE, it is necessary to install the Lombok to IDE in order to work with compile (build) function provided by the IDE. In this guideline, introduced how to install Lombok to Spring Tool Suite (Later referred as the "STS"). However installation methods are different depending on IDE henceforth refer `this page `_ in case you want to use IDE besides STS. | .. _LombokSetupIDEDownload: Download Lombok """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Download the jar file of Lombok. The jar file of Lombok, * `Download page of Lombok `_ * Local repository of Maven can retrieve from (Normally, :file:`${HOME}/.m2/repository/org/projectlombok/lombok//lombok-.jar`) | .. _LombokSetupIDEInstall: Lombok Installation """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Launch the installer by running (double-click) the downloaded Lombok jar file. .. figure:: ./images_Lombok/LombokInstaller.png :alt: Lombok Installer :align: center **Lombok Installer** After selecting the targeted STS, follow installation process by pressing the "Install / Update" button. The installer will automatically detect the location of supported IDE However if cannot auto detected, it is necessary to specify the IDE by pressing the "Specify location ..." .. figure:: ./images_Lombok/LombokInstallSuccessful.png :alt: Lombok Install Successful :align: center **Successful installation dialog** Once Lombok installation completes, it is possible to start development using Lombok on STS after booting (Or re-booting) STS. | .. _LombokHowToUse: How to use Lombok -------------------------------------------------------------------------------- From here, the specific use of Lombok is described. If Lombok is used first time, it is recommendation to watch Lombok [`Demo Video `_]. The length of Demo Video is less than 4 minutes and described the most basic usage. | .. _LombokHowToUseAnnotation: Lombok Annotations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Typical annotations provided by Lombok are introduced below. For Detailed usage of each annotation as well as annotation that have not been explained in this guideline, please refer, * `Lombok features `_ | .. tabularcolumns:: |p{0.10\linewidth}|p{0.30\linewidth}|p{0.60\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 30 60 * - Sr.No - Annotation - Description * - 1. - `@lombok.Getter `_ - Annotation for generating getter method. If annotation is specified at class level, it is possible to generate getter methods for all respective fields. * - 2. - `@lombok.Setter `_ - Annotation for generating setter method. If annotation is specified at class level, it is possible to generate setter methods for all non-final respective fields. * - 3. - `@lombok.ToString `_ - Annotation for generating \ ``toString``\ method. * - 4. - `@lombok.EqualsAndHashCode `_ - Annotation for generating \ ``equals``\ and \ ``hashCode``\ method. * - 5. - `@lombok.RequiredArgsConstructor `_ - Annotation for generating constructor with the required arguments for those fields (final field etc) where initialization required. If all fields are optional fields, the default constructor (without argument constructor) is generated. * - 6. - `@lombok.AllArgsConstructor `_ - Annotation for generating constructor having all fields in arguments. * - 7. - `@lombok.NoArgsConstructor `_ - Annotation for generating default constructor. * - 8. - `@lombok.Data `_ - Short cut annotation for \ ``@Getter``\ , \ ``@Setter``\ , \ ``@ToString``\ , \ ``@EqualsAndHashCode``\ , \ ``@RequiredArgsConstructor``\ . If \ ``@Data``\ annotation is specified, it has the same meaning as specification of all above five annotations. * - 9. - `@lombok.extern.slf4j.Slf4j `_ - Annotation for generating logger instance of SLF4J. | .. _LombokHowToUseJavaBean: Creation of JavaBean ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If an application is built the way that this guideline is recommended, * Form class * Resource class(REST API configured) * Entity class * DTO class it is necessary to create a JavaBean for above classes. Below is the example of creating a JavaBean. .. code-block:: java package com.example.domain.model; import lombok.Data; @Data // (1) public class User { private String userId; private String password; } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr.No - Description * - | (1) - By assigning a \ ``@Data``\ annotation at class level, * getter/setter method * \ ``equals``\ / \ ``hashCode``\ method * \ ``toString``\ method * default constructor are created. | .. _LombokHowToUseJavaBeanExcludeToString: How to exclude specific field from toString """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" At the time of converting the state of object into a string, * Field that holds an object of cross reference relationship * Field that holds sensitive information such as personal information and password etc are required to exclude from the scope of string conversion. If these fields are not excluded from the string conversion, * The \ ``StackOverflowError``\ and \ ``OutOfMemoryError``\ occurs due to circular reference * There is a possibility of leakage the personal information due to use of converted string Henceforth it is necessary to take an attention. .. warning:: If \ ``@Data``\ or \ ``@ToString``\ annotation is used at the Entity class of JPA, it is necessary to keep in mind that it tends to the circular reference. | How to exclude a specific field from string conversions are indicated below. .. code-block:: java package com.example.domain.model; import lombok.Data; import lombok.ToString; @Data @ToString(exclude = "password") // (1) public class User { private String userId; private String password; } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr.No - Description * - | (1) - Specify the \ ``@ToString``\ annotation to the class level and list the name of fields that you want to exclude into \ ``exclude``\ attribute. If you call \ ``toString``\ method of the class that is generated from the source code of above example, * \ ``User(userId=U00001)``\ is converted to the string. | .. _LombokHowToUseJavaBeanExcludeEqualsAndHashCode: How to exclude specific field from equals and hashCode """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" If \ ``equals``\ method and \ ``hashCode``\ method generated using Lombok annotation, field that holds an object of cross reference relationship needs to be removed. If methods are generated without excluding these fields, the \ ``StackOverflowError``\ and \ ``OutOfMemoryError``\ occurs due to circular reference henceforth it is necessary to take an attention. .. warning:: If \ ``@Data``\ annotation, \ ``@Value``\ annotation, \ ``@EqualsAndHash``\ annotation is used at the Entity class of JPA, it is necessary to keep in mind that it tends to the circular reference. | How to exclude a specific field is indicated below. .. code-block:: java package com.example.domain.model; import java.util.List; import lombok.Data; @Data public class Order { private String orderId; private List orderLines; } .. code-block:: java package com.example.domain.model; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @Data @ToString(exclude = "order") @EqualsAndHashCode(exclude = "order") // (1) public class OrderLine { private Order order; private String itemCode; private int quantity; } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr.No - Description * - | (1) - Specify the \ ``@EqualsAndHashCode``\ annotation to the class level and list the name of fields that you want to exclude into \ ``exclude``\ attribute. .. tip:: Instead of specifying the field to be excluded, it is also possible to specify to use only specific fields. .. code-block:: java @Data @ToString(exclude = "order") @EqualsAndHashCode(of = "itemCode") // (2) public class OrderLine { private final Order order; private final String itemCode; private final int quantity; } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr.No - Description * - | (2) - In case of using only specific fields, specify the name of fields that you want to include into \ ``of``\ attribute of \ ``@EqualsAndHashCode``\ annotation. In the above example, \ ``equals``\ method and \ ``hashCode``\ method get generated by referring only \ ``itemCode``\ field. | .. _LombokHowToUseJavaBeanConstructor: How to generate constructor for field initialization """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" If you want to create an instance of JavaBean from the implementation code of application, it is more convenient that having constructor where initial value of the field can be passed as an argument, to eliminate the redundant code. If you create an instance using the default constructor, the code would be like below. .. code-block:: java public void login(String userId, String password) { User user = new User(); user.setUserId(userId); user.setPassword(password); // ... } | How to generate a constructor that specifies the initial values of the field are indicated below. .. code-block:: java package com.example.domain.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @AllArgsConstructor // (1) @NoArgsConstructor // (2) @ToString(exclude = "password") public class User { private String userId; private String password; } .. code-block:: java public void login(String userId, String password) { User user = new User(userId, password); // (3) // ... } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr.No - Description * - | (1) - Specify the \ ``@AllArgsConstructor``\ annotation to the class level, to generate a constructor that takes the initial values of all fields as an argument. * - | (2) - Specify the \ ``@NoArgsConstructor``\ annotation to the class level, to generate a default constructor. It is necessary to generate a default constructor if going to be used as JavaBean. * - | (3) - Create an instance of JavaBean by calling the constructor that having initial values of the field. If default constructor is used, instance can be generated in one step instead of 3 steps. .. tip:: If you want to create above \ ``User``\ class as Immutable class instead of JavaBean, it is preferable to use \ ``@lombok.Value``\ annotation. Please refer `Lombok reference `_ for \ ``@Value``\ annotation. | .. _LombokHowToUseLogger: Creating logger instance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If it is necessary to generate a logger instance for output a debug log and application log, it is preferable to use annotations for creating a logger instance. If you want to create a logger instance without using Lombok annotations, below could be code. .. code-block:: java package com.example.domain.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @Service public class AuthenticationService { private static final Logger log = LoggerFactory.getLogger(AuthenticationService.class); public void login(String userId, String password) { log.info("{} had tried login.", userId); // ... } } | How to create a logger instance using Lombok annotation is described below. .. code-block:: java package com.example.domain.service; import org.springframework.stereotype.Service; import lombok.extern.slf4j.Slf4j; @Slf4j // (1) @Service public class AuthenticationService { public void login(String userId, String password) { log.info("{} had tried login.", userId); // (2) // ... } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - Sr.No - Description * - | (1) - Generate SLF4J logger instance by specifying \ ``@Slf4j``\ annotation at the class level. In this guideline, it is prerequisite to output a log using \ ``org.slf4j.Logger``\ of SLF4J. By default, FQCN class that granted the annotation (In above example \ ``com.example.domain.service.LoginService``\ ) is used as the logger name and, logger instance corresponding to the logger name is set to field called \ ``log``\ . * - | (2) - Output the log by calling the method of SLF4J logger instance that has been generated by Lombok. In above example, * \ ``11:29:45.838 [main] INFO c.e.d.service.AuthenticationService - U00001 had tried login.``\ will be output. .. tip:: If you want to change the logger name that is used by default, specify optional logger name in the \ ``topic``\ attribute of \ ``@Slf4j``\ annotation. .. raw:: latex \newpage