Pull to refresh

Comments 8

В Вашем реализации, в синглтон внедряется не реальный объект класса, а прокси. И при каждом вызове его методов, прокси решает, создавать новый экземпляр класса для вызова или нет. То есть, в Вашем случае, при каждом вызове метода getInput(), прокси решил создавать новый объект. Вызовите метод getInput() на одном и том же прокси 100 раз, думаю, там будет создано гораздо больше экземпляров, чем 1.

Простой пример, получен один бин из Вашей реализации, и на одном и том же бине вызван 500 раз один и тот же метод, вывод по спойлером под кодом.

        final Bank bank = ctx.getBean(Bank.class);
        IntStream.range(0, 500)
                .forEach(i -> System.out.println(bank.getInput()));
Вывод кода выше

new bank created
356f5044-9117-4d2a-b67a-e7d1690cc84b
new bank created
53950529-5dfb-4c85-b95f-0e84f25b2108
new bank created
bcd131c0-09d8-49e7-ad2b-4331aac0cabf
new bank created
f8fa5bbe-ee93-42cb-ab16-e153a0a013ed
new bank created
5707d254-78e9-4bf5-85d6-4b842da36403
new bank created
648d3719-fb8b-4c39-b3af-ca1e9736a05d
new bank created
102b8341-5309-4dfc-9cb3-2d3e3717971d
new bank created
9e9e1189-7eb5-43cd-b9c8-b2438c0db6c9
new bank created
3b762b69-2746-4ef4-9a8e-36238129617b
new bank created
704b9894-47fa-420b-9c7f-b86e019bcebe
new bank created
9d1a519f-d841-4cb4-ace2-2fbcd0cb983d
new bank created
0e55e9a6-662e-4fed-b568-dd44abe5829d
new bank created
43162b6d-ed89-451a-9fce-cec798862d96
new bank created
427bcda1-6d2d-4cf0-8b1e-17aaaec1ebee

Всё же, внедрять Prototype лучше так, как описано в документации, например, имплементировать интерфейс ApplicationContextAware и вызывать context.getBean(Prototype.class).https://docs.spring.io/spring-framework/docs/3.0.0.M4/reference/html/ch03s04.html#beans-factory-method-injection

Вызов ApplicationContext.getBean() это не Инверсия Контроля! Несмотря на то, что по-прежнему легко изменить реализацию, настроенную для заданного имени компонента, класс теперь напрямую полагается на Spring для предоставления этой зависимости и не может получить ее каким-либо другим способом. Вы не можете просто сделать свою собственную имитацию реализации в тестовом классе и передать ее ему самостоятельно. Это, по сути, противоречит цели Spring как контейнера для внедрения зависимостей.

Если кратко то ваш вариант это костыль.

Видимо, я не смог донести до Вас свою мысль.

В Ваш синглтон всегда инжектится один, и только один экземпляр Bank. Только в случае использования proxyMode=TARGET_CLASS, инжектится не объект класса, а прокси. Вы всегда получаете один и тот же объект, когда вызываете метод синглтона getNewBank(), можете это проверить.

Пример ниже, в котором получены два экземпляра Bank через BankService, убеждаемся, что получили один и тот же экземпляр и вызываем дважды метод getInput() для обоих Bank. Было создано 4 реальных экземпляра Bank, по одному на каждый вызов метод. Остаётся вопрос, действительно ли разработчик хотел бы вызвать метод getInput() на 4 разных объектах, или же только на 2, как это видно в коде.

final BankService bankService = ctx.getBean(BankService.class);
final Bank bank1 = bankService.getBank();
final Bank bank2 = bankService.getBank();
if(Objects.equals(bank1, bank2)) {
    System.out.println("There is the same bank");
    IntStream.range(0, 2)
            .forEach(i -> {
                System.out.println(bank1.getInput());
                System.out.println(bank2.getInput());
            });
}
Результат выполнения

There is the same bank
Created new bank.
3dafc23c-ad22-4a05-96af-ec155cc9cd46
Created new bank.
e328e94f-3a62-44df-837f-df3c8f6fd440
Created new bank.
39381838-c273-4131-9a54-0e4736cf3155
Created new bank.
3f7dca89-15ef-49e8-b71f-4289790d51e3

А почему прокси решает создать новый инстанс класса при каждом вызове метода? Это довольно контринтуитивно.

Большое спасибо за ваши комментарии. Не скажу, что теперь стало понятно с логикой спринга (скорее, всё стало сложнее), но буду впредь осторожнее с бинами, чтобы не попасть как автор статьи.

Я бы не утверждал, что это не инверсия контроля. Это скорее dependency lookup

на java как долго import org.springframework.beans.factory.annotation.Autowired; \

@SpringBootTest
class PrimaryExampleApplicationTests {
    @Autowired
    BankService messageService;

    @Test
    public void testing(){
        Bank newBank1 = messageService.getNewBank();
        Bank newBank = messageService.getNewBank(); // <- Это здесь не нужно. getNewBank() возвращает всегда один и тот же прокси объект. А вот при обращении к прокси объекту внутри него идет создание каждый раз нового прототипа.
        System.out.println(newBank.getInput());
        System.out.println(newBank1.getInput());
    }
}

Такой код даст такой же результат:

@SpringBootTest
class PrimaryExampleApplicationTests {
    @Autowired
    BankService messageService;

    @Test
    public void testing(){
        Bank newBank = messageService.getNewBank();
        System.out.println(newBank.getInput());
        System.out.println(newBank.getInput());
    }
}

Подход может подойти для обработкчиков каких-нибудь, чтобы в них можно было хранить состояния на время выполнения обработки. Ну а вообще подход извращенный, гораздо проще через контекст доставать бины прототипа.

Sign up to leave a comment.

Articles