Внедрить хранилище в сервис без DI-фреймворка - PullRequest
0 голосов
/ 28 апреля 2019

У меня есть требование создать простой REST API с базовыми операциями CRUD над ресурсом, не используя Spring, а только Java. Я использую JAX-RS (реализация в Джерси) и Jetty в качестве встроенного контейнера сервлета. Я использую JPA (Hibernateреализация) и база данных H2 в памяти.Я не использую никакой DI-инфраструктуры, поэтому я делаю все DI "вручную" с помощью new ().

Ниже приведен сервис JAX-RS с конечной точкой POST.Я создал хранилище в качестве статической конечной переменной внутри службы.BookRepository - это интерфейс, а BookRepositoryImpl - это реализация этого хранилища.Интересно, это лучший подход?Если бы я делал это с помощью аннотации Spring Autowired, у меня был бы одноэлементный репозиторий, поэтому единственный способ, которым я подражал этому, - статическая конечная переменная.Когда контейнер запускается, создается ли отдельный экземпляр BookService для каждого запроса (потока)?Таким образом, несколько потоков будут иметь доступ к одной копии bookRepository?Разве это не то, что происходит с Autowired и синглтоном?

@Path("/books")
public class BookService {

private static final BookRepository bookRepository = new BookRepositoryImpl();

@POST
@Path("")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Book registerBook(Book b) {
    return bookRepository.saveBook(b);
}
}

Ответы [ 2 ]

2 голосов
/ 28 апреля 2019

Применение шаблона внедрения зависимостей без DI-контейнера - это практика, обычно называемая Pure DI .При таком подходе вы применяете те же принципы и практики объектно-ориентированного проектирования и DI.Но вместо того, чтобы подключать все с помощью DI-контейнера, а по пути запуска приложения, вы строите свои графы объектов вручную, используя ключевое слово new.

Pure DI - это распространенный и правильный подходпрактика DI - контейнеры DI полезны, но необязательные инструменты.

Это, однако, не тот подход, который вы практикуете в настоящее время.Вы не вводите свои зависимости своим потребителям.Создав BookRepositoryImpl внутри класса BookService, вы применяете анти-паттерн Control Freak с особой формой нарушения Принцип инверсии зависимостей .Это тесно связывает класс BookRepositoryImpl с классом BookService, что, вероятно, вызовет проблемы с удобством обслуживания, поскольку BookRepositoryImpl является изменчивой зависимостью .Изменчивые зависимости - это причина, по которой мы вводим абстракции и используем Dependency Injection.

Кроме того, использование статических полей только усиливает боль, поскольку это может вызвать проблемы безопасности потоков в случае BookRepositoryImpl (или одной из его зависимостей).) не ориентирован на многопотоковое исполнение.

Таким образом, вместо сильной связи BookRepositoryImpl с BookService, вы должны вставить абстракцию BookRepository в конструктор BookService.Это обеспечивает слабую связь двух компонентов и дает вам все преимущества, которые дает слабая связь:

@Path("/books")
public class BookService {

    private final BookRepository bookRepository;

    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    @POST
    @Path("")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Book registerBook(Book b) {
        return bookRepository.saveBook(b);
    }
}

Это, однако, означает, что вам следует переопределить способ, которым ваша веб-платформа REST API создает эту службу.Такая структура обычно может создавать экземпляры только от вашего имени, если у них есть конструктор по умолчанию.Я должен признать, что у меня нет опыта работы с JAX-RS, но большинство сред позволяют переопределять создание своих корневых классов.Например, с помощью инфраструктуры ASP.NET MVC вы можете реализовать пользовательский IControllerFactory и заменить реализацию платформы по умолчанию.Внутри вашей собственной фабрики вы создадите полное дерево вручную с простой старой Java:

public IController Create(Type controllerType)
{
    if (controllerType == typeof(HomeController))
       return new HomeController(new PersonsRepositoryImpl(this.connectionString));
    if (controllerType == typeof(BooksController))
       return new BooksController(new BookRepositoryImpl(this.connectionString));
    if (...)
    throw new InvalidOperationException(controllerType.FullName + " unknown.");
}

Я ожидаю, что JAX-RS содержит аналогичную модель расширения, которая позволяет вам практиковать Pure DI.

0 голосов
/ 11 мая 2019

Спасибо Стивену за ответ. В заключение, это конфигурация JAX-RS, где я делаю DI:

public class AppConfig extends ResourceConfig {

    public AppConfig() {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-unit");

        BookRepository bookRepository = new BookRepositoryImpl(emf);

        BookService bookService = new BookService(bookRepository);

        register(bookService);

  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...