Подтвердить что ты не робот

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

4b9b3361

Ответ 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)

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