ApplicationContext.getBean(Class clazz) 与代理不兼容
我在 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之前的世界中,我像在Spring 3中一样使用它
ApplicationContext ctx = ...;
my.Interface foo = (my.Interface) ctx.getBean("my.Bean"); // cast is necessary
,可以进行类型安全查找,例如:
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 查找,如果是的话 - 如何?
I have a bean definition in Spring and it's proxy counterpart which is meant to be used everywhere:
<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>
This all works well; and in pre-Spring v3 world I was using it like
ApplicationContext ctx = ...;
my.Interface foo = (my.Interface) ctx.getBean("my.Bean"); // cast is necessary
In Spring 3 it became possible to do type safe lookups, e.g.:
my.Interface foo = ctx.getBean(my.Interface.class);
Again, this works well for ordinary beans whereas for proxied beans I am getting my.BeanTarget
instead of my.Bean
. I have tried to inline my.BeanTarget
(as shown in Spring documentation) to make it hidden, but all I got was
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [my.Interface] is defined: expected single bean but found 0:
So is it possible to use type safe bean lookups with proxied beans and if yes - how?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这里的问题是
ProxyFactoryBean
上的scope="prototype"
。上下文只会急切地初始化单例 bean 定义。非单例范围的 Bean 仅在需要时才进行初始化。这意味着当您向上下文请求给定类型的 bean 时,上下文无法初始化这些非单例 bean 来询问它们的类型,它必须纯粹依赖 bean 定义中的信息。
对于
ProxyFactoryBean
,生成的代理的类型由需要完全初始化bean 的复杂逻辑决定。如果没有该初始化,ProxyFactoryBean
只能将目标类型报告为null
。除了使用单例 bean 定义,或者通过名称显式询问 bean 之外,我无法解决这个问题,例如
然后:
在这里,我们使用 bean 名称的约定作为它们实现的接口。
The problem here is the
scope="prototype"
on yourProxyFactoryBean
.The context will only eagerly-initialize singleton bean definitions. Beans of non-singleton scope are only initialized when asked for. This means that when you ask the context for beans of a given type, the context cannot initialize those non-singleton beans in order to ask them for their type, it has to go purely on the information in the bean definition.
In the case of
ProxyFactoryBean
, the type of the generated proxy is determined by complex logic that requires the bean to be fully initialized. Without that initialization,ProxyFactoryBean
can only report the target type asnull
.I can't say a way around this, other than using a singleton bean definition, or explicitly asking for the bean by name, e.g.
and then:
Here, we use the convention of bean names being the interface they implement.
看起来应该使用
singleton
属性而不是scope
属性指定由ProxyFactoryBean
创建的代理范围:这解决了目标 bean 时的问题内。
当您有多个同一类的顶级 bean 时,您可以通过 id 使用类型安全查找:
It looks like the scope of proxies created by
ProxyFactoryBean
should be specified usingsingleton
property instead ofscope
attribute:This solved the problem when target bean is inner.
When you have several top-level beans of the same class, you can use a type-safe lookup by id:
你不能制作
my.Interface foo = ctx.getBean(my.Bean.class);
吗?Can't you make
my.Interface foo = ctx.getBean(my.Bean.class);
?由于 Spring 使用接口,在 aop 的上下文中,您可以定义一组不同的接口并请求您期望的接口。这样就不需要对真实类进行强制转换,但 Spring 将管理接口。
假设你有一个类 A 实现了 B。你想将 A 强制转换为 B,但它被拒绝了,因为 A 是 aop 的代理。
然后让 A 实现 C,C 扩展 B。C 拥有所需的方法,而 C 是只能从您的实现代码访问的私有接口。
最后让 spring 根据您的期望注入 B 或 C。
这样,即使真实的类是代理,它也可以根据您的需要实现您的私有接口。
As Spring works with Interfaces, in the context of aop, you could define different set of interfaces and request the one you expect. This way no cast will be needed to a real class but Spring will manage interfaces.
Let's say you have Class A implements B. You want to cast A to B but it is refused as A is a proxy due to aop.
Then make A implements C and C extends B. C owns needed methods, and C is private interface accessed only from your implementation code.
Finally ask spring to inject either B or C depending on your expectations.
This way, even if real class is a proxy, it implements your Private Interface with all what your need.