Encodera

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

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

Оглавление

Бины и внедрение

CDI (Contexts and Dependency Injection) — спецификация, которая создаёт объекты приложения и связывает их друг с другом. Объект, которым управляет CDI, называется бином. Вместо new PostStore() класс просто объявляет, что ему нужно хранилище, а контейнер CDI сам создаёт его и внедряет (inject) в поле. Так все части приложения получают один и тот же объект, а код не зависит от того, как этот объект собран.

Какой класс становится бином

В Jakarta EE бином становится класс с аннотацией области видимости: @ApplicationScoped, @RequestScoped, @SessionScoped, @Dependent. Хранилище из прошлой главы уже бин:

// src/main/java/com/example/blog/PostStore.java (фрагмент)
@ApplicationScoped
public class PostStore {
    // ...
}

Файл beans.xml для этого не нужен: сервер сам находит классы с такими аннотациями. Ресурсы Jakarta REST, сервлеты и фильтры тоже управляются контейнером, поэтому в них работает внедрение.

Внедрение в поле

Ресурс получает хранилище аннотацией @Inject:

// src/main/java/com/example/blog/PostResource.java (фрагмент)
@Inject
PostStore store;

То же самое работает в сервлете. Создайте src/main/java/com/example/blog/StatsServlet.java:

// src/main/java/com/example/blog/StatsServlet.java
package com.example.blog;
 
import jakarta.inject.Inject;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
 
@WebServlet("/stats")
public class StatsServlet extends HttpServlet {
 
    @Inject
    PostStore store;
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        response.setContentType("text/plain; charset=UTF-8");
        response.getWriter().println("Записей в блоге: " + store.findAll().size());
    }
}

Сервлет и REST-ресурс получили один и тот же объект хранилища. Это легко проверить: добавим запись через API и посмотрим счётчик.

curl http://localhost:8080/blog/stats
curl -H "Content-Type: application/json" -d '{"title":"CDI","body":"Бины и внедрение."}' http://localhost:8080/blog/api/posts
curl http://localhost:8080/blog/stats
Записей в блоге: 2
{"body":"Бины и внедрение.","createdAt":"2026-10-04","id":3,"title":"CDI"}
Записей в блоге: 3

Внедрение через конструктор

Бины могут зависеть друг от друга. Сервис публикации получает хранилище через конструктор:

// src/main/java/com/example/blog/PostService.java
package com.example.blog;
 
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
 
@ApplicationScoped
public class PostService {
 
    private PostStore store;
 
    protected PostService() {
        // нужен контейнеру для создания прокси
    }
 
    @Inject
    public PostService(PostStore store) {
        this.store = store;
    }
 
    public Post publish(NewPost data) {
        return store.add(data);
    }
}

Для бинов @ApplicationScoped контейнер создаёт прокси — объект-посредник, который передаёт вызовы настоящему бину. Прокси требует конструктор без параметров, поэтому рядом с @Inject-конструктором нужен protected PostService(). Без него приложение не запустится с ошибкой WELD-001410: The injection point has non-proxyable dependencies. В ресурсе замените store.add(data) на вызов сервиса:

// src/main/java/com/example/blog/PostResource.java (фрагмент)
@Inject
PostService service;
 
// в методе create:
Post post = service.publish(data);

Способы внедрения

СпособПримерКогда удобен
Поле@Inject PostStore store;Ресурсы, сервлеты, короткий код
Конструктор@Inject PostService(PostStore store)Зависимости видны сразу, удобно тестировать
Метод@Inject void setStore(PostStore store)Редко

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

Забыть аннотацию области видимости. Если убрать @ApplicationScoped у PostStore, класс перестаёт быть бином, и приложение не запустится. В логе сервера появится:

WELD-001408: Unsatisfied dependencies for type PostStore with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject com.example.blog.PostResource.store

«Unsatisfied dependencies» значит, что для поля с @Inject контейнер не нашёл ни одного подходящего бина.

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

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