Как внедрить Prototype в Singleton в Spring с помощью параметра ProxyMode

от автора

Довольно популярный вопрос на собеседовании — Можно ли внедрить Prototyte в Singleton и как это сделать?

Если просто добавить к определению бина аннотацию @Scope(SCOPE_PROTOTYPE), и использовать этот бин в синглтоне через аннотацию @Autowired – будет создан только один объект. Потому что синглтон создается только однажды, и обращение к прототипу случится тоже однажды при его создании (при внедрении зависимости).

На самом деле вариантов довольно много:

  • с помощью @Lookup

  • context.getBean(Prototype.class)

  • API -интерфейс javax.inject

  • Интерфейс ObjectFactory 

Но эти варианты мягко скажем «не очень».

В некоторых статьях пишут что можно сделать это с помощью параметра ProxyMode в аннотации @Scope, но я не смог найти реальных примеров внедрения, so напишу свой.

Для начала создадим наш будущий Prototype.

  • Каждый раз когда создается новый экземпляр будем выводить в консоль «new bank created».

  • Добавим @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,proxyMode = TARGET_CLASS)

    Выражение proxyMode=TARGET_CLASS oзначает, что класс создается с помощью наследования.

    Можно сделать proxyMode = INTERFACES, тогда proxy создастся c использованием интерфейсов, наш класс не реализует никаких интерфейсов(интерфейсы — маркеры не подойдут), поэтому используем TARGET_CLASS.

import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import java.util.UUID;  import static org.springframework.context.annotation.ScopedProxyMode.TARGET_CLASS;  /**  * Created in SoftMediaLab  * by Mark Ponomarev  * Date : 15.09.2023  **/  @Component @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode=TARGET_CLASS) public class Bank {      private final String bankName;      public Bank() {         this.bankName = UUID.randomUUID().toString();         System.out.println("new bank created");     }      public String getInput() {         return bankName;     } }

Сейчас создадим Singleton.

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;  /**  * Created in SoftMediaLab  * by Mark Ponomarev  * Date : 15.09.2023  **/  @Service() public class BankService {      @Autowired     Bank bank;      public Bank getNewBank(){         return bank;     }    }

Тут все довольно просто, Мы инжектим наш Prototype c помощью aннотации @Autowired.

Для проверки напишем дополнительный метод.

import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;    @SpringBootTest class PrimaryExampleApplicationTests {     @Autowired     BankService messageService;      @Test     public void testing(){         Bank newBank1 = messageService.getNewBank();         Bank newBank = messageService.getNewBank();         System.out.println(newBank.getInput());         System.out.println(newBank1.getInput());     } }

Можете самостоятельно проверить, что будет создано 2 экземпляра класса Bank.

Для полноты картины вы можете самостоятельно внедрить Prototype, используя динамический Proxy(proxyMode = INTERFACES). Не забудьте про то, что нужно реализовать интерфейс, иначе будет Exeption при поднятии контекста.


ссылка на оригинал статьи https://habr.com/ru/articles/761330/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *