Я пытаюсь свести к минимуму повторяющийся код для ряда обработчиков ресурсов JAX-RS, каждый из которых требует несколько одинаковых путей и параметров запроса.Шаблон базового URL для каждого ресурса выглядит следующим образом:
/{id}/resourceName
, и каждый ресурс имеет несколько подресурсов:
/{id}/resourceName/subresourceName
Таким образом, пути к ресурсам / подресурсам (включая параметры запроса) могут выглядетькак
/12345/foo/bar?xyz=0
/12345/foo/baz?xyz=0
/12345/quux/abc?xyz=0
/12345/quux/def?xyz=0
Общими частями ресурсов foo
и quux
являются @PathParam("id")
и @QueryParam("xyz")
.Я мог бы реализовать классы ресурсов следующим образом:
// FooService.java
@Path("/{id}/foo")
public class FooService
{
@PathParam("id") String id;
@QueryParam("xyz") String xyz;
@GET @Path("bar")
public Response getBar() { /* snip */ }
@GET @Path("baz")
public Response getBaz() { /* snip */ }
}
// QuuxService.java
@Path("/{id}/quux")
public class QuxxService
{
@PathParam("id") String id;
@QueryParam("xyz") String xyz;
@GET @Path("abc")
public Response getAbc() { /* snip */ }
@GET @Path("def")
public Response getDef() { /* snip */ }
}
Мне удалось избежать повторения внедрения параметра в каждый метод get*
. 1 Это хорошее начало, но я также хотел бы избежать повторения для всех классов ресурсов.Подход, который работает с CDI (который мне также нужен), заключается в использовании базового класса abstract
, который FooService
и QuuxService
может extend
:
// BaseService.java
public abstract class BaseService
{
// JAX-RS injected fields
@PathParam("id") protected String id;
@QueryParam("xyz") protected String xyz;
// CDI injected fields
@Inject protected SomeUtility util;
}
// FooService.java
@Path("/{id}/foo")
public class FooService extends BaseService
{
@GET @Path("bar")
public Response getBar() { /* snip */ }
@GET @Path("baz")
public Response getBaz() { /* snip */ }
}
// QuuxService.java
@Path("/{id}/quux")
public class QuxxService extends BaseService
{
@GET @Path("abc")
public Response getAbc() { /* snip */ }
@GET @Path("def")
public Response getDef() { /* snip */ }
}
Внутри методов get*
инъекция CDI (чудесным образом) работает правильно: поле util
не равно нулю.К сожалению, инъекция JAX-RS работает , а не ;id
и xyz
равны null
в get*
методах FooService
и QuuxService
.
Есть ли исправление или обходной путь для этой проблемы?
Учитывая, чтоCDI работает так, как мне хотелось бы, мне интересно, является ли сбой при внедрении @PathParam
s (и т. д.) в подклассы ошибкой или просто частью спецификации JAX-RS.
Другой подход, который я уже попробовал, - это использование BaseService
в качестве единой точки входа, которая делегирует FooService
и QuuxService
по мере необходимости.Это в основном как описано в RESTful Java с JAX-RS с использованием локаторов подресурсов.
// BaseService.java
@Path("{id}")
public class BaseService
{
@PathParam("id") protected String id;
@QueryParam("xyz") protected String xyz;
@Inject protected SomeUtility util;
public BaseService () {} // default ctor for JAX-RS
// ctor for manual "injection"
public BaseService(String id, String xyz, SomeUtility util)
{
this.id = id;
this.xyz = xyz;
this.util = util;
}
@Path("foo")
public FooService foo()
{
return new FooService(id, xyz, util); // manual DI is ugly
}
@Path("quux")
public QuuxService quux()
{
return new QuuxService(id, xyz, util); // yep, still ugly
}
}
// FooService.java
public class FooService extends BaseService
{
public FooService(String id, String xyz, SomeUtility util)
{
super(id, xyz, util); // the manual DI ugliness continues
}
@GET @Path("bar")
public Response getBar() { /* snip */ }
@GET @Path("baz")
public Response getBaz() { /* snip */ }
}
// QuuxService.java
public class QuuzService extends BaseService
{
public FooService(String id, String xyz, SomeUtility util)
{
super(id, xyz, util); // the manual DI ugliness continues
}
@GET @Path("abc")
public Response getAbc() { /* snip */ }
@GET @Path("def")
public Response getDef() { /* snip */ }
}
недостатком этого подхода является то, что ни инъекция CDI, ни инъекция JAX-RS не работают в подресурсных классах.Причина этого довольно очевидна 2 , но то, что означает , заключается в том, что мне нужно вручную повторно вводить поля в конструктор подклассов, что является грязным, уродливым иПозвольте мне настроить дальнейшую инъекцию.Пример: скажем, я хотел @Inject
экземпляр в FooService
, но не QuuxService
.Поскольку я явно создаю экземпляр для подклассов BaseService
, внедрение CDI не будет работать, поэтому уродство продолжается.
tl; dr Как правильно избегать многократного внедрения полей через JAX-Классы обработчиков ресурсов RS?
И почему JAX-RS не вводит унаследованные поля, в то время как у CDI нет проблем с этим?
Редактировать 1
Снемного направления от @ Tarlog , я думаю, что нашел ответ на один из моих вопросов,
Почему JAX-RS не вводит унаследованные поля?
In JSR-311 §3.6 :
Если у подкласса или метода реализации есть какие-либо аннотации JAX-RS, тогда all аннотации к суперклассу или интерфейсному методу игнорируются.
Я уверен, что есть реальная причина для этого решения, но, к сожалению, этот факт работает против меня в данном конкретном случае использования.Я все еще интересуюсь любыми возможными обходными путями.
1 Предостережение с использованием внедрения на уровне поля состоит в том, что я теперь привязан к классу ресурсов для каждого запросареализации, но я могу жить с этим.
2 Потому что я тот, кто вызывает new FooService()
, а не контейнер / реализацию JAX-RS.