Spring, CGLIB: почему универсальный класс нельзя проксировать? - PullRequest
1 голос
/ 12 декабря 2011

Я хотел бы спросить о основной причине исключения:

Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to  
java.lang.reflect.ParameterizedType

, которое происходит в Spring, когда мы заставляем Spring генерировать прокси для классов (т. Е. Устанавливая proxy-target-class="true"в диспетчере транзакций):

<tx:annotation-driven transaction-manager="transactionManager" 
proxy-target-class="true"/>

и когда класс для прокси является параметризованным классом, то есть

public class SomeDAOImpl<SomeEntity> implements SomeDAO ...

Полный текст статьи можно прочитать, например, в этом вопросе: Абстрактный шаблон DAO и проблема Spring "Прокси не может быть приведен к ..."!

Вопрос: почему Spring не может использовать прокси Spring для этого класса?Это потому, что он использует старые библиотеки генерации кода?Из-за стирания типа?Если SomeDAOImpl не является универсальным классом, он будет успешным (я проверял это).

Пожалуйста, не отвечайте так: «Вы должны использовать прокси-интерфейсы, а не классы».Я знаю это.

Ответы [ 4 ]

5 голосов
/ 12 декабря 2011

Это похоже на проблему Spring / CGLIB, но проблема на самом деле в вашем конструкторе! Я предполагаю, что вы пытаетесь получить тип параметра, чтобы вы могли предоставить его менеджеру сущностей (или любой другой структуре постоянства, которую вы пытаетесь использовать). Я также предполагаю, что для этого вы вызываете getGenericSuperclass() и приводите его к ParameterizedType?

Способ поведения метода зависит от класса. Оказывается, что не все объекты Class реализуют интерфейс ParameterizedType (что, я согласен, странно). Сгенерированные прокси CGLIB не хранят обобщенную информацию. Это выглядит примерно так:

public class SomeDAOImplProxy extends SomeDAOImpl

На самом деле я не знаю, почему прокси-серверы CGLIB не сохраняют дженерики, поскольку детали прокси-серверов CGLIB очень сложны. Возможно, это просто невозможно. Возможно также, что это не является для них приоритетом.

Вот простой эксперимент, который убирает Spring и CGLIB из картинки, но все равно получает эту ошибку.

public static void main(String [] args) {
    List<Object> fooList = new ArrayList<Object>();
    String fooString = "";
    System.out.println((ParameterizedType)fooList.getClass().getGenericSuperclass());
    System.out.println((ParameterizedType)fooString.getClass().getGenericSuperclass());
}
4 голосов
/ 17 декабря 2012

Поскольку CGLIB подклассирует ваш класс для генерации прокси, вы должны получить универсальный суперкласс суперкласса, чтобы получить ParameterizedType:

public class MyCglibProxiedBean<T> {

    private final Class<T> paramClass;

    public MyCglibProxiedBean() {
        Type genericSuperClass = getClass().getGenericSuperclass();

        // Get the generic super class of the super class if it's a cglib proxy
        if (getClass().getName().contains("$$EnhancerByCGLIB$$")) {
            genericSuperClass = getClass().getSuperclass().getGenericSuperclass();
        }

        this.paramClass = (Class<T>) ((ParameterizedType) genericSuperClass).getActualTypeArguments()[0];
    }
}
1 голос
/ 02 января 2014

Мы используем решение, аналогичное ответ Джеймса , но ИМХО немного более общее:

Type genericSuperClass = repositoryClass.getGenericSuperclass();
ParameterizedType parametrizedType;
if (genericSuperClass instanceof ParameterizedType) { // class
    parametrizedType = (ParameterizedType) genericSuperClass;
} else if (genericSuperClass instanceof Class) { // in case of CGLIB proxy
    parametrizedType = (ParameterizedType) ((Class<?>) genericSuperClass).getGenericSuperclass();
} else {
    throw new IllegalStateException("class " + repositoryClass + " is not subtype of ParametrizedType.");
}

@SuppressWarnings("unchecked")
Class<T> entityClass = (Class<T>) parametrizedType.getActualTypeArguments()[0];
return entityClass;
0 голосов
/ 11 мая 2016

Мне удалось решить проблему с помощью этого кода:

Type t = getClass();
do {
  t = ((Class<T>) t).getGenericSuperclass();
} while (!(t instanceof ParameterizedType));
ParameterizedType pt = (ParameterizedType) t;
actualGenericType = (Class<T>) pt.getActualTypeArguments()[0];

Я думаю, что это немного более чистое решение, потому что оно не использует внутреннюю структуру CGLIB.

...