Информационный портал по безопасности » Программирование » Веб-разработка » Реактивное программирование со Spring Boot 2. Часть 2

 

Реактивное программирование со Spring Boot 2. Часть 2

Автор: admin от 17-11-2017, 12:10, посмотрело: 678

Реактивное программирование со Spring Boot 2. Часть 2

В первой части мы узнали, что такое реактивность и как с ней работать на базовом уровне. Если вы хотите продолжить изучение реактивного программирования с новым фреймворком от Spring, то добро пожаловать!

18 декабря. Он нам сильно облегчит работу и сэкономит много времени.
  • Gradle.Будет использован в качестве системы сборки.

  • Java 8. Да, это не последняя версия на данный момент, но для наших целей вполне подойдет.

  • Lombok. Поможет сократить код.



  • Все настройки и финальный проект можно посмотреть на github.



    Приступим



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



    	compile('org.springframework.boot:spring-boot-starter-data-mongodb-reactive')
    	compile('org.springframework.boot:spring-boot-starter-webflux')
    	compileOnly('org.projectlombok:lombok')
    	testCompile('org.springframework.boot:spring-boot-starter-test')
    	testCompile(‘io.projectreactor:reactor-test')




    Итак, у нас все готово. Для начала создадим пользователя нашего приложения:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Document
    public class User {
    
        @Id
        private String id;
        private String firstName;
        private String lastName;
    }


    Напоминаю, что для успешной компиляции и работы в IntelliJ Idea необходимо выставить галочку в пункте enable annotation processing либо написать самостоятельно все сеттеры, гетеры и конструкторы.



    Далее создадим репозиторий с нашими пользователями:



    import org.faoxis.habrreactivemongo.domain.User;
    import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
    
    public interface UserRepository extends ReactiveMongoRepository<User, String> {
    }


    Здесь следует заметить, что мы наследуемся от особого интерфейса для работы в реактивном режиме. Если заглянуть в интерфейс ReactiveMongoRepository, то можно увидеть, что нам возвращаются объекты, обернутые в уже знакомые нам классы Mongo и Flux. Это значит, что при каком-либо обращении в БД, мы не получаем сразу же результат. Вместо этого мы получаем поток данных, из которого можно получить данные по мере готовности.



    На данный момент многослойная архитектура является наиболее распространенным решением при работе с микросервисной архитектурой. Это действительно очень удобно. Давайте создадим сервисный слой. Для этого сделаем соответствующий интерфейс:



    public interface UserService {
    
        Flux<User> get();
        Mono<User> save(User user);
    }
    


    Наш сервис достаточно прост, поэтому сразу создаем несколько полезных методов. И тут же реализуем их:



    @Service
    @AllArgsConstructor
    public class UserServiceImpl implements UserService {
    
        private UserRepository userRepository;
    
        @Override
        public Flux<User> get() {
            return userRepository.findAll();
        }
    
        @Override
        public Mono<User> save(User user) {
            return userRepository.save(user);
        }
    }


    Здесь стоит заметить, что внедрение зависимости UserRepository происходит через конструктор с помощью аннотации AllArgsConstructor. На всякий случай напомню, что с некоторой версии Spring 4 можно осуществлять автоматическое внедрение зависимостей через конструктор без аннотации Autowire.



    И, наконец, сделаем контроллер:



    import lombok.AllArgsConstructor;
    import org.faoxis.habrreactivemongo.domain.User;
    import org.faoxis.habrreactivemongo.service.UserService;
    import org.springframework.web.bind.annotation.*;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    @RestController
    @RequestMapping("/users")
    @AllArgsConstructor
    public class UserController {
    
        private UserService userService;
    
        @PostMapping
        public Mono<User> post(@RequestBody User user) {
            return userService.save(user);
        }
    
        @GetMapping
        public Flux<User> get() {
            return userService.get();
        }
    }


    Запустим наше приложение. Все должно работать. Теперь сделаем POST запрос на localhost:8080/users со следующим содержимым:



    {
    	"firstName": "Peter",
    	"lastName": "Griffin"
    }


    В ответ мы получим такой же объект, но с присвоенным ему id:

    {
        "id": "5a0bf0fdc48fd53478638c9e",
        "firstName": "Peter",
        "lastName": "Griffin"
    }


    Отлично! Давайте сохраним еще пару пользователей и попробуем посмотреть, что у нас уже есть в БД. У меня такой результат GET запроса на localhost:8080/users:



    [
        {
            "id": "5a0bf0fdc48fd53478638c9e",
            "firstName": "Peter",
            "lastName": "Griffin"
        },
        {
            "id": "5a0bf192c48fd53478638c9f",
            "firstName": "Lois",
            "lastName": "Griffin"
        },
        {
            "id": "5a0bf19ac48fd53478638ca0",
            "firstName": "Mag",
            "lastName": "Griffin"
        }
    ]


    Отлично! У нас есть целый сервис, который работает в асинхронном режиме! Но есть еще кое-что, о чем следует помнить. Работа с асинхронным репозиториями может очень сильно поменять вид сервиса, который получает данные из него.



    Чтобы продемонстрировать это, создадим еще один обработчик URL метода в нашем контроллере:



        @GetMapping("{lastName}")
        public Flux<User> getByLastName(@PathVariable(name = "lastName") String lastName) {
            return userService.getByLastName(lastName);
        }


    Здесь все просто. Мы получаем фамилию пользователя и выводим всех людей с такой фамилией.



    Конечно, такую логику можно возложить на базу данных, но здесь я хотел бы обратить внимание, как это будет выглядит в сервисе:



        @Override
        public Flux<User> getByLastName(final String lastName) {
            return userRepository
                    .findAll()
                    .filter(user  user.getLastName().equals(lastName));
        }


    Обратите внимание, что есть разница в работе с данными и потоками данных. Обычно мы осуществляем какие-то действия над данными непосредственно. Здесь ситуация другая. Мы говорим, что следует сделать в потоке данных.



    Попробуем сделать GET запрос по URL localhost:8080/users/Griffin. У меня такой результат:



    [
        {
            "id": "5a0bf0fdc48fd53478638c9e",
            "firstName": "Peter",
            "lastName": "Griffin"
        },
        {
            "id": "5a0bf192c48fd53478638c9f",
            "firstName": "Lois",
            "lastName": "Griffin"
        },
        {
            "id": "5a0bf19ac48fd53478638ca0",
            "firstName": "Mag",
            "lastName": "Griffin"
        }
    ]


    В этой статье мы рассмотрели, как построить асинхронный сервис с новым фреймворком WebFlux и сервером Netty (он идет из коробки по умолчанию). Также мы убедились, как легко этого достичь со Spring Boot 2. Если у вас на проекте микросервисная архитектура, то скорее всего вы легко сможете при желании перевести свои приложения на WebFlux с выходом Spring Boot 2(если, конечно, в этом есть потребность).



    P.S. Есть несколько идей по продолжению. Если вам интересен материал, и вы не против моей подачи, то в следующих частях мы можем рассмотреть, например, асинхронное взаимодействие между приложениями. Спасибо за внимание!


    Источник: Хабрахабр

    Теги: java

    Категория: Программирование / Веб-разработка

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

    Добавление комментария

    Имя:*
    E-Mail:
    Комментарий:
    • bowtiesmilelaughingblushsmileyrelaxedsmirk
      heart_eyeskissing_heartkissing_closed_eyesflushedrelievedsatisfiedgrin
      winkstuck_out_tongue_winking_eyestuck_out_tongue_closed_eyesgrinningkissingstuck_out_tonguesleeping
      worriedfrowninganguishedopen_mouthgrimacingconfusedhushed
      expressionlessunamusedsweat_smilesweatdisappointed_relievedwearypensive
      disappointedconfoundedfearfulcold_sweatperseverecrysob
      joyastonishedscreamtired_faceangryragetriumph
      sleepyyummasksunglassesdizzy_faceimpsmiling_imp
      neutral_faceno_mouthinnocent