J'essaie de minimiser code à répétition pour un certain nombre de JAX-RS gestionnaires de ressources, ce qui nécessite un peu de la même chemin d'accès et les paramètres de la requête. La base du modèle d'url pour chaque ressource ressemble à ceci:
/{id}/resourceName
et chaque ressource dispose de plusieurs sous-ressources:
/{id}/resourceName/subresourceName
Donc, ressource/sous-ressource chemins (incl. les paramètres de la requête) peut ressembler
/12345/foo/bar?xyz=0
/12345/foo/baz?xyz=0
/12345/quux/abc?xyz=0
/12345/quux/def?xyz=0
Les pièces communes aux différentes ressources foo
et quux
sont @PathParam("id")
et @QueryParam("xyz")
. J'ai pu mettre en œuvre les ressources des classes comme ceci:
// 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 */ }
}
J'ai réussi à éviter de répéter le paramètre d'injection dans chaque get*
méthode.1 C'est un bon début, mais je voudrais être en mesure d'éviter la répétition à travers les ressources de classes. Une approche qui fonctionne avec le CDI (dont j'ai aussi besoin d') est d'utiliser un abstract
classe de base qui FooService
et QuuxService
peut 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 */ }
}
À l'intérieur de l' get*
méthodes, le CDI injection (miraculeusement) fonctionne correctement: l' util
champ n'est pas nul. Malheureusement, JAX-RS injection n'a pas de travail; id
et xyz
sont null
dans la get*
méthodes FooService
et QuuxService
.
Est-il un correctif ou une solution de contournement pour ce problème?
Étant donné que le CDI fonctionne comme je le voudrais, je me demandais si le défaut d'injecter @PathParam
s (etc.) dans les sous-classes est un bug ou juste une partie de JAX-RS spec.
Une autre approche, j'ai déjà essayé à l'aide de BaseService
comme un point d'entrée unique que les délégués à l' FooService
et QuuxService
en tant que de besoin. C'est essentiellement comme décrit dans Reposante Java avec JAX-RS à l'aide de sous-ressource locators.
// 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 */ }
}
L'inconvénient de cette approche est que ni CDI injection, ni de JAX-RS des travaux d'injection dans le sous-ressource classes. La raison pour cela est assez évident2, mais ce que cela signifie , c'est que j'ai manuellement ré-injecter les champs dans la sous-classes " constructeur, qui est sale, moche, et ne se laisse pas facilement permettez-moi de personnaliser davantage l'injection. Exemple: dire que je voulais @Inject
d'une instance en FooService
mais pas QuuxService
. Parce que je suis instancier explicitement les sous-classes de BaseService
, CDI injection ne fonctionne pas, de sorte que la laideur est poursuivie.
tl;dr Quel est le bon chemin pour éviter les injections répétées de champs de JAX-RS ressource classes de gestionnaire?
Et pourquoi ne pas hérité des champs injecté par JAX-RS, tandis que la CDI n'a pas de problèmes avec cela?
Edit 1
Avec un peu de sens de @Tarlog, je crois que j'ai trouvé la réponse à une de mes questions,
Pourquoi ne sont pas héritées des champs injecté par JAX-RS?
Dans la JSR-311 §3.6:
Si une sous-classe ou méthode de mise en œuvre a tout de JAX-RS annotations puis tous les annotations sur la super-classe ou la méthode de l'interface sont ignorés.
Je suis sûr qu'il y a une vraie raison de cette décision, mais, malheureusement, le fait est contre moi dans ce cas d'utilisation particulier. Je suis toujours intéressé à toutes les solutions possibles.
1 La mise en garde avec l'aide du champ au niveau de l'injection, c'est que je suis maintenant lié par la demande de ressources de l'instanciation de classe, mais je peux vivre avec ça.
2 Parce que je suis le seul appelant new FooService()
plutôt que le contenant/JAX-RS de mise en œuvre.