@Autowire странная проблема - PullRequest
       7

@Autowire странная проблема

23 голосов
/ 26 апреля 2010

У меня странное поведение при автопроводке

У меня есть подобный код, и он работает

@Controller
public class Class1 {
    @Autowired
    private Class2 object2;
    ...
}

@Service
@Transactional
public class Class2{
   ...
}

Проблема в том, что мне нужно, чтобы Class2 реализовал интерфейс, поэтому я изменил только Class2, так что теперь он выглядит так:

@Controller
public class Class1 {
    @Autowired
    private Class2 object2;
    ...
}

@Service
@Transactional
public class Class2 implements IServiceReference<Class3, Long>{
   ...
}

public interface IServiceReference<T, PK extends Serializable> {
    public T reference(PK id);
}

с этим кодом я получаю org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type for Class2. Кажется, что аннотация @Transitional не совместима с интерфейсом, потому что, если я удаляю аннотацию @Transitional или i mplements IServiceReference<Class3, Long>, проблема исчезает, и бин вводится (хотя мне нужно иметь оба в этом классе). Это также происходит, если я добавляю аннотацию @Transitional в методы, а не в класс.

Я использую Spring 3.0.2, если это поможет.

Не совместим ли интерфейс с транзакционным методом? Может ли это быть весенняя ошибка?

Ответы [ 3 ]

28 голосов
/ 26 апреля 2010

Проблема в том, что вашему Class1 нужна ссылка на IServiceReference, а не конкретная ссылка на Class2

@Controller
public class Class1 {
@Autowired
private IServiceReference object2;
    ...
}

Причина в том, что Spring создает динамический прокси для классов, которые вы пометили как @Transactional. Таким образом, когда Class2 создается, он оборачивается в объект Proxy, который явно не относится к типу Class2, но имеет тип IServiceReference.

Если вы хотите использовать Class2 с поддержкой прокси, вам нужно включить CGLIB Читайте ниже:

Из документа Спрингс:

Spring AOP по умолчанию использует стандартные Динамические прокси J2SE для прокси AOP. Это позволяет любой интерфейс (или набор интерфейсы) для прокси.

Spring AOP также может использовать прокси CGLIB. Это необходимо для прокси-классов, а не интерфейсы. CGLIB используется по умолчанию, если бизнес-объект делает не реализовать интерфейс. Как оно есть хорошая практика для программирования интерфейсов а не классы, бизнес-классы обычно будет реализовывать один или несколько бизнес-интерфейсы. Можно заставить использование CGLIB, в тех (надеюсь, редко) случаи, когда вам нужно посоветовать метод, который не объявлено на интерфейсе, или где вы необходимо передать объект прокси в метод как конкретный тип.

Важно понять тот факт, что Spring AOP основан на прокси. Увидеть раздел под названием Раздел 6.6.1, «Понимание AOP-прокси» для тщательное изучение того, что именно эта деталь реализации на самом деле значит.

13 голосов
/ 26 апреля 2010

Аннотация Transactional предписывает Spring генерировать прокси-объекты вокруг аннотированных bean-компонентов для реализации семантики транзакций. Сгенерированный прокси будет реализовывать те же интерфейсы, что и целевой компонент. Поэтому, если ваш целевой бин реализует IServiceReference, то и сгенерированный прокси тоже будет.

Если целевой компонент не имеет реализованных интерфейсов, вместо этого сгенерированный прокси будет подклассом типа целевого компонента.

В вашем исходном примере транзакционный прокси будет подклассом Class2, потому что Class2 не реализовал никаких интерфейсов. Когда вы изменили Class2 для реализации IServiceReference, сгенерированный прокси больше не расширялся Class2, а вместо этого реализовал IServiceReference. Это вызвало ваш ClassCastException.

Лучший подход к этой ситуации - удалить ссылку с Class1 на Class2 и вместо этого говорить с Class2 исключительно через ее интерфейсы. Class2 может реализовывать столько интерфейсов, сколько вам нужно, прокси будет реализовывать их все.

Вы можете заставить Spring генерировать прокси подкласса независимо от интерфейсов, но это дополнительная сложность, и я бы рекомендовал против нее.

1 голос
/ 17 июля 2012

Вы можете принудительно настроить его на прокси, добавив

@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)

также см. эту документацию .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...