我目前正在尝试InjectableProvider
使用Jersey 创建一个,但无法让Jersey接它。
除了@Provider
在实现中使用批注之外,我找不到任何实际用法示例,甚至找不到如何使用它。看似写在泽西岛上的人在某些帖子中暗示这足以使它被拾起。
我需要指定一些SPI服务文件,还是将其添加到某个工厂的某个地方?
注意:我正在Glassfish 3.1中运行,并使用Spring 3.1。Spring可能会以某种方式接管Provider
s
的自动加载似乎是合理的。但是,我只是不知道。我无论如何都没有使用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);
}
}
参考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();
}
来自Jersey开发人员的参考链接。
更新 :calvinkrishy指出了我思考的两个缺陷。
首先,我假设Jersey @Provider
在被传统的Jersey-Spring servlet:开始后将开始扫描s
com.sun.jersey.spi.spring.container.servlet.SpringServlet
。这主要是不正确的。它会开始扫描,但会查找具有注释的Spring
bean。
其次,我假设PerRequestTypeInjectableProvider
将在每个传入请求时要求,Injectable
以处理所控制的注释。这也是错的。在PerRequestTypeInjectableProvider
启动时被实例化,符合市场预期,但泽西然后立即请求Injectable
的处理给定的注释与给定的type
,它决定通过扫描RESTful服务,它有-
在这一点上-决定它管理(这也就是说,所有这些)。
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
从Provider
,但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 request
,使代码更具可读性,而该类仅用于隐藏的用法,然后按上述方法通过一些辅助方法将其用于访问属性。
然后,您可以执行以下操作:
@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
。为每个请求提供一个abstract
告知其类型(Class<T>
)的非实现要容易得多AttributeParam
,从而避免了一堆只为构造函数提供类型的实现。这也避免了必须为要与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
实例在启动时都会实例化一次,而不是每个请求实例化一次,但是会在每个传入请求时调用它们。
您如何初始化Jersey?
我假设您正在使用Jersey-spring Servlet使用Jersey。在这种情况下,Jersey将默认使用Spring
Bean进行初始化,因此您Provider
必须是Spring Bean。尝试在中添加一个@Named
(或如果不使用atinject
@Component
或Spring注释之一)Provider
。
使用Injectable Providers的示例。
更新 :注射范围更清晰:
它Provider
必须是一个Singleton,因为出于所有实际目的,它是一个与范围相关联的工厂,因此无需为每个请求都构造一个工厂。注入本身将根据请求进行。换句话说,getInjectable
将为每个请求调用该方法。您有机会尝试吗?
OTOH,如果扩展SingletonTypeInjectableProvider
相同的对象,则每次都会将其注入资源中。
我不确定我是否完全理解您的Provider
实现。我相信类似以下的方法应该起作用。
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);
}
};
}
}
已更新 :提供有关Jersey可用注射类型和上下文的更多信息。
正如您现在可能已经想到的那样,如果HttpServletRequest
仅需要访问,则直接将其注入到您的文档中Resource
或Provider
使用@Context
注释即可得到。
但是,要将这些值传递给Injectable必须使用AssistedProvider
或使用与您相似的方法。但是,如果您Injectable
在Provider中内联定义并将其注入HttpServletRequest
到Provider
类中,则可以再次缓解这种情况。在那种情况下,Injectable
它将能够访问HttpServletRequest
实例(因为它在范围内)。我刚刚更新了示例以显示该方法。
使用PerRequestTypeInjectableProvider
和SingletonTypeInjectableProvider
并不是唯一将值注入资源的两个选项。您还可以使用*Param
值注入值StringReaderProvider
。显然,这种注入是请求范围内的。
@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);
}
};
}
}
Provider
对于*Param
资源中拥有的任何资源,将调用此方法。因此,使用Provider
上面注册的资源和下面注册的资源,该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
感觉更干净。
如spring boot博客所述 我尝试自定义我的对象序列化。 在我的配置中添加了一个新的配置bean之后 当我尝试输出类用户的实例时,json结果不在CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES中 也许我需要在我的Jersey配置中注册一些东西来激活我的自定义obejctMapper配置 谢谢
问题内容: 我想创建一个类,该类的对象可以使用注释(或者在需要将参数传递给注释的情况下使用自定义注释)注入资源方法中。在泽西岛1. *中,我会使用(在我的情况下,与一起使用)。我想要实现的是来自 dropwizard的 [ 1 ] (使用Jersey 1.7)。 __ 据我所知,Jersey的注入功能已被HK2取代,我找不到我所描述的任何示例。 问题答案: 您需要从HK2 实现InjectionR
问题内容: 我们计划使用Spring 4.0.6版本制作一个新应用程序。我们使用可以返回“ XML”或“ JSON”的控制器。在上一个项目中,我们已经使用JAX-RS API成功地将Spring的Jersey支持REST支持,但是在阅读了前辈的几篇文章和建议后,他们说Spring提供了很好的REST支持。 如果我不使用JAX-RS和Jersey而使用Spring REST支持,则使我真正感到困惑的
我需要在java上开发简单的web服务。我是java技术新手,根据几篇文章,我决定将JAX-RS(Jersey)与嵌入式http服务器(Grizzly2)结合使用,因为它看起来适合构建REST服务,部署似乎很简单。 在我的开发环境中,所有工作都很完美(使用IntllijIdea)。 但当我尝试在测试服务器上部署时,每个请求都返回“500内部错误”(偶数/application.wadl) 简单资源
我使用Jersey 2.10异常映射器类来处理异常。我想返回错误状态和错误信息的JSON正文。我想得到类似的回应: Jersey没有在响应中发送JSON正文。我得到的是: 如果我将状态代码更改为200,那么我将获得预期的响应正文 请帮我找出这个问题的解决方案。 异常映射器在错误对象中填充错误消息和状态。以下是异常映射程序代码: 这是来自错误对象的代码:
我在WAS 8.0上部署了一个JAX-RS WS应用程序,其中包含一个空的2.4web.xml,这个类扩展了'javax.WS.rs.core.application'和2个资源,它工作得很好。 如何在WAS7.0上部署JAX-RS应用程序而不使用Jersey或任何其他与应用程序服务器相关的类? 谢谢