gpt4 book ai didi

java - Jersey :InjectableProvider 没有被选中 - Spring

转载 作者:IT老高 更新时间:2023-10-28 13:50:35 24 4
gpt4 key购买 nike

我目前正在尝试创建一个 InjectableProvider与 Jersey ,但我无法让 Jersey 拿起它。

除了使用 @Provider 之外,我找不到任何有关其用法的真实示例,甚至找不到如何使用它的方法。关于实现的注释。似乎在 Jersey 写它的人在一些帖子中暗示这足以让它被接受。

我是否需要指定一些 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();
}

引用 link from Jersey developer .

更新 : calvinkrishy 指出了我想法的两个缺陷。

首先,我假设 Jersey 将开始扫描 @Provider s 被传统的 Jersey-Spring servlet 启动后: com.sun.jersey.spi.spring.container.servlet.SpringServlet .这主要是不正确的。它确实开始扫描,但它会查找具有注释的 Spring bean。

其次,我假设 PerRequestTypeInjectableProvider将在每次传入请求时询问 Injectable处理它控制的注释。这也是错误的。 PerRequestTypeInjectableProvider正如预期的那样在启动时实例化,但 Jersey 然后立即要求 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
}
}

更新2 : 我想到了我的 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 使用 jersey-spring servlet。在这种情况下, Jersey 将默认使用 Spring bean 进行初始化,因此您的 Provider必须是一个Spring bean。尝试添加 @Named (或者如果您不使用 atinject @Component 或 Spring 注释之一)到您的 Provider .

An example of using Injectable Providers .

更新 :注入(inject)范围更清晰:
Provider必须是一个单例,因为对于所有实际目的来说,它是一个与范围相关的工厂,并且不需要为每个请求构建一个工厂。注入(inject)本身会根据请求发生。换句话说,getInjectable每个请求都会调用方法。你有机会尝试吗?

OTOH,如果您扩展 SingletonTypeInjectableProvider每次都会将相同的对象注入(inject)到您的资源中。

我不确定我是否完全理解您的 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 中可用的注入(inject)类型和上下文的更多信息。

正如您现在可能想到的,如果您只需要访问 HttpServletRequest然后直接将其注入(inject)您的 ResourceProvider使用 @Context注释会让你知道。

但是,要将这些值传递给 Injectable 必须使用 AssistedProvider或使用与您类似的方法。但是,如果您内联您的 Injectable,您可以再次缓解这种情况。在 Provider 中定义并注入(inject) HttpServletRequestProvider类(class)。在这种情况下, Injectable将能够访问 HttpServletRequest实例(因为它在范围内)。我刚刚更新了我的例子来展示这种方法。

注入(inject)使用 PerRequestTypeInjectableProviderSingletonTypeInjectableProvider不是您必须向资源中注入(inject)值(value)的唯一两个选项。您也可以使用 *Param 注入(inject)值使用 StringReaderProvider .显然,这种注入(inject)是请求范围的。
@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 value 将被注入(inject)到您的资源方法中。
@Path("/user/")
@Named
public class UserResource {

@Path("{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Result<Users> get(@AttributeParam("foo") @PathParam("id") Users user) {
...
}

}

但老实说,我认为这是对 StringReaderProvider 契约(Contract)的滥用,而前一种技术是使用 Injectable感觉更干净。

关于java - Jersey :InjectableProvider 没有被选中 - Spring,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11944434/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com