我找不到任何真实的使用示例,甚至除了在实现上使用@Provider注释之外,还可以找到它.在泽西岛看似写的人在一些帖子中暗示,这足以让它拾起.
注意:我正在Glassfish 3.1中运行,并使用Spring 3.1.似乎合理的是,Spring可能会以某种方式接管供应商的自动加载.但是,我只是不知道.我没有使用Spring来管理下面的建议的InjectableProvider,也没有尝试以其他方式添加它,这可能是我的问题.
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()); } }
基本实施:
import javax.ws.rs.ext.Provider; @Component // <- Spring Annotation @Provider // <- Jersey Annotation public class MyTypeAttributeInjectableProvider extends AbstractAttributeInjectableProvider<MyType> { public MyTypeAttributeInjectableProvider() { super(MyType.class); } }
参考文献注释:
@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(); }
更新:calvinkrishy指出了我的想法的两个缺陷.
首先,我假设泽西岛将在传统的Jersey-Spring servlet被启动之后开始扫描@Providers:com.sun.jersey.spi.spring.container.servlet.SpringServlet.这大概是错误的它确实开始扫描,但它查找具有注释的Spring bean.
其次,我假设PerRequestTypeInjectableProvider将被询问每个传入的请求,一个可注入来处理它控制的注释.这也是错的. PerRequestTypeInjectableProvider在启动时按预期进行实例化,但Jersey立即要求Injectable处理给定类型的给定注释,该给定的注释通过扫描Restful Services来确定它在此时决定管理(也就是说,他们都是这样).
PerRequestTypeInjectableProvider和SingletonTypeInjectableProvider之间的区别似乎是所产生的Injectable或者包含值,而不是为它工作(单例),或者每次查找该值(每个请求),从而使该值可以根据请求更改.
通过强制我在我的AttributeInjectable(下面的代码)中做一些额外的工作,而不是按照我计划的方式传递一些对象,以避免给予AttributeInjectable额外的知识,从而将一个较小的扳手投入到我的计划中.
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(); } }
我希望能够从提供者传递HttpServletRequest,但是AttributeInjectable只能按照唯一的注释/类型进行实例化.由于我无法做到这一点,我使用了Spring的RequestContextFilter单例,它提供了一个ThreadLocal机制,用于安全地检索HttpServletRequest(与当前请求相关的其他内容).
<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>
结果确实有效,并且它使得代码更加可读,而不会强制各种服务扩展一个基类,只是为了隐藏@Context HttpServletRequest请求的使用,然后用于通过某些帮助方法完成上述的访问属性.
那么你可以做一些这样的事情:
@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 } }
这变得非常方便,因为我使用Servlet过滤器来确保用户在传递数据之前具有访问服务的适当权限,然后我可以解析传入的数据(或加载它或任何)并将其转储到属性中要加载
如果您不想要上述Provider方法,并且您希望基类访问属性,则可以在此处:
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:我想到了我的AbstractAttributeInjectableProvider的实现,它本身就是一个通用类,它只存在为给定类型提供AttributeInjectable,Class< T>和提供的AttributeParam.提供与每个请求的AttributeParam类型(Class< T>)告知的非抽象实现非常简单,从而避免了一系列构造函数实现,为您提供类型.这也避免了为要用于AttributeParam注释的每一种类型编写代码.
@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()); } }
注意:每个Injectable在启动时实例化一次,而不是每个请求,但是在每个传入请求时都会调用它们.
解决方法
我会假设你正在使用泽西使用泽西弹簧servlet.在这种情况下,泽西将默认使用Spring bean初始化,因此您的Provider必须是一个Spring bean.尝试添加一个@Named(或者如果您不使用atinject @Component或Spring annotaions之一)到您的Provider.
An example of using Injectable Providers.
更新:注射范围更清晰:
提供商必须是一个单身人士,因为所有实际的目的,它的一个工厂的范围与它,并且没有必要建立一个工厂的每一个请求.注射本身会根据要求进行.换句话说,将为每个请求调用getInjectable方法.你有机会尝试吗?
OTOH,如果您扩展SingletonTypeInjectableProvider,每次都会将相同的对象注入到您的资源中.
我不知道我完全了解您的提供商实施.我相信像下面这样的东西应该有效.
public class UserProvider extends PerRequestTypeInjectableProvider<AttributeParam,Users>{ public UserProvider(){ super(Users.class); } @Context HttpServletRequest request; @Override public Injectable<Users> getInjectable(ComponentContext cc,AttributeParam a) { String attributeValue = AnnotationUtils.getValue(a); return new Injectable<Users>(){ public Users getValue() { System.out.println("Called"); //This should be called for each request return request.getAttribute(attributeValue); } }; } }
更新:提供更多关于泽西州注射类型和上下文的信息.
正如你现在可能想到的那样,如果您需要的只是访问HttpServletRequest,那么只需使用@Context注释直接将其注入到您的资源或提供程序中即可.
但是,要将这些值传递给可注入的值,必须使用AssistedProvider或使用类似于您的方法.但是,再次,如果您在提供程序中嵌入可注入的定义并将HttpServletRequest注入到Provider类中,那么您可以减轻这一点.在这种情况下,Injectable将能够访问HttpServletRequest实例(因为它在范围内).我只是更新了我的例子来表明这种方法.
使用PerRequestTypeInjectableProvider和SingletonTypeInjectableProvider进行注入不是将值注入到资源中的唯一两个选项.您也可以使用StringReaderProvider注入* Param值.显然这样的注射是要求范围.
@Provider @Named("userProviderParamInjector") public class UserProviderParam implements StringReaderProvider<Users> { @Context HttpServletRequest request; public StringReader<Users> getStringReader(Class<?> type,Type type1,Annotation[] antns) { if(type.equals(Users.class) { return null; } String attributeValue = null; for(Annotation a : antns) { if((a.getClass().getSimpleName()).equals("AttributeParam")){ attributeValue = (String)AnnotationUtils.getValue(a); } } return new StringReader<Users>(){ public Users fromString(String string) { // Use the value of the *Param or ignore it and use the attributeValue of our custom annotation. return request.getAttribute(attributeValue); } }; } }
您的资源中的任何* Param将调用此提供程序.因此,如上所述的提供商,以及如下所述的资源,Users值将被注入到您的资源方法中.
@Path("/user/") @Named public class UserResource { @Path("{id}") @GET @Produces(MediaType.APPLICATION_JSON) public Result<Users> get(@AttributeParam("foo") @PathParam("id") Users user) { ... } }
但是说实话,我认为这是滥用StringReaderProvider合同,而使用Injectable的前一种技术感觉更清洁.