我目前正試圖與澤西島創建一個InjectableProvider
,但我無法讓澤西撿起來。澤西島:InjectableProvider沒有拿起 - 春季
我找不到任何真實的使用例子,甚至除了在實現中使用@Provider
註釋之外,還找不到它的使用方法。看起來在澤西寫的這個人在一些帖子中暗示這足以讓它收拾起來。
我是否需要指定一些SPI服務文件,或將它添加到某個工廠的某處?
注:我在Glassfish 3.1中運行,並使用Spring 3.1。春天可能以某種方式接管Provider
的自動加載似乎是合理的。但是,我只是不知道。我不是在用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();
}
UPDATE:calvinkrishy指出兩個缺陷,以我的思維。
首先,我認爲澤西在被傳統的Jersey-Spring servlet啓動後將開始掃描@Provider
s:com.sun.jersey.spi.spring.container.servlet.SpringServlet
。這大多是不正確的;它確實開始掃描,但它會查找帶有註釋的Spring bean。
其次,我認爲PerRequestTypeInjectableProvider
將被要求每個傳入請求的Injectable
來處理它控制的註釋。這也是錯誤的。如預期的那樣,在啓動時實例化PerRequestTypeInjectableProvider
,但澤西然後立即要求Injectable
處理給定的type
的註釋,它通過掃描Restful Services確定它具有 - 在這一點上 - 決定它管理(也就是說,所有這些)。
的PerRequestTypeInjectableProvider
和SingletonTypeInjectableProvider
之間的差異似乎是所得Injectable
或者包含的值而不爲它(singleton)的工作,或者它每次查找它的值(每個請求),從而使值每個請求更改。
這扔了小扳手插入我的計劃通過強迫我做我的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
singleton,它提供了一個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
,它本身就是一個通用的類,只存在爲給定類型Class<T>
和提供的AttributeParam
提供AttributeInjectable
。提供非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
在啓動時,而不是每個請求實例化一次,但他們在每個傳入的請求調用。
實例化提供程序的bean(或讓它們自動拾取)似乎讓他們拾起,但我出於某種原因被迫使他們成爲單身人士。你有什麼想法,爲什麼它需要我使'PerRequestTypeInjectableProvider'單例作用域?如果我強制它進入請求範圍,它將失敗,併發生異常。這個例子實際上是最初的Injectable Provider例子,它讓我瞭解了這一點。他提供的代碼示例並不是最好的(他將'Provider'和'Injectable'結合在一起)。澤西試圖抽象出「ServletRequest」是愚蠢的。 – pickypg 2012-08-29 01:02:27
更新了更多信息。簡而言之,作爲單例的'Provider'是一個特性,但是應該爲每個請求提供一個新的值。 – calvinkrishy 2012-08-29 03:23:09
啊哈。我錯誤地理解了'PerRequestTypeInjectableProvider'的目的。 Jersey立即構建了「Injectable」,它在掃描它控制的各種Restful Services之後決定它需要。我拿「PerRequest」來表示'Provider'會被每個給定的請求檢查。我希望使用'Provider'將'HttpServletRequest'注入到一個新的'AttributeInjectable'(我自己的類型)中,以'AttributeParam'的名稱來提取'HttpServletRequest#getAttribute(String)'。這樣我的Restful Services可以有乾淨地注入屬性。 – pickypg 2012-08-29 04:26:10