Как литерал java 'class' может возвращать разные экземпляры объекта Class для одного и того же класса? - PullRequest
2 голосов
/ 01 сентября 2011

У меня чрезвычайно загадочная ситуация, и я ищу какие-либо идеи.

Я запускаю небольшое приложение Spring MVC, где я использую аннотацию RequestMapping на моем контроллере " AnsController ". Когда RequestDispatcher сканирует мои bean-компоненты для аннотации RequestMapping , в какой-то момент все сводится к следующей строке:

clazz.getAnnotation(RequestMapping.class)

(clazz = AnsController.class)

Строка выше не находит аннотацию, даже если она там есть.

Я начал исследовать это в отладчике Eclipse и обнаружил очень загадочную проблему. Причина, по которой вышеприведенная строка дает сбой, заключается в том, что b / c RequestMapping.class возвращает объект Class, который, по-видимому, описывает правильную аннотацию, но имеет другой внутренний идентификатор и hashCode, чем объект Class, хранящийся в аннотации массив в AnsController.class !

Я написал тестовый сервлет, в который поместил вышеуказанную строку кода, и я вижу, что класс, хранящийся в массиве аннотаций , и класс, возвращаемый RequestMapping.class , тот же объект.

Тем не менее, в RequestDispatcher сервлете, RequestMapping.class , кажется, создает экземпляр другого экземпляра класса для той же самой аннотации (я могу сказать, что внутренний идентификатор намного выше, чем идентификатор объекта Class в карте ().

Другими словами, вызов RequestMapping.class в моем тестовом сервлете приводит к тому, что объект Class отличается от вызова точно такого же кода в RequestDispatcher servlet.

Должно ли это быть даже возможным при условии, что используется один и тот же загрузчик классов? Является ли это достаточным доказательством для заключения о том, что эти разные экземпляры объекта Class, которые должны представлять одну и ту же аннотацию, должны создаваться разными загрузчиками классов?

Я не могу найти в письменной форме ничего, что подтвердило бы мое предположение, что для каждого класса разрешен только один экземпляр объекта Class, но это кажется разумным ... Или я ошибаюсь?

Ответы [ 5 ]

4 голосов
/ 01 сентября 2011

Кажется разумным, да, но, к сожалению, это не всегда работает таким образом.Описание спецификации языка Java:

Во время выполнения классы и интерфейсы загружаются виртуальной машиной Java с использованием загрузчиков классов.Каждый загрузчик классов определяет свой собственный набор классов и интерфейсов.В результате два загрузчика могут загружать идентичные определения классов или интерфейсов, но при этом создают разные классы или интерфейсы во время выполнения.

2 голосов
/ 01 сентября 2011

Они должны быть загружены разными загрузчиками классов. Обычно это не проблема - загрузчик RequestDispatcher должен быть родителем загрузчика контроллера; и дочерний загрузчик должен сначала запросить родительский загрузчик, когда запрашивается класс (здесь RequestMapping); поэтому оба должны видеть один и тот же класс RequestMapping.

Если это сломано, весь ад расколется; любой класс Spring, который видит Контроллер, отличается, и инфраструктура и контроллер не могут взаимодействовать.

Изучите загрузчик классов контроллера и выясните, почему у него нет загрузчика классов инфраструктуры в качестве родителя.

1 голос
/ 01 сентября 2011

И у нас это есть. Убедившись во всех ответах, что у меня беспорядок в загрузчике классов, я смог найти проблему. Я был в заднице от взлома, который я сделал год назад :).

В то время я написал плагин Eclipse, который использовался для запуска сервера Jetty 6 с развернутым приложением на месте. Этот плагин поместил все зависимости сборки веб-проекта в AppClasspath с помощью параметра командной строки -classpath. Это было желательно для приложения, для которого я его использовал, потому что это позволило мне значительно упростить стратегию загрузки классов в режиме разработки для этого конкретного случая. В этом случае, однако, я закончил с банками Spring на AppClassLoader и WebClassLoader, б / у Spring банок были в WEB-INF/lib.

Как только я понял это, мне просто нужно было установить parentLoaderPriority на WebAppContext на true в конфигурации моего пристани, и проблема исчезла. Конечно, это все еще хак, но достаточно для быстрого и грязного приложения, которое я здесь делаю.

Спасибо за все полезные ответы!

1 голос
/ 01 сентября 2011

Если вы пытаетесь сделать это внутри подкласса HandlerInterceptorAdapter, вы должны использовать параметр handler для получения класса. При таком подходе у меня не возникло проблем с извлечением аннотаций из контроллеров.

...
public void preHandle(HttpServletRequest request, HttpServletResponse response,
                      Object handler)
{
    Class clazz = handler.getClass();
    ...
}
...
1 голос
/ 01 сентября 2011

Для данного класса T может быть только один экземпляр Class, представляющий T на загрузчик классов.Ваш вопрос был «Возможно ли это даже при условии, что используется один и тот же загрузчик классов?», И ответ «Нет».

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

...