Написание более читаемого кода с помощью Project Lombok -9




В течение многих лет мы написали на Java много шаблонного кода, такого как методы getter, setter, equals, hashCode  и т.д.. В некоторых случаях это вызывает проблемы с точки зрения чистого и читаемого кода.

В таких ситуациях Project Lombok спасает нас. Кроме того, с помощью Lombok вы сможете уделять больше времени бизнес-логике.

Фото Фачи Марина на Unsplash
Фото Фачи Марина на Unsplash

В этом руководстве я расскажу вам о Project Lombok и приведу пример использования Spring Boot.

  • Здесь используются проекты Spring Boot и Spring Data для разработки REST API.

  • Maven используется для автоматизации процесса сборки.

  • Используется типичный подход к разработке на основе предметной области (domain-driven), разделяющий классы модели, репозитория, сервиса и контроллера.

Вы можете скачать исходный код проекта по этой ссылке на GitHub

Что такое Project Lombok?

Я скопировал описание проекта Ломбок отсюда (Официальный сайт). Я не мог бы придумать лучшего предложения, чтобы объяснить это :). «Project Lombok - это java-библиотека, которая автоматически подключается к вашему редактору и инструментам сборки, делая вашу java более пикантной».

Фото Канвара на Slideshare
Фото Канвара на Slideshare

«Никогда больше не пишите еще один метод getter или equals, с одной аннотацией ваш класс получает полнофункциональный конструктор, автоматизируется создание переменных журналирования и многое другое».

Project Lombok использует аннотации, чтобы избежать написания шаблонного кода. В некоторых случаях всего пять строк кода могут заменить сотни строк.

Как вы видите ниже, вместо почти восьмидесяти строк кода было использовано несколько аннотаций.

Класс Book, созданный с использование Lombok

package com.medium.libraryinfosystem.model.lombok;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * @author ragcrix
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    private String writer;
    private String name;
    private String genre;
    private String year;
}

Описание используемых аннотаций согласно Lombok JavaDoc :

  • @Data: «Создает методы getter для всех полей, метод toString и реализации hashCode и equals, которые проверяют * все не transient поля. Также будет генерировать конструктор, а также методы setter для всех не final полей».

  • @AllArgsConstructor : «Создает конструктор со всеми аргументами».

  • @NoArgsConstructor : «Создает конструктор без аргументов».

  • @Slf4j : «Заставляет ломбок генерировать поле logger.»

Ниже Вы можете увидеть класс модели Book, которая также создана без использования Lombok.

Класс Book, созданный с использование стандартного кода

package com.medium.libraryinfosystem.model.classic;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Objects;

/**
 * @author ragcrix
 */
@Entity
@Table
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    private String writer;
    private String name;
    private String genre;
    private String year;

    public Book() {
    }

    public Book(Long id, String writer,
                String name, String genre, String year) {
        this.id = id;
        this.writer = writer;
        this.name = name;
        this.genre = genre;
        this.year = year;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getWriter() {
        return writer;
    }

    public void setWriter(String writer) {
        this.writer = writer;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGenre() {
        return genre;
    }

    public void setGenre(String genre) {
        this.genre = genre;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return Objects.equals(id, book.id) &&
                Objects.equals(writer, book.writer) &&
                Objects.equals(name, book.name) &&
                Objects.equals(genre, book.genre) &&
                Objects.equals(year, book.year);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, writer, name, genre, year);
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", writer='" + writer + '\'' +
                ", name='" + name + '\'' +
                ", genre='" + genre + '\'' +
                ", year='" + year + '\'' +
                '}';
    }
}

Project Lombok также обеспечивает удобство ведения журнала следующим образом. Например, вы можете использовать @Slf4jаннотацию для ведения журнала.

Аннотация @ Slf4j используется для ведения журнала

package com.medium.libraryinfosystem.service.impl;

import com.medium.libraryinfosystem.model.lombok.Book;
import com.medium.libraryinfosystem.repository.BookRepository;
import com.medium.libraryinfosystem.service.BookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author ragcrix
 */
@Slf4j
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookRepository bookRepository;

    @Override
    public Book findByName(String name) {
        log.info("inside findByName()");
        return bookRepository.findByName(name);
    }

    @Override
    public List<Book> findAll() {
        log.info("inside findAll()");
        return bookRepository.findAll();
    }

    @Override
    public Book save(Book book) {
        log.info("inside save()");
        return bookRepository.save(book);
    }

    @Override
    public void delete(Book book) {
        log.info("inside delete()");
        bookRepository.delete(book);
    }


}

Как установить Lombok

Мы можем легко интегрировать Lombok в нашу IDE (IntelliJ, Eclipse и т.д.).

В этом разделе я расскажу вам об установке Lombok для IntelliJ Idea. Вы можете перейти сюда, чтобы увидеть другие варианты установки.

  1. Во-первых, вы должны выбрать Lombok при инициализации проекта Spring Boot, как показано ниже.

Инициализация проекта
Инициализация проекта

2. После этого, если вы не добавили плагин Lombok в свою IDE раньше, IntelliJ задаст вам вопрос. Вы должны выбрать плагин Lombok и нажать кнопку ОК, чтобы установить плагин в вашу среду IDE.

Установка Lombok
Установка Lombok

Вы можете убедиться, что Lombok установлен в вашей среде IDE, как показано ниже. Как только установка Lombok будет завершена, мы готовы к работе.

Плагин Lombok установлен
Плагин Lombok установлен

Информационная система библиотеки

В этом руководстве для кодирования, сборки и тестирования REST API используются IntelliJ IDEA , Maven и Postman . Я работаю в Windows 10 с помощью PowerShell.

Все программное обеспечение написано на Java, с использованием Spring 5.2 и Spring Boot 2.2.2, база данных H2 в памяти используется для сохранения данных.

Далее я предполагаю, что у вас установлены JDK 13.0, IntelliJ, Maven и Postman.

Информационная система библиотеки предоставляет информацию о книгах и позволяет добавлять новые книги в систему или удалять книги из системы.

Фото Иво Раиньи на Unsplash
Фото Иво Раиньи на Unsplash

Моя цель здесь - показать, как использовать Lombok простым способом без перетасовки бизнес-логики.

  • Операция POST используется для добавления книг в систему.

  • Таблица book в базе данных H2 в памяти содержит данные о названии каждой книги, авторе, жанре и году.

  • Все, что хранится, получается и возвращается службой, форматируется как JSON.

Модель Book 

Эта модель имеет 5 полей: id, name, writer, genre и year.

package com.medium.libraryinfosystem.model.lombok;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * @author ragcrix
 */

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    private String writer;
    private String name;
    private String genre;
    private String year;
}

Все использованные аннотации Lombok объяснены выше.

Репозиторий Book 

Вы можете легко создать интерфейс, расширяющий CrudRepository, с помощью Spring Data Project. При этом нет необходимости реализовывать этот интерфейс :).

package com.medium.libraryinfosystem.repository;

import com.medium.libraryinfosystem.model.lombok.Book;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

/**
 * @author ragcrix
 */
public interface BookRepository extends CrudRepository<Book, Long> {
    Book findByName(String name);

    List<Book> findAll();

    Book save(Book book);

    void delete(Book book);
}

В BookRepository есть четыре метода. Это простые операции CRUD (создание, чтение, обновление, удаление) .

Сервис Book 

Сервис Book имеет четыре метода. Просто эти методы выдают список книг по некоторым критериям, сохраняют или обновляют данные о книге и удаляют книгу.

package com.medium.libraryinfosystem.service;

import com.medium.libraryinfosystem.model.lombok.Book;

import java.util.List;

/**
 * @author ragcrix
 */
public interface BookService {
    Book findByName(String name);

    List<Book> findAll();

    Book save(Book book);

    void delete(Book book);
}

Вы можете увидеть реализацию этого интерфейса ниже.

package com.medium.libraryinfosystem.service.impl;

import com.medium.libraryinfosystem.model.lombok.Book;
import com.medium.libraryinfosystem.repository.BookRepository;
import com.medium.libraryinfosystem.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author ragcrix
 */
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookRepository bookRepository;

    @Override
    public Book findByName(String name) {
        return bookRepository.findByName(name);
    }

    @Override
    public List<Book> findAll() {
        return bookRepository.findAll();
    }

    @Override
    public Book save(Book book) {
        return bookRepository.save(book);
    }

    @Override
    public void delete(Book book) {
        bookRepository.delete(book);
    }
}

Book контроллер

BookController.java - это класс REST контроллера, который имеет два метода с аннотацией @GetMapping, один с  @PostMappingи один с @DeleteMapping. 

package com.medium.libraryinfosystem.controller;

import com.medium.libraryinfosystem.dto.BookDTO;
import com.medium.libraryinfosystem.model.lombok.Book;
import com.medium.libraryinfosystem.service.BookService;
import com.medium.libraryinfosystem.util.ObjectMapperUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @ragcrix
 */
@RestController
@RequestMapping("/books")
public class BookController {

    @Autowired
    private BookService bookService;

    @GetMapping(value = "/")
    public List<BookDTO> getAllBooks() {
        return ObjectMapperUtils.mapAll(bookService.findAll(), BookDTO.class);
    }


    @GetMapping(value = "/byName/{bookName}")
    public BookDTO getBookByName(@PathVariable("bookName") String bookName) {
        return ObjectMapperUtils.map(bookService.findByName(bookName), BookDTO.class);
    }

    @PostMapping(value = "/save")
    public ResponseEntity<?> saveOrUpdateBook(@RequestBody BookDTO bookDTO) {
        bookService.save(ObjectMapperUtils.map(bookDTO, Book.class));
        return new ResponseEntity("Book added successfully", HttpStatus.OK);
    }

    @DeleteMapping(value = "/delete/{bookName}")
    public ResponseEntity<?> deleteBookByName(@PathVariable String bookName) {
        bookService.delete(bookService.findByName(bookName));
        return new ResponseEntity("Book deleted successfully", HttpStatus.OK);
    }


}

Конфигурация базы данных

Чтобы подключиться к базе данных H2, отредактируйте настройки в файле «application.properties», который расположен в директори resources. Достаточно следующей конфигурации.

# Server
server.port=9090

# Enabling H2 Console
spring.h2.console.enabled=true

# Datasource
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

После запуска информационной системы библиотеки вы можете легко получить доступ к базе данных H2 по этой ссылке .

Примечание. Каждый раз, когда запускается этот проект, будут выполняться скрипт в файле data.sql, который находится в директории resources. Конечно, для этого используется Spring Data. 

insert into book values(1, 'SCI-FI', 'Frankenstein', 'Mary Shelley', '1818'); 
insert into book values(2, 'Fantastic', 'Harry Potter', 'J. K. Rowling', '1997');

Запуск службы и тестирование с помощью Postman

Вы можете легко запустить этот проект для тестирования службы REST.

Если вы хотите добавить книгу в информационную систему библиотеки, вы можете использовать файл data.json в корне проекта.

Запустите проект в IntelliJ
Запустите проект в IntelliJ

После запуска проекта вы можете протестировать службу REST. Для этого откройте Postman, введите URL-адрес и выберите метод GET, как показано ниже.

Список книг
Список книг

Снова введите ссылку с названием книги в базу данных.

Франкенштейн
Франкенштейн

Вы можете добавить книгу в систему с помощью метода POST в Postman. Сначала вы должны ввести пару ключ-значение заголовка Content-Type:application/json . Во-вторых, во вкладке body вы должны ввести информацию о книге, которую вы хотите сохранить в базе данных в формате JSON.

Добавление книги
Добавление книги

Если вы хотите удалить книгу, вам следует выбрать метод DELETE в Postman и добавить название книги, которую вы хотите удалить, в конец ссылки.

Удаление книги
Удаление книги

Ресурсы

Примечание. В этом руководстве рассматривается сочетание Spring Data, H2 в памяти и REST API. В проекте нет проверки для полей объекта.

Спасибо за прочтение! Ваши мысли очень ценны для меня. Пожалуйста, поделитесь. 

Теги:




Комментарии (6):

  1. Hett
    /#22193854 / +3

    Рассказывая про lombok приплести к этому делу postman — это сильно.
    В общем смешались в кучу кони, люди.
    По сути большая часть статьи это как сделать rest api на spring boot.

    • val6852
      /#22194876

      Ствтья про использование lombok в конкретном проекте рзработки rest api на spring boot.
      Тестирование rest api в postman — обычное дело. Это не описание postman.

  2. olegnyr
    /#22194074 / +1

    Не советую ставить аннотацию Data над сущьностью, как правильно сказано она формирует метод toString и при связях, можно вытащить половину бд, тоже самое hashcode и equals, да и тема bookDto не раскрыта а там можно заюзать value

    • DarkSavant
      /#22194208

      Кстати да, опасная штука при связях.

  3. iFebrity
    /#22194966 / +1

    Cтавим Data на entity, случайно пишем зависимости и привет stackOverFlowError!
    На мой взгляд необходимо убрать информацию про crudRepository и примеры с постманом, дабы не путать джунов, которые пришли посмотреть что такое ломбок. Если писать уж статью про ломбок, то уж точно не ограничиваться Getter Setter. Почему нет информации про Singular? Builder? @SuperBuilder? @NonNull? А наш любимый RequiredArgsConstructor? Ну и вишенка на торте @SneakyThrows

  4. sshikov
    /#22195474

    Мой опыт подсказывает, что в тех проектах, где имеет смысл ломбок, зачастую можно взять котлин, груви, скалу — и получить все тоже самое плюс ряд дополнительных плюшек.