gpt4 book ai didi

java - 为什么在将 CGLIB 原型(prototype)注入(inject) Singleton 的情况下,每次对原型(prototype)的访问都会创建一个新对象?

转载 作者:行者123 更新时间:2023-12-02 00:09:00 24 4
gpt4 key购买 nike

免责声明:我读过以下关于 JDK 动态代理和 CGLIB 的有用文章:https://stackoverflow.com/a/21762454/2674303

我读过以下有趣的文章:Injecting Spring Prototype bean into Singleton bean

第一种情况:

原型(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);
}

}

显然测试失败了,因为注入(inject)只发生一次,实际结果为 1,但我们预期为 2。

第二种情况:

Singleton 和 Test 是相同的,但原型(prototype)现在看起来像这样(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

我无法理解哪个 bean 是依赖的,以及为什么每次访问代理 messageBuilder 基因都会产生新的 bean 实例化。为什么第一种情况的情况不同?你能解释一下吗?据我了解 - 代理无论如何都是装箱的 - 使用 CGLIB 或使用动态代理,所以无论如何代理都会被注入(inject)

最佳答案

如果您使用 prototype 范围定义一个 bean,则当从 Spring 上下文引用该 bean 时,ApplicationContext 将返回一个新实例。在第一个示例中,当创建 MessageService singleton bean 时,会创建 MessageBuilder prototype 的新实例。然而,由于 MessageService 在 Spring 生命周期中仅构造一次(因为它是单例),因此它只请求注入(inject) MessageBuilder 原型(prototype) bean 的一个引用。

换句话说,在第一个示例中,MessageBuilder bean 仅实例化一次,因为它被注入(inject)( Autowiring )到 MessageService 一次。对注入(inject)的原型(prototype) bean 执行的方法调用之后将不会代理到新的原型(prototype) bean 实例。

通过将proxyMode设置为TARGET_CLASSApplicationContext不会直接在另一个bean中注入(inject)新的原型(prototype)bean实例,而是注入(inject)一个原型(prototype) bean 的代理。因此,当单例 bean 调用注入(inject)的单例 bean 中的方法时,中间代理引用新的原型(prototype) bean 并调用该方法。

更多信息可以在Spring documentation中找到:

If you want to inject (for example) an HTTP request-scoped bean into another bean of a longer-lived scope, you may choose to inject an AOP proxy in place of the scoped bean. That is, you need to inject a proxy object that exposes the same public interface as the scoped object but that can also retrieve the real target object from the relevant scope (such as an HTTP request) and delegate method calls onto the real object.

关于java - 为什么在将 CGLIB 原型(prototype)注入(inject) Singleton 的情况下,每次对原型(prototype)的访问都会创建一个新对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58136577/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com