如何@Autowire隐藏在ProxyFactoryBean后面的bean?

时间:2011-06-28 14:16:43

标签: java spring javabeans interceptor autowired

假设我们有两个bean,在Spring中定义

<bean class="foo.A"/>
<bean class="foo.B"/>
public class A {
   @Autowired
   private B b;
}

public class B {
   public void foo() {
      ...
   }
}

我想要实现的是拦截对B.foo()的所有调用。查看文档,我编写了拦截器C并更改了bean B的定义,如下所示:

public class C implements org.springframework.aop.MethodBeforeAdvice {
    public void before(final Method method, final Object[] args, final Object target) {
        // interception logic goes here
    }
}
<bean class="foo.C"/>

<bean class="org.springframework.aop.framework.ProxyFactoryBean" scope="prototype">
    <property name="proxyTargetClass" value="true"/>
    <property name="singleton" value="false"/>
    <property name="target">
        <bean class="foo.B" scope="prototype"/>
    </property>
    <property name="interceptorNames">
        <list>
            <value>foo.C</value>
        </list>
    </property>
</bean>

问题:启动时,Spring容器会抱怨:找不到匹配的[foo.B]类型的匹配bean:预计至少有一个bean可以作为此依赖项的autowire候选者。换句话说,它无法将B注入A,因为B隐藏在 org.springframework.aop.framework.ProxyFactoryBean 之后,不再“自动” “得到认可。如果我将定义替换为简单的class=foo.B,容器就可以正常启动了。解决这个问题的最佳方法是什么?

加分问题:是否可以在不涉及B.foo()且仅使用注释(最好不涉及ProxyFactoryBean)的情况下实施<aop:...的拦截?< / p>

3 个答案:

答案 0 :(得分:5)

foo.B定义一个界面(例如foo.BInterface)并在A类中使用foo.BInterface

还要注意Autowired注射只进行一次。因此,如果foo.A是单身,它只会收到第一个创建的foo.B实例,而您希望它是原型。

回答奖金:是的,但可能会更复杂。作为一种可能的解决方案,您可以实施BeanPostProcessor。在实现中,您可以使用动态代理替换foo.B.所以基本上你也这样做,但不是使用<aop:,而是使用基本的Spring功能自己做。再说一遍:你没有解决“原型不是自动装配”的问题,你还需要一个界面。

答案 1 :(得分:1)

也许您希望在ProxyFactoryBean之外定义Bean'foo.B',并从目标属性中引用它。

<bean class="foo.C"/>
<bean id="fooB" class="foo.B" scope="prototype"/>

<bean class="org.springframework.aop.framework.ProxyFactoryBean" scope="prototype">
   <property name="proxyTargetClass" value="true"/>
   <property name="singleton" value="false"/>
   <property name="target" ref="fooB"/>
   <property name="interceptorNames">
      <list>
         <value>foo.C</value>
      </list>
   </property>
</bean>

答案 2 :(得分:0)

Tarlog的回答是正确的,但为了更清楚: 你应该通过他们的界面而不是他们的类来连接对象:

public class A {
   @Autowired
   private C b;
}

public class B implements C{
   public void foo() {
      ...
   }
}