- 免责声明:**我已经阅读了以下关于JDK动态代理和CGLIB的有用的工作人员:https://stackoverflow.com/a/21762454/2674303
我读过以下有趣的文章:Injecting Spring Prototype bean into Singleton bean
第一个案例:
- 原型:**
@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。
为了解释行为,作者提供了以下图片:
我不明白哪一个bean是依赖的,为什么每次访问代理messageBuilder
genetes都需要新的bean示例化。为什么第一种情况会不同?你能解释一下吗?就我所知-代理是创建的-使用CGLIB或使用动态代理,所以无论如何代理都是注入的
2条答案
按热度按时间nwnhqdif1#
如果您使用
prototype
范围定义Bean,则当从Spring上下文引用该Bean时,ApplicationContext
将返回一个新示例。在第一个示例中,在创建MessageService
singleton Bean时,将创建MessageBuilder
prototype 的新示例。但是,由于MessageService
在Spring生命周期中仅构造一次(因为它是单例),它只请求注入MessageBuilder
原型bean的一个引用。换句话说,在第一个示例中,
MessageBuilder
bean只被示例化一次,因为它被注入(自动连接)到MessageService
中一次。在注入的原型bean上执行的方法调用将不会被代理到新的原型bean示例。通过将
proxyMode
设置为TARGET_CLASS
,ApplicationContext
不直接将新的原型bean示例注入到另一个bean中,而是注入原型bean的 * 代理 *。因此,当单例bean从注入的单例bean调用方法时,中间代理引用新的原型bean并调用该方法。更多信息可以在Spring文档中找到:
如果你想注射(例如)将一个HTTP请求作用域的bean插入到另一个更长生存期的作用域的bean中,您可以选择注入一个AOP代理来代替作用域bean。您需要注入一个代理对象,该代理对象公开与作用域对象相同的公共接口,但也可以从相关作用域中检索真实的的目标对象(如HTTP请求)并将方法调用委托给真实的对象。
xpcnnkqh2#
https://stackoverflow.com/a/21762454/2674303是关于JDK动态代理和CGLIB的讨论
文章http://dolszewski.com/spring/accessing-prototype-bean-in-singleton/是关于如何将原型bean注入单例bean的
通过原型作用域的定义,在为MessageBuilder设置原型作用域时,每次得到的对象都不一样,可以验证ctx.getBean(MessageBuilder.class);
当MessageService被自动连接到一个单例bean时,你想要相同的行为。MessageService是单例的,并且只初始化一次。如果你向它注入一个真实的的MessageBuilder对象,你不可能在MessageService中得到一个不同的MessageBuilder对象。所以你需要为MessageBuilder对象注入一个aop代理。Spring可以在内部正确地处理它,并且即使只有一个MessageService,也会给你不同的对象。