Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Бины и контейнер

В обычной программе на Java вы сами создаёте объекты через new. В Spring эту работу берёт на себя контейнер (его ещё называют контекстом приложения). При запуске он находит ваши классы, создаёт по одному объекту каждого и хранит их. Объект, которым управляет контейнер, называется бином (bean).

Как пометить класс

Контейнер создаёт бины из классов с особыми аннотациями:

АннотацияДля чего
@ComponentЛюбой вспомогательный класс
@ServiceКласс с логикой приложения
@RepositoryКласс для работы с данными
@Controller, @RestControllerОбработчики HTTP-запросов
@ConfigurationКласс с настройками и методами @Bean

Технически все они — разновидности @Component. Разные названия помогают читать код: по аннотации сразу видно роль класса.

Первый сервис

// src/main/java/com/example/blog/PostService.java
package com.example.blog;
 
import java.util.List;
 
import org.springframework.stereotype.Service;
 
@Service
public class PostService {
 
    public List<String> titles() {
        return List.of("Привет, Spring", "Зачем нужны записи");
    }
}

Благодаря @Service контейнер при старте создаст один объект PostService. Вызывать new PostService() нигде не нужно.

Как получить бин

Другой бин получает сервис через конструктор — Spring сам передаст готовый объект:

// src/main/java/com/example/blog/TitleController.java
package com.example.blog;
 
import java.util.List;
 
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class TitleController {
 
    private final PostService postService;
 
    public TitleController(PostService postService) {
        this.postService = postService;
    }
 
    @GetMapping("/api/titles")
    public List<String> titles() {
        return postService.titles();
    }
}

Запрос curl http://localhost:8080/api/titles вернёт ["Привет, Spring","Зачем нужны записи"]. Как именно Spring подбирает аргументы конструктора, разберём в следующем уроке.

Где Spring ищет классы

Поиск (component scanning) начинается с пакета, в котором лежит класс с @SpringBootApplication, то есть com.example.blog, и охватывает все его подпакеты. Класс в com.example.blog.posts будет найден, а в com.example.other — нет.

Бин из чужого класса

На класс из библиотеки аннотацию не повесить. Для таких случаев пишут метод с @Bean в классе настроек:

// src/main/java/com/example/blog/AppConfig.java
package com.example.blog;
 
import java.time.Clock;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class AppConfig {
 
    @Bean
    public Clock clock() {
        return Clock.systemUTC();
    }
}

Spring вызовет clock() один раз и запомнит результат как бин типа Clock с именем clock. Теперь любой класс может попросить Clock в конструкторе — это пригодится, чтобы ставить дату создания статьи.

Частая ошибка

Если забыть аннотацию @Service или положить класс вне пакета com.example.blog, приложение не запустится:

***************************
APPLICATION FAILED TO START
***************************
 
Description:
 
Parameter 0 of constructor in com.example.blog.PostController required a bean of type 'com.example.blog.PostService' that could not be found.

Spring говорит: контроллеру нужен PostService, но такого бина нет. Проверьте аннотацию и пакет класса.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.