@Autowire странная проблема
У меня странное поведение при автоподготовке
У меня есть похожий код, похожий на этот, и он работает
@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);
}
с этим кодом я получаю a org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type for Class2
.
Похоже, что аннотация @Transitional
несовместима с интерфейсом, потому что если я удалю аннотацию @Transitional
или я mplements IServiceReference<Class3, Long>
, проблема исчезнет, а bean будет введена (хотя мне нужно иметь оба в этом классе). Это также происходит, если я помещаю аннотацию @Transitional
в методы вместо класса.
Я использую Spring 3.0.2, если это помогает.
Не совместим ли интерфейс с транзакционным методом?
Может быть, это ошибка Spring?
Ответы
Ответ 1
Проблема в том, что ваш Class1 нуждается в ссылке на IServiceReference, а не на конкретную ссылку Class2
@Controller
public class Class1 {
@Autowired
private IServiceReference object2;
...
}
Причина в том, что Spring создает динамический прокси для классов, которые вы отметили @Transactional. Таким образом, когда Class2 создается, он завернут в объект Proxy, который, очевидно, не относится к типу Class2, но имеет тип IServiceReference.
Если вы хотите, чтобы поведение использования класса 2 с поддержкой прокси-сервера, вам нужно включить CGLIB
Читайте ниже:
Из Springs Doc:
Spring AOP по умолчанию использует стандартные Динамические прокси-серверы J2SE для прокси-серверов AOP. Это позволяет использовать любой интерфейс (или набор интерфейсы) для проксирования.
Spring AOP также может использовать прокси CGLIB. Это необходимо для прокси-классов, а не интерфейсов. CGLIB используется по умолчанию, если бизнес-объект не реализовать интерфейс. Поскольку это хорошая практика программирования интерфейсов а не классы, бизнес-классы обычно будет реализовывать один или несколько бизнес-интерфейсов. Можно принудить использование CGLIB в тех (надеюсь, редкие) случаи, когда вам нужно сообщить метод, который не является объявлен на интерфейсе, или где вы необходимо передать прокси-объект в метод как конкретный тип.
Важно понять тот факт, что Spring AOP основан на прокси-сервере. См. раздел, озаглавленный Раздел 6.6.1, "Понимание прокси-серверов АОП" для тщательное изучение именно того, что эта реальность реализации фактически означает.
Ответ 2
В аннотации Transactional
указывается Spring для создания прокси-объектов вокруг аннотированного beans для реализации транзакционной семантики. Сгенерированный прокси-сервер будет реализовывать те же интерфейсы, что и целевой bean. Поэтому, если ваша цель bean реализует IServiceReference
, тогда будет создан сгенерированный прокси.
Если целевой bean не имеет реализованных интерфейсов, тогда сгенерированный прокси будет вместо этого подклассом целевого типа bean.
В вашем исходном примере транзакционный прокси будет подклассом Class2
, потому что Class2
не реализовал никаких интерфейсов. Когда вы изменили Class2
для реализации IServiceReference
, сгенерированный прокси-сервер больше не расширил Class2
, а вместо этого реализовал IServiceReference
. Это вызвало ваш ClassCastException
.
Лучший подход к этой ситуации - удалить ссылку от Class1
до Class2
, а вместо этого поговорить с Class2
исключительно через свои интерфейсы. Class2
может реализовать как можно больше интерфейсов, прокси-сервер будет реализовывать их все.
Вы можете заставить Spring создавать прокси-классы подкласса независимо от интерфейсов, но это дополнительная сложность, и я бы рекомендовал против него.
Ответ 3
Вы можете заставить его прокси, добавив
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
также см. эту документацию.