使用AOP作用域代理自动装配单例bean中的原型bean

3

我能够测试在单例bean中自动装配原型bean只会创建一个原型bean。

为了解决这个问题,我读到可以为原型bean定义AOP作用域代理或使用Spring的查找方法注入。

这是我尝试过的 -

PrototypeBean.java

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode =   ScopedProxyMode.INTERFACES)
public class PrototypeBean implements Prototype {

private String welcomeMessage;

public String getWelcomeMessage() {
    return welcomeMessage;
}

public void setWelcomeMessage(final String welcomeMessage) {
    this.welcomeMessage = welcomeMessage;
}
}

SingletonBean.java

@Component
public class SingletonBean implements Singleton{
@Autowired private Prototype prototype;

public Prototype getPrototype() {
    return prototype;
}

public void greet() {
    System.out.println(prototype.getWelcomeMessage());
}
}

测试类

public class AutowiredDependenciesDemo {

@Autowired private Singleton autowiredSingleton;
@Autowired ConfigurableApplicationContext context;

@Test
public void testPrototypeBeanWithAopScopedProxy(){
    Assert.assertNotNull(autowiredSingleton);

    Prototype prototypeBean = (Prototype) ((SingletonBean) autowiredSingleton).getPrototype();
    prototypeBean.setWelcomeMessage("hello world");

    autowiredSingleton.greet();

    Singleton contextSingleton = (Singleton) context.getBean("singletonBean");
    Assert.assertSame(autowiredSingleton, contextSingleton);

    Prototype anotherPrototypeBean = (Prototype) ((SingletonBean)contextSingleton).getPrototype();
    anotherPrototypeBean.setWelcomeMessage("hello india");

    contextSingleton.greet();
    autowiredSingleton.greet();
    // i expected both the prototype instances to be different. in the debugger, it does show two different 'proxied' instances. however the test fails.
    Assert.assertNotSame(prototypeBean, anotherPrototypeBean);
}

我有点不明白这里是否有什么问题?此外,调用greet()方法返回null值。
1个回答

10

关于代理和原型bean的概念,在思考时容易混淆。

当Spring Framework将一个原型作用域的bean注入到单例作用域的bean中时,它会创建一个代理对象(实现所有必需的接口),并将其注入代替原型bean的实例。然后,每当在此原型代理上调用方法时,Spring都会创建一个新实例,并在该新实例上调用该方法。

在您的情况下 - 在测试中 - 您只比较注入的代理对象,因为只有一个代理对象存在于原型Bean中,这个代理对象负责在需要时创建原型Bean的新实例。

以下是我的示例: 我有一个Prototype接口及其实现PrototypeImpl。在我的测试中,我直接从ApplicationContext获取Prototype类型的Bean,并使用@Autowired进行注入。然后在调试器中我看到了这个:

enter image description here

请注意,只有一个代理(查看其地址),但在此代理上调用'toString()'会显示两个不同的PrototypeImpl对象的地址,这正好说明了我上面所写的。
编辑:关于取消代理的信息
如M. Deinum所述,您可以通过以下方式从代理中提取基础对象:
Prototype extracted = null;

if(AopUtils.isAopProxy(a) && a instanceof Advised) {
    Object target = ((Advised)a).getTargetSource().getTarget();
    extracted = (Prototype) target;
}

是的,我也遇到了类似的情况。但是,我正在尝试在代理bean上调用特定的方法。在我的测试类中,我正在调用prototypeBean.setWelcomeMessage("hello world");anotherPrototypeBean.setWelcomeMessage("hello india");。我原本期望这会使Spring创建两个原型bean(根据PrototypeBeanImpl内存地址),但最后的Assert语句并没有确认我的观察结果。 - user3842182
2
每次调用此代理都将创建原型作用域bean的新实例。如果您想获取这样的原型作用域bean实例并在其上调用多个方法,则应使用查找方法方法。 - Rafal G.
1
它仍将是相同的代理(如答案所解释的)。如果您想比较生成的对象,则需要取消包装代理对象并获取对象的基础实例。 - M. Deinum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接