Остаться DRY с JAX-RS

Я пытаюсь свести к минимуму повторяющийся код для ряда обработчиков ресурсов 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 (который мне также нужен), заключается в использовании абстрактного базового класса, который FooService и QuuxService может расширять :

// 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 и [1115613760] 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?

В JSR-311 §3.6 :

Если подкласс или метод реализации имеет какие-либо аннотации JAX-RS, то все из аннотации суперкласса или метода интерфейса игнорируются.

Я уверен, что для этого решения есть реальная причина, но, к сожалению, этот факт работает против меня в данном конкретном случае использования. Меня все еще интересуют возможные обходные пути.


1 Предостережение при использовании инъекции на уровне поля состоит в том, что теперь я привязан к созданию экземпляров класса ресурсов для каждого запроса, но я могу с этим жить.
2 Потому что я в игровой зоне случайным образом расположены связки звезд. Есть три типа звезд: большие звезды быстрее, меньшие звезды движутся медленнее.

Это выглядит так:

ScreenShot1

~ 10% времени все звезды появляются в «полосах» ScreenShot2

Думаю, стоит упомянуть, что даже при том, что они в узких полосах; они не все в одном и том же положении. Это похоже на то, что он все еще генерирует случайное число - только крошечное.

Чтобы воспроизвести ошибку, я просто нажимаю «f5» в браузере. Практически всегда он работает, как ожидалось. Редко получаются полосы. Повторное нажатие «f5» решит проблему.

Без публикации гигантской стены кода; Я считаю, что это наиболее актуальный код. Похоже, в базовом классе наследуются все мои звезды. Он вызывается один раз, когда создается каждая звезда.

Protected Sub SetInitialPosition()
    myElipse.Height = GetStarSize()
    myElipse.Width = GetStarSize()

    _location.X = GetRandom.Next(-1 * Settings.StarEdge, CType(GameCanvas.Width, Integer) + Settings.StarEdge)
    _location.Y = GetRandom.Next(0, CType(GameCanvas.Height, Integer))

    myElipse.Fill = New SolidColorBrush(GetStarColor)

End Sub

Я не вижу ничего плохого. GetRandom () возвращает одноэлементный класс Random, и я зависим от допустимости GameCanvas.Height и GameCanvas.Width, но опять же, .Width, похоже, работает именно так, как ожидалось.

Есть ли у кого-нибудь возможное объяснение такого поведения? Есть ли какие-то ошибки, на которые следует обратить внимание при генерации случайных чисел? Каждый раз, когда я просматриваю код, все в порядке, и игра работает должным образом.

Если это поможет, я могу опубликовать ссылку на игру.
( http://robdude.weebly.com/cci.html )

ИЗМЕНИТЬ №1:
Вот код из GetRandom ()

Protected Shared Function GetRandom() As Random
    If _random Is Nothing Then _random = New Random()

    Return _random
End Function

ИЗМЕНИТЬ №2: Я очень ценю все мысли / советы по этому поводу.

10
задан Rob P. 4 May 2011 в 16:17
поделиться