ApplicationContext.getBean(Class clazz)与代理不兼容

时间:2010-08-26 10:11:00

标签: java spring proxy aop

我在Spring中有一个bean定义,它的代理对应物可以在任何地方使用:

<bean name="my.Bean" class="org.springframework.aop.framework.ProxyFactoryBean" scope="prototype">
  <property name="proxyInterfaces" value="my.Interface"/>
  <property name="target" ref="my.BeanTarget"/>
  <property name="interceptorNames">
    <list>
      <value>someInterceptor</value>
    </list>
  </property>
</bean>

<bean name="my.BeanTarget" class="my.InterfaceImpl" scope="prototype">
  <property name="foo" ref="bar"/>
</bean>

这一切都运作良好;在Spring-v3之前的世界中我使用它就像

一样
ApplicationContext ctx = ...;
my.Interface foo = (my.Interface) ctx.getBean("my.Bean"); // cast is necessary

在Spring 3中,可以进行类型安全查找,例如:

my.Interface foo = ctx.getBean(my.Interface.class);

同样,这适用于普通bean,而对于代理bean,我得到的是my.BeanTarget而不是my.Bean。我试图内联my.BeanTarget(如Spring文档中所示)将其隐藏起来,但我得到的只是

org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [my.Interface] is defined: expected single bean but found 0: 

因此可以对代理bean使用类型安全bean查找,如果是 - 如何?

4 个答案:

答案 0 :(得分:6)

此处的问题是scope="prototype"上的ProxyFactoryBean

上下文只会急切地初始化单例bean定义。非单一范围的豆只在被要求时才被初始化。这意味着当你向上下文询问给定类型的bean时,上下文不能初始化那些非单例bean以询问它们的类型,它必须完全依赖于bean定义中的信息。

ProxyFactoryBean的情况下,生成的代理的类型由需要完全初始化bean的复杂逻辑确定。如果没有初始化,ProxyFactoryBean只能将目标类型报告为null

除了使用单例bean定义,或者通过名称明确要求bean之外,我无法解决这个问题,例如。

<bean id="my.Interface"> class="ProxyFactoryBean"... >

然后:

ctx.getBean(MyInterface.class.getName());

在这里,我们使用bean名称的约定作为它们实现的接口。

答案 1 :(得分:2)

看起来ProxyFactoryBean创建的代理范围应使用singleton属性而不是scope属性指定:

<bean name="my.Bean" class="org.springframework.aop.framework.ProxyFactoryBean">  
    <property name="singleton" value="false"/>  
    ...
</bean>

这解决了目标bean内部时的问题。

如果您有多个同一类的顶级bean,则可以使用id进行类型安全查找:

my.Interface foo = ctx.getBean("my.Bean", my.Interface.class); 

答案 2 :(得分:1)

你不能做my.Interface foo = ctx.getBean(my.Bean.class);吗?

答案 3 :(得分:0)

当Spring使用Interfaces时,在aop的上下文中,您可以定义不同的接口集并请求您期望的接口。这样就不需要对真正的类进行强制转换,但Spring会管理接口。

让我们假设您有A类实现B.您想要将A转换为B但是由于aop,A被拒绝,因为A是代理。 然后使A实现C和C扩展B. C拥有所需的方法,C是仅从您的实现代码访问的专用接口。 最后请求春天注入B或C,具体取决于您的期望。

PrivateItf executor = context.getBean(PrivateItf.class);

这样,即使真正的类是代理,它也可以根据您的需要实现您的私有接口。