Реализация службы GWT RequestFactory для запросов не связанных с сущностью
У меня есть следующий Java-сервлет, который выполняет то, что я называю "Addition Service":
public class AdditionService extends HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
// The request will have 2 Integers inside its body that need to be
// added together and returned in the response.
Integer addend = extractAddendFromRequest(request);
Integer augend = extractAugendFromRequest(request);
Integer sum = addend + augend;
PrintWriter writer = response.getWriter();
writer.write(sum);
}
}
Я пытаюсь заставить GWT RequestFactory сделать то же самое (добавить два числа на сервер приложений и вернуть сумму в качестве ответа), используя ValueProxy
а также AdditionService
и сталкиваюсь с несколькими проблемами.
Вот AdditionRequest
(уровень клиента), который представляет собой объект значения, содержащий два целых числа, которые необходимо добавить:
// Please note the "tier" (client, shared, server) I have placed all of my Java classes in
// as you read through the code.
public class com.myapp.client.AdditionRequest {
private Integer addend;
private Integer augend;
public AdditionRequest() {
super();
this.addend = 0;
this.augend = 0;
}
// Getters & setters for addend/augend.
}
Следующий мой прокси (клиентский уровень):
@ProxyFor(value=AdditionRequest.class)
public interface com.myapp.client.AdditionRequestProxy extends ValueProxy {
public Integer getAddend();
public Integer getAugend();
public void setAddend(Integer a);
public void setAugend(Integer a);
}
Далее мой сервис API (на общем уровне):
@Service(value=DefaultAdditionService.class)
public interface com.myapp.shared.AdditionService extends RequestContext {
Request<Integer> sum(AdditionRequest request);
}
Следующая фабрика моих запросов (общий уровень):
public class com.myapp.shared.ServiceProvider implements RequestFactory {
public AdditionService getAdditionService() {
return new DefaultAdditionService();
}
// ... but since I'm implementing RequestFactory, there's about a dozen
// other methods GWT is forcing me to implement: find, getEventBus, fire, etc.
// Do I really need to implement all these?
}
Наконец, где происходит волшебство (уровень сервера):
public class com.myapp.server.DefaultAdditionService implements AdditionService {
@Override
public Request<Integer> sum(AdditionRequest request) {
Integer sum = request.getAddend() + request.getAugend();
return sum;
}
// And because AdditionService extends RequestContext there's another bunch of
// methods GWT is forcing me to implement here: append, create, isChanged, etc.
// Do I really need to implement all these?
}
Вот мои вопросы:
- Правильна ли моя "уровневая" стратегия? Я упаковал все типы в правильных клиентских / общих / серверных пакетах?
- Я не думаю, что мои настройки правильны, потому что
AdditionService
(в общих) ссылкахDefaultAdditionService
, который находится на сервере, что он не должен делать. Общие типы должны быть в состоянии жить как на клиенте, так и на сервере, но не должны зависеть ни от одного из них...
- Я не думаю, что мои настройки правильны, потому что
- Должен
ServiceProvider
быть классом, который реализуетRequestFactory
или это должен быть интерфейс, расширяющий его? Если последнее, где я могу определитьServiceProvider
импл, а как мне связать его со всеми этими другими классами? - Как насчет всех этих методов в
ServiceProvider
а такжеDefaultAdditionService
? Нужно ли реализовывать все 20+ этих основных методов GWT? Или я использую API неправильно или не так просто, как мог бы его использовать? - Где здесь указатель услуг? Как?
Я думаю, что я близко, но мне нужна помощь, чтобы пересечь финишную черту здесь. Если бы кто-то мог взять мой код и настроить его, чтобы показать правильный способ использования RF/ValueProxies, я думаю, что это связало бы всю основу для меня... заранее спасибо!
1 ответ
Если вы хотите использовать RF как простой механизм RPC [*], вы можете (и вы правы: только ValueProxy
s), но вам нужно нечто большее: ServiceLocator
s (т. е. GWT 2.1.1).
С ServiceLocator
вы можете просто поместить реализацию вашей службы (например, ваш сервлет) в реальный экземпляр службы, а не в объект сущности (так как вы будете использовать только ValueProxy
с, без статического getXyz()
методы) в соответствии с требованиями протокола РФ. Обратите внимание на существование также Locator
s, используется для вывода всех этих методов из ваших серверных объектов: не требуется, если вы используете ValueProxy
везде.
ServiceLocator
выглядит примерно так (взято из официальных документов):
public class DefaultAdditionServiceLocator implements ServiceLocator {
@Override
public Object getInstance(Class<?> clazz) {
try {
return clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Вы должны аннотировать свой DefaultAdditionService
также с параметром локатора, так что RF знает, на что положиться, когда дело доходит до отправки вашего запроса к вашим услугам. Что-то вроде:
@Service(value = DefaultAdditionService.class, locator = DefaultAdditionServiceLocator.class)
public interface com.myapp.shared.AdditionService extends RequestContext {
// Note here, you need to use the proxy type of your AdditionRequest.
Request<Integer> sum(AdditionRequestProxy request);
}
Тогда ваш сервис станет самой простой вещью на Земле (не нужно расширять / реализовывать что-либо, связанное с РЧ):
public class com.myapp.server.DefaultAdditionService {
// The server-side AdditionRequest type.
public Integer sum(AdditionRequest request) {
Integer sum = request.getAddend() + request.getAugend();
return sum;
}
}
Если вы ошиблись sum()
или вы не реализуете метод, объявленный в вашем RequestContext
вы получите ошибку.
Для создания экземпляра RequestContext
s вам нужно продлить RequestFactory
интерфейс, с общедоступным методом фабрики для com.myapp.shared.AdditionService
, Что-то вроде:
public interface AdditionServiceRequestFactory extends RequestFactory {
public com.myapp.shared.AdditionService createAdditionServiceRequestContext();
}
Все ваши клиентские звонки начнутся с этого. Смотрите документы, если не уже.
Теперь RF работает, полностью отделяя объекты, которые вы хотите передать от клиента (используя EntityProxy
а также ValueProxy
) и сервер (реальные объекты, либо Entity
значения или просто DTO
классы). Вы будете использовать прокси-типы (т. Е. Интерфейсы, для которых автоматически создаются реализации) везде на уровне client / shared, и вы будете использовать относительный объект домена (тот, на который ссылается @ProxyFor
) только на стороне сервера. РФ позаботится обо всем остальном. Так что ваши AdditionRequest
будет на вашей стороне сервера, в то время как AdditionRequestProxy
будет на вашей стороне клиента (см. примечание в RequestContext
). Также обратите внимание, что если вы просто используете примитивные / коробочные типы в качестве RequestContext
параметры или возвращаемые типы, вам даже не нужно создавать ValueProxy
s вообще, так как они переносимы по умолчанию.
Последнее, что вам нужно, это подключить RequestFactoryServlet
на ваше web.xml
, Смотрите документы здесь. Обратите внимание, что вы можете расширить его, если хотите, скажем, поиграть с ExceptionHandler
с или ServiceLayerDecorator
с, но вам не нужно.
Говоря о том, куда все положить:
Locator
s,ServiceLocator
s, экземпляры служб, доменные объекты иRequestFactoryServlet
расширения, будут на вашей стороне сервера;-
RequestContext
,RequestFactory
расширения и все ваши типы прокси будут на общей стороне; - клиентская сторона инициализирует
RequestFactory
расширение и использовать его для получения экземпляра фабрики для ваших запросов на обслуживание.
В общем... чтобы создать простой механизм RPC с RF, просто:
- создать свой сервис вместе с
ServiceLocator
; - создать
RequestContext
для ваших запросов (помечены значениями сервиса и локатора); - создать
RequestFactory
расширение, чтобы вернуть вашRequestContext
; - если вы хотите использовать больше, чем примитивные типы в вашем
RequestContext
(вроде простоDTO
s), просто создайте для них клиентские прокси-интерфейсы с пометкой@ProxyFor
и помните, где использовать каждый тип; - провод все.
Во многом так. Хорошо, я написал слишком много и, вероятно, что-то забыл:)
Для справки смотрите:
- Официальная документация РФ;
- Статьи Томаса Бройера [1], [2];
- RF против GWT-RPC с точки зрения автора РФ.
[*]: При таком подходе вы переносите логику с ориентированных на данные приложений на сервисы. Вы отказываетесь от использования Entity
s, ID
s, version
s и, конечно, вся сложная логика сравнения между клиентом и сервером, когда дело доходит до CRUD
операции.