Jersey: InjectableProvider nicht abgeholt - Frühling
Ich bin versucht derzeit, erstellen Sie eine InjectableProvider
mit Jersey, aber ich Schaffe es nicht, Jersey, um ihn abzuholen.
Ich finde keine wirklichen Beispiele für seine Verwendung, oder sogar, wie man es nahm neben der Verwendung der @Provider
- Anmerkung für die Umsetzung. Die person, die scheinbar schrieb er in Jersey implizit in manchen Beiträgen, dass dies genug ist, um es abgeholt.
Muss ich angeben, einige SPI-service-Datei, oder fügen Sie es, um eine Fabrik irgendwo?
Hinweis: ich verwende in Glassfish 3.1, und mit Spring 3.1. Es scheint vernünftig, dass der Frühling vielleicht irgendwie die übernahme der für das automatische laden der Provider
s. Aber ich weiß einfach nicht. Ich bin nicht mit Feder eh zu verwalten, die vorgeschlagenen InjectableProvider unten, noch will ich hinzufügen, dass es in irgendeiner anderen Art und Weise, das kann-auch mein problem.
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider;
public abstract class AbstractAttributeInjectableProvider<T>
extends PerRequestTypeInjectableProvider<AttributeParam, T>
{
protected final Class<T> type;
public AbstractAttributeInjectableProvider(Class<T> type)
{
super(type);
this.type = type;
}
@Override
public Injectable<T> getInjectable(ComponentContext componentContext,
AttributeParam attributeParam)
{
return new AttributeInjectable<T>(type, attributeParam.value());
}
}
Grundlegende Implementierung:
import javax.ws.rs.ext.Provider;
@Component //<- Spring Annotation
@Provider //<- Jersey Annotation
public class MyTypeAttributeInjectableProvider
extends AbstractAttributeInjectableProvider<MyType>
{
public MyTypeAttributeInjectableProvider()
{
super(MyType.class);
}
}
Referenz Annotation
:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AttributeParam
{
/**
* The value is the name to request as an attribute from an {@link
* HttpContext}'s {@link HttpServletRequest}.
* @return Never {@code null}. Should never be blank.
*/
String value();
}
Referenz link aus Jersey Entwickler.
UPDATE: calvinkrishy wies darauf hin zwei Fehler, mein denken.
Zuerst bin ich davon ausgegangen, dass Jersey Los war, kick-off-scanning für @Provider
s nach dem Startschuss durch den traditionellen Jersey-Spring-servlet: com.sun.jersey.spi.spring.container.servlet.SpringServlet
. Dies war vor allem falsch; es tut, starten Sie das scanning, aber es sieht für die Spring-Bean, die die annotation.
Zweiten bin ich davon ausgegangen, dass die PerRequestTypeInjectableProvider
gefragt würde, auf jede eingehende Anfrage für ein Injectable
Griff die Anmerkung, dass es steuert. Auch das war falsch. Die PerRequestTypeInjectableProvider
instanziiert wird beim Start, wie erwartet, aber Jersey, dann fragt sofort für Injectable
's zum behandeln der gegebenen annotation mit dem gegebenen type
, die er bestimmt, durch das Scannen der Restful-Dienste, die es hat-an dieser Stelle-entschieden, dass es ihm gelingt (und das ist zu sagen, alle von Ihnen).
Den Unterschied zwischen den PerRequestTypeInjectableProvider
und SingletonTypeInjectableProvider
scheint zu sein, dass die daraus resultierenden Injectable
enthält entweder den Wert ohne die für Sie tätig sind (singleton), oder sucht er es jedes mal um den Wert (pro Antrag), so dass der Wert änderung pro Anfrage.
Dieser warf ein kleiner Schraubenschlüssel in meine Pläne durch und zwingt mich zu tun, einige zusätzliche Arbeit in meiner AttributeInjectable
(code unten) anstatt in einigen Objekten, als ich geplant hatte, zu verhindern, dass die AttributeInjectable
zusätzliches wissen.
public class AttributeInjectable<T> implements Injectable<T>
{
/**
* The type of data that is being requested.
*/
private final Class<T> type;
/**
* The name to extract from the {@link HttpServletRequest} attributes.
*/
private final String name;
/**
* Converts the attribute with the given {@code name} into the {@code type}.
* @param type The type of data being retrieved
* @param name The name being retrieved.
* @throws IllegalArgumentException if any parameter is {@code null}.
*/
public AttributeInjectable(Class<T> type, String name)
{
//check for null
//required
this.type = type;
this.name = name;
}
/**
* Look up the requested value.
* @return {@code null} if the attribute does not exist or if it is not the
* appropriate {@link Class type}.
* <p />
* Note: Jersey most likely will fail if the value is {@code null}.
* @throws NullPointerException if {@link HttpServletRequest} is unset.
* @see #getRequest()
*/
@Override
public T getValue()
{
T value = null;
Object object = getRequest().getAttribute(name);
if (type.isInstance(object))
{
value = type.cast(object);
}
return value;
}
/**
* Get the current {@link HttpServletRequest} [hopefully] being made
* containing the {@link HttpServletRequest#getAttribute(String) attribute}.
* @throws NullPointerException if the Servlet Filter for the {@link
* RequestContextHolder} is not setup
* appropriately.
* @see org.springframework.web.filter.RequestContextFilter
*/
protected HttpServletRequest getRequest()
{
//get the request from the Spring Context Holder (this is done for
// every request by a filter)
ServletRequestAttributes attributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
return attributes.getRequest();
}
}
Hoffte ich, in der Lage sein pass in die HttpServletRequest
von der Provider
, aber die AttributeInjectable
wird nur instanziiert für jeden eindeutigen annotation/Typ. Als ich das nicht tun kann, ich tun, dass der pro-Wert-lookup, verwendet Spring RequestContextFilter
singleton, die eine ThreadLocal
Mechanismus zum sicheren abrufen der HttpServletRequest
(unter anderem im Zusammenhang mit der aktuellen Anfrage).
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>
org.springframework.web.filter.RequestContextFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/path/that/i/wanted/*</url-pattern>
</filter-mapping>
Das Ergebnis funktioniert, und es macht den code sehr viel besser lesbar, ohne dass die verschiedenen Dienstleistungen zu erweitern, eine Basisklasse, die nur zu verbergen, die Verwendung von @Context HttpServletRequest request
, die dann verwendet werden, um Zugriff auf die Attribute wie oben durch einige Helfer-Methode.
Dann können Sie tun, etwas entlang der Linien von:
@Path("my/path/to")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public interface MyService
{
@Path("service1")
@POST
Response postData(@AttributeParam("some.name") MyType data);
@Path("service2")
@POST
Response postOtherData(@AttributeParam("other.name") MyOtherType data);
}
@Component //Spring
public class MyServiceBean implements MyService
{
@Override
public Response postData(MyType data)
{
//interact with data
}
@Override
public Response postOtherData(MyOtherType data)
{
//interact with data
}
}
Dieser wird sehr bequem, wie ich Sie verwenden einen Servlet-Filter, um sicherzustellen, dass der Benutzer die entsprechenden Berechtigungen zum Zugriff auf den service vor der übergabe der Daten, und dann kann ich analysieren die eingehenden Daten (oder laden Sie es, oder was auch immer) und werfen Sie es in das Attribut geladen werden.
Wenn Sie nicht möchten, dass die oben Provider
Ansatz, und Sie möchten, dass die Basisklasse für den Zugriff auf die Attribute, dann hier gehen Sie:
public class RequestContextBean
{
/**
* The current request from the user.
*/
@Context
protected HttpServletRequest request;
/**
* Get the attribute associated with the current {@link HttpServletRequest}.
* @param name The attribute name.
* @param type The expected type of the attribute.
* @return {@code null} if the attribute does not exist, or if it does not
* match the {@code type}. Otherwise the appropriately casted
* attribute.
* @throws NullPointerException if {@code type} is {@code null}.
*/
public <T> T getAttribute(String name, Class<T> type)
{
T value = null;
Object attribute = request.getAttribute(name);
if (type.isInstance(attribute))
{
value = type.cast(attribute);
}
return value;
}
}
@Path("my/path/to")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public interface MyService
{
@Path("service1")
@POST
Response postData();
@Path("service2")
@POST
Response postOtherData();
}
@Component
public class MyServiceBean extends RequestContextBean implements MyService
{
@Override
public Response postData()
{
MyType data = getAttribute("some.name", MyType.class);
//interact with data
}
@Override
Response postOtherData()
{
MyOtherType data = getAttribute("other.name", MyOtherType.class);
//interact with data
}
}
UPDATE2: ich dachte über meine Umsetzung der AbstractAttributeInjectableProvider
, der selbst eine generische Klasse, die nur existiert, um zu bieten AttributeInjectable
's für einen bestimmten Typ, der Class<T>
und der mitgelieferten AttributeParam
. Es ist viel einfacher, um eine nicht-abstract
Implementierung, die gesagt wird, seinen Typ (Class<T>
) mit den jeweils angeforderten AttributeParam
, so dass eine Reihe von Konstruktor-nur Implementierungen, die den Typ für Sie. Dies auch vermeiden, schreiben Sie den code für jede einzelne Art, die Sie verwenden möchten, mit der AttributeParam
annotation.
@Component
@Provider
public class AttributeParamInjectableProvider
implements InjectableProvider<AttributeParam, Type>
{
/**
* {@inheritDoc}
* @return Always {@link ComponentScope#PerRequest}.
*/
@Override
public ComponentScope getScope()
{
return ComponentScope.PerRequest;
}
/**
* Get an {@link AttributeInjectable} to inject the {@code parameter} for
* the given {@code type}.
* @param context Unused.
* @param parameter The requested parameter
* @param type The type of data to be returned.
* @return {@code null} if {@code type} is not a {@link Class}. Otherwise
* an {@link AttributeInjectable}.
*/
@Override
public AttributeInjectable<?> getInjectable(ComponentContext context,
AttributeParam parameter,
Type type)
{
AttributeInjectable<?> injectable = null;
//as long as it's something that we can work with...
if (type instanceof Class)
{
injectable = getInjectable((Class<?>)type, parameter);
}
return injectable;
}
/**
* Create a new {@link AttributeInjectable} for the given {@code type} and
* {@code parameter}.
* <p />
* This is provided to avoid the support for generics without the need for
* {@code SuppressWarnings} (avoided via indirection).
* @param type The type of data to be returned.
* @param parameter The requested parameter
* @param <T> The type of data being accessed by the {@code param}.
* @return Never {@code null}.
*/
protected <T> AttributeInjectable<T> getInjectable(Class<T> type,
AttributeParam parameter)
{
return new AttributeInjectable<T>(type, parameter.value());
}
}
Hinweis: jeder Injectable
instanziiert wird einmal beim Start eher als pro Anfrage, aber Sie sind aufgerufen, auf jede eingehende Anfrage.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wie sind Sie initialisieren Jersey?
Ich davon aus, dass Sie mit Trikot mit der Trikot-spring-servlet. In dem Fall Jersey standardmäßig initialisieren mit Spring-beans und damit Ihre
Provider
hat eine Spring bean. Versuchen Sie, ein@Named
(oder wenn Sie nicht verwenden atinject@Component
oder eine der im Frühjahr) auf IhrerProvider
.Ein Beispiel für die Verwendung von Injizierbaren Anbieter.
Aktualisiert: Mehr Klarheit über den Umfang der Injektion:
Den
Provider
hat ein Singleton sein, als für alle praktischen Zwecke sein ein Werk mit einem Umfang gebunden und es gibt keine Notwendigkeit zum Bau einer Fabrik für jede Anforderung. Die Injektion selbst passieren würde, pro Anfrage. In anderen Worten, diegetInjectable
Methode aufgerufen werden würde, für jede Anforderung. Haben Sie eine chance, um zu versuchen, dass?OTOH, wenn Sie erweitern, die
SingletonTypeInjectableProvider
das gleiche Objekt wäre eingespritzt, Ihre Ressourcen zu jeder Zeit.Ich bin nicht sicher, ob ich völlig verstehen, Ihre
Provider
Umsetzung. Ich glaube, so etwas wie das folgende sollte funktionieren.Aktualisiert: mehr Informationen über die Injektion Typen und Kontexte zur Verfügung, die in Jersey.
Als Sie wahrscheinlich dachte, jetzt, wenn alles, was Sie brauchen, ist Zugang zu den
HttpServletRequest
dann einfach direkt injizieren Sie in IhreResource
oderProvider
mit der@Context
annotation erhalten Sie, dass.Allerdings geben diese Werte an die Injizierbare verwenden Sie eine
AssistedProvider
oder verwenden Sie einen ähnlichen Ansatz wie die ihrige. Aber wieder können Sie mildern, wenn Sie inline-IhreInjectable
definition in der Anbieter-und injizieren dieHttpServletRequest
in dieProvider
Klasse. In diesem Fall wird dieInjectable
Lage wären, Zugang zu denHttpServletRequest
Instanz (seit Ihr da in scope). Ich habe gerade aktualisiert, meinem Beispiel zu zeigen, dass Ansatz.Injektion mit
PerRequestTypeInjectableProvider
undSingletonTypeInjectableProvider
sind nicht die einzigen zwei Optionen, die Sie haben, um zu injizieren Werte in Ihren Ressourcen. Sie könnten auch Spritzen mit*Param
Werte mit einemStringReaderProvider
. Offensichtlich wie eine Injektion Anfrage beschränkt.Diese
Provider
würde aufgerufen werden, die für alle*Param
, die Sie in Ihrer Ressource. Also mit einemProvider
wie oben registriert und eine Ressource wie dem folgenden, derUsers
Wert wäre, injiziert in Ihre Ressource-Methode.Aber um ehrlich zu sein, ich halte dies für einen Missbrauch des StringReaderProvider Vertrag in der Erwägung, dass der ehemalige Technik der Verwendung von
Injectable
fühlt sich sauberer.PerRequestTypeInjectableProvider
singleton-scoped? Wenn ich es zwingen, in einen anforderungsbereich, es schlägt mit einer Ausnahme fehl. Das Beispiel war eigentlich die ursprüngliche Injizierbaren Anbieter Beispiel, dass mir auf diesem. Die code-Beispiele, die er bietet, sind nicht die besten (er verbindet dieProvider
mit derInjectable
). Es ist doof dass Jersey versucht zu abstrahieren derServletRequest
.Provider
ein singleton ist ein feature, sondern ein neuer Wert sollte für jede Anforderung.PerRequestTypeInjectableProvider
. Jersey sofort Konstrukte derInjectable
s, dass es entscheidet, dass es muss nach dem Scannen der verschiedenen Restful-Dienste, die es steuert. Ich nahm "PerRequest" bedeuten, dass dieProvider
würden überprüft, mit jeder Anforderung. Ich wollte dieProvider
zu injizieren, dieHttpServletRequest
in eine neueAttributeInjectable
(meine eigene Art) zu extrahieren, dieHttpServletRequest#getAttribute(String)
mit den Namen derAttributeParam
. So ist meine Restful-Services haben könnten Attribute injiziert sauber.HttpServletRequest
von derProvider
Schnittstelle, anstatt die viel weniger nützlichHttpContext
stellt, dass es zuInjectable
s.