Почему в случае внедрения прототипа CGLIB в Singleton каждый доступ к прототипу создает новый объект? - PullRequest
0 голосов
/ 27 сентября 2019

Отказ от ответственности: Я прочитал следующий полезный персонал о динамическом прокси JDK и CGLIB: https://stackoverflow.com/a/21762454/2674303

Я прочитал следующую интересную статью: Инъекция бина Spring Prototypeв синглтон бин

Первый случай:

Прототип:

@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
class MessageBuilder {

    private static final AtomicInteger instanceCounter = new AtomicInteger(0);

    MessageBuilder() {
        instanceCounter.incrementAndGet();
    }

    static int getInstanceCounter() {
        return instanceCounter.get();
    }
    ....
}

Синглтон:

@Service
class MessageService {

    private final MessageBuilder messageBuilder;

    MessageService(MessageBuilder messageBuilder) {
        this.messageBuilder = messageBuilder;
    }

    Message createMessage(String content, String receiver) {
        return messageBuilder
                .withContent(content)
                .withReceiver(receiver)
                .build();
    }     
}

Тест:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MessageServiceTest {

    @Autowired
    private MessageService messageService;

    @Test
    public void shouldCreateTwoBuilders() throws Exception {
        //when
        messageService.createMessage("text", "alice");
        messageService.createMessage("msg", "bob");
        //then
        int prototypeCounter = MessageBuilder.getInstanceCounter();
        assertEquals("Wrong number of instances", 2, prototypeCounter);
    }

}

Очевидно, что тест не пройден, потому что инъекция происходит только один раз, и фактический результат будет 1, но мы ожидали 2.

Второй случай:

Singleton и Test одинаковы, но прототип теперь выглядит следующим образом (proxyMode был изменен):

@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, 
       proxyMode = ScopedProxyMode.TARGET_CLASS)
class MessageBuilder {
  // ...
}

Когда мы начинаем наш тест, мы видим, что фактический результат равен 6 , поскольку изнутри createMessage метод messageBuilder доступен 3 раза.и createMessage метод вызывается дважды, поэтому 3 * 2 = 6.

Для объяснения поведения автор предоставил следующую картинку:

enter image description here

Я не могу понять, какой бин зависит и почему каждый доступ к прокси messageBuilder порождает новую реализацию бина.Почему для первого случая ситуация иная?Не могли бы вы объяснить это?Насколько я понимаю - прокси все равно создаются - используя CGLIB или динамический прокси, так что в любом случае прокси вводится

1 Ответ

0 голосов
/ 29 сентября 2019

Если вы определяете компонент с областью действия prototype, новый экземпляр возвращается ApplicationContext, когда на компонент ссылаются из контекста Spring.В первом примере новый экземпляр MessageBuilder prototype создается при создании компонента MessageService singleton .Однако, поскольку MessageService создается только один раз в течение жизненного цикла Spring (поскольку он является одиночным), он запрашивает только одну ссылку на компонент-прототип MessageBuilder для внедрения.

Другими словами, вВ первом примере боб MessageBuilder создается только один раз, поскольку один раз (автоматически подключается) в MessageService.Вызовы методов, выполняемые с внедренным компонентом-прототипом, впоследствии не будут переданы на новый экземпляр компонента-прототипа.

Если для proxyMode установлено значение TARGET_CLASS, ApplicationContext не будет напрямую внедрять новый экземпляр компонента-прототипа.в другом компоненте, но вместо этого вводит прокси компонента-прототипаТаким образом, когда одноэлементный компонент вызывает метод из внедренного одноэлементного компонента, промежуточный прокси-сервер ссылается на новый прототипный компонент и вызывает метод.

Дополнительную информацию можно найти в документации Spring :

Если вы хотите внедрить (например) компонент с областью действия HTTP-запроса в другой компонент с более длительным сроком действия, вы можете внедрить прокси-сервер AOP вместо этого компонента.То есть вам нужно внедрить прокси-объект, который предоставляет тот же общедоступный интерфейс, что и объект области действия, но который также может извлечь реальный целевой объект из соответствующей области (например, HTTP-запрос) и делегировать вызовы методов в реальный объект.

...