2016-05-13 5 views
0

У меня есть MessageBodyReader со следующего класса:Как MessageBodyReader знает, что такое общий тип?

@Provider 
@Consumes(MediaType.APPLICATION_JSON) 
public class TransactionMessageBodyReader implements MessageBodyReader<Transaction<Customer>> 

boolean isReadable(Class<?> type, 
         Type genericType, 
         Annotation[] annotations, 
         MediaType mediaType) 

И у меня есть следующий REST конечной точки:

@POST 
@Produces(MediaType.APPLICATION_JSON) 
@Consumes(MediaType.APPLICATION_JSON) 
@Path("trans") 
public Response checkStatus(Transaction<Customer> transaction) { ... } 

В моем отладчике у меня есть точки останова на isReadable и она попадает в точку останова перед вызовом checkStatus способ. До сих пор хорошо. Я вижу в своем отладчике, что genericType говорит Transaction<Customer> ... другими словами, он знает информацию о типе. Как он может знать информацию о типе, когда стирание стилей Java стирает его, чтобы быть только Transaction во время выполнения? Я вижу, что genericType состоит из ParameterizedType, который, как мне кажется, используется для передачи информации о типе вокруг (чтобы обойти проблему стирания типа). Однако, как Джерси заполняет genericType автоматически? Потому что я никогда не указывал информацию о типе, кроме заголовков методов, которые я опубликовал выше.

Кроме того, второй вопрос: если «автоматически» Джерси так много знает о моих типах, зачем мне вообще нужен MessageBodyReader? Не существует ли более простой способ использования дженериков с конечными точками Джерси/REST и заставить его «просто работать»?

+0

На самом деле, это происходит потому, что есть 'реализация MessageBodyReader', что она волшебным образом работает. См. Также http://stackoverflow.com/questions/19860393/java-generics-obtaining-actual-type-of-generic-parameter – Tunaki

+0

@Tunaki Я знаю, но почему? Как MessageBodyReader действительно знает информацию о типе? (Это также стирается тип - правильно?). – KyleM

+0

Я подозреваю, что это использование стандартного трюка: стирание стилей стирает дженерики от _объектов_, но не _classes_. –

ответ

4

Взгляните на javadoc.

genericType - тип экземпляра, который будет выпущен. Например. если тело сообщения должно быть преобразовано в параметр метода, это будет формальный тип параметра метода, возвращаемый Method.getGenericParameterTypes.

Общая параметризация в классах, методах и полях поддерживается в соответствующих файлах .class. Это, очевидно, необходимо для компиляции. Вы не сможете упаковывать и использовать классы Java в качестве библиотеки, если это не так.

Reflection предоставляет информацию этого типа (и более).

Джерси в основном сканирует ваши классы для методов обработчика, аннотируется @POST и т.п. и получает соответствующие объекты Method. После того, как это имеет, что он может использовать Method#getGenericParameterTypes()

Возвращает массив Type объектов, представляющих формальный параметр типов, в порядке декларации, исполняемого файла, представленного этим объекта. Возвращает массив длиной 0, если базовый исполняемый файл не принимает параметров.

Если формальный тип параметра параметризованный типа, Type объекта возвращается к нему должен точно отражать фактические параметры типа , используемых в исходном коде.

На Другими словами, Type должен представлять собой как необработанный типа параметра и его параметризации, если он есть.

Так Джерси извлекает эти типы и подает их в ваш MessageBodyReader.Ожидается, что ваша реализация должна иметь возможность обрабатывать genericType.

Я не знаю Джерси, что хорошо, но если она будет следовать той же стратегии, что и Spring MVC, она будет просто перебирать все зарегистрированные MessageBodyReader экземпляры. Он действительно не заботится о своем типе (например, ваш TransactionMessageBodyReader), поэтому он полагается на isReadable. Теоретически это может привести к отображению типов в версии MessageBodyReader, но это становится громоздким в конце и менее гибким. В любом случае, как правило, в приложении не так много типов/форматов, для которых он может сериализоваться, поэтому повторение нескольких объектов не является чем-то большим.


С Java 8, у вас есть доступ к Executable#getParameters (Method является подтипом Executable).


Вот пример того, что может сделать Джерси

public class StackOverflow { 
    public static void main(String[] args) throws Exception { 
     Method method = StackOverflow.class.getDeclaredMethod("method", List.class); 
     Type[] parameterTypes = method.getGenericParameterTypes(); 
     for (Type parameterType : parameterTypes) { 
      System.out.println(parameterType.getClass()); 
      System.out.println(parameterType); 
      ParameterizedType parameterizedType = (ParameterizedType) parameterType; 
      System.out 
        .println(parameterizedType.getRawType().getTypeName() + "<" + parameterizedType.getActualTypeArguments()[0].getTypeName() + ">"); 
     } 
    } 

    public String method(List<StackOverflow> parameter) { 
     return "example"; 

    } 
} 

отпечатки

class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl 
java.util.List<com.example.StackOverflow> 
java.util.List<com.example.StackOverflow> 
+0

Спасибо. Итак, если Джерси может «видеть» все типы методов, зачем мне вообще писать MessageBodyReader? Без этого Джерси, похоже, не знает параметры типового типа. Другие плакаты в этом обсуждении сказали, что библиотеки используют трюки, такие как анонимные классы, для обнаружения параметров типа. Таким образом, это не так просто, как просто «он находится в файле класса, поэтому он доступен». – KyleM

+0

@KyleM Прочитайте тип и сделайте с ним что-нибудь? У Джерси может быть реализация по умолчанию для обработки JSON, XML и стандартных форматов. Но как только вам нужно что-то более сложное или менее распространенное, вам нужна ваша собственная реализация. –

+0

Моя точка зрения заключается в том, что из метода checkStatus(), который я опубликовал выше, Джерси не знает, что такое параметр типового типа. Следовательно, необходимость MessageBodyReader ... – KyleM