Pull to refresh

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

Level of difficultyMedium
Reading time2 min
Views8.1K

Довольно популярный вопрос на собеседовании - Можно ли внедрить 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 при поднятии контекста.

Tags:
Hubs:
Total votes 2: ↑2 and ↓0+2
Comments8

Articles