【问题标题】:ApplicationContext.getBean(Class clazz) doesn't go well with proxiesApplicationContext.getBean(Class clazz) 不适用于代理
【发布时间】:2023-04-06 01:21:01
【问题描述】:

我在 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 查找,如果可以,如何使用?

【问题讨论】:

  • 你真的需要直接与上下文交互吗?我的大多数应用程序只需要引导它,然后使用依赖注入处理其他所有内容(这适用于代理 bean)。我已经完成了一些需要访问上下文的框架工作,但根据我的经验,这种情况很少见。
  • 我们的系统比较宽,有些位和类不是Spring诞生的(也不可能),所以他们必须使用beanFactory/appCtx来获取必要的依赖。

标签: java spring proxy aop


【解决方案1】:

这里的问题是您的ProxyFactoryBean 上的scope="prototype"

上下文只会急切地初始化单例 bean 定义。非单例范围的 Bean 仅在请求时才被初始化。这意味着,当您向上下文询问给定类型的 bean 时,上下文无法初始化那些非单例 bean 以询问它们的类型,它必须纯粹基于 bean 定义中的信息。

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

除了使用单例 bean 定义或通过名称明确要求 bean 之外,我无法说出解决方法,例如

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

然后:

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

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

【讨论】:

    【解决方案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); 
    

    【讨论】:

    • 我接受这个答案太快了。事实证明,singleton=false 属性与 scope=prototype 不是一回事。即使指定了 singleton=false,我也设法获得了包装在目标代理中的单例 bean。斯卡夫曼的回答其实更接近真相。
    • getBean(String, Class) 中的字符串参数是什么?你能解释一下吗?
    • 它是应用程序上下文中请求的 bean 的名称。它对应于 XML 配置中的idname 属性。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      由于 Spring 使用接口,在 aop 的上下文中,您可以定义不同的接口集并请求您期望的接口。这样,一个真正的类不需要强制转换,但 Spring 将管理接口。

      假设您有 A 类实现 B。您想将 A 强制转换为 B,但由于 aop 导致 A 是代理而被拒绝。 然后让 A 实现 C,C 扩展 B。C 拥有所需的方法,而 C 是只能从您的实现代码访问的私有接口。 最后要求 spring 根据您的期望注入 B 或 C。

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

      这样,即使真正的类是一个代理,它也可以满足您的所有需要​​来实现您的私有接口。

      【讨论】:

        猜你喜欢
        • 2012-07-17
        • 2013-02-15
        • 1970-01-01
        • 2011-11-20
        • 1970-01-01
        • 2012-01-08
        • 2015-12-13
        • 2016-11-25
        • 1970-01-01
        相关资源
        最近更新 更多