Как @Autowire bean-компонент, который скрыт за ProxyFactoryBean? - PullRequest
2 голосов
/ 28 июня 2011

Допустим, у нас есть два 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] для зависимости: ожидается как минимум 1 компонент, который квалифицируется как кандидат для автоматической передачи этой зависимости . Другими словами, он не может внедрить B в A, поскольку B скрыт за org.springframework.aop.framework.ProxyFactoryBean и больше не распознается автоматически. Если я заменю определение на простой class=foo.B, контейнер запустится нормально. Какой лучший способ решить эту проблему?

Бонусный вопрос: Возможно ли реализовать перехват B.foo() без участия ProxyFactoryBean и только с использованием аннотаций (желательно без участия <aop:...)?

Ответы [ 3 ]

5 голосов
/ 28 июня 2011

Определите интерфейс для foo.B (например, foo.BInterface) и используйте foo.BInterface в классе A.

Также обратите внимание, что Autowired инъекции делаются только один раз. Таким образом, если foo.A является синглтоном, он получит только первый созданный экземпляр foo.B, а вы хотите, чтобы он был прототипом.

Ответ на бонус : Да, но это может быть сложнее. В качестве возможного решения вы можете реализовать BeanPostProcessor. В реализации вы можете заменить foo.B на динамический прокси. Таким образом, в основном вы делаете то же самое, но вместо использования <aop: вы делаете это самостоятельно, используя базовую функциональность Spring. И снова: вы не решаете проблему «прототип не подключен автоматически» и вам все еще нужен интерфейс.

1 голос
/ 28 июня 2011

Возможно, вы хотите, чтобы Bean-компонент 'foo.B' определялся вне ProxyFactoryBean, и ссылался на него из целевого свойства.

<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>
0 голосов
/ 28 июня 2011

Ответ Тарлога верен, но для ясности: вы должны связывать объекты по интерфейсу, а не по классу:

public class A {
   @Autowired
   private C b;
}

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