mindas
mindas

Reputation: 26713

How to @Autowire a bean which is hidden behind ProxyFactoryBean?

Let's say we have two beans, defined in Spring

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

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

What I want to achieve is the interception of all calls to B.foo(). Looking at documentation, I wrote interceptor C and changed the definition of bean B as follows:

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>

Problem: when starting up, Spring container complains: No matching bean of type [foo.B] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. In other words, it can't inject B into A because B is hidden behind org.springframework.aop.framework.ProxyFactoryBean and no longer "automagically" recognized. If I replace the definition into a simple class=foo.B, container starts fine. What's the best way to solve this?

Bonus question: is it possible to implement interception of B.foo() without involvement of ProxyFactoryBean and only using annotations (preferably without involvement of <aop:...)?

Upvotes: 2

Views: 2687

Answers (3)

Sean Patrick Floyd
Sean Patrick Floyd

Reputation: 298818

Tarlog's answer is correct, but to make it more clear: you should wire objects by their interface, not by their class:

public class A {
   @Autowired
   private C b;
}

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

Upvotes: 0

Tarlog
Tarlog

Reputation: 10154

Define an interface for foo.B (e.g. foo.BInterface) and use foo.BInterface in the class A.

Also pay attention that Autowired injections are done only once. So if foo.A is singleton, it will receive only the first created instance of foo.B, while you want it to be a prototype.

Answer to bonus: Yes, but it may be more complicated. As a possible solution you can implement BeanPostProcessor. In the implementation you can replace the foo.B with dynamic proxy. So basically you do the same, but instead of using <aop: you do it yourself using basic Spring functionality. And again: you don't solve the "prototype is not autowired" problem and you still need an interface.

Upvotes: 5

nicholas.hauschild
nicholas.hauschild

Reputation: 42849

Perhaps you want to have the Bean 'foo.B' defined outside of the ProxyFactoryBean, and refer to it from the target property.

<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>

Upvotes: 1

Related Questions