Java 如何使用CXF、JAX-RS和HTTP缓存
文档中提到了缓存: CXF-JAXRS通过处理If-Match、If-Modified-Since和ETags头来支持许多高级HTTP特性。JAXRS请求上下文对象可用于检查前提条件。还支持Vary、CacheControl、Cookies和Set CookiesJava 如何使用CXF、JAX-RS和HTTP缓存,java,rest,cxf,jax-rs,http-caching,Java,Rest,Cxf,Jax Rs,Http Caching,文档中提到了缓存: CXF-JAXRS通过处理If-Match、If-Modified-Since和ETags头来支持许多高级HTTP特性。JAXRS请求上下文对象可用于检查前提条件。还支持Vary、CacheControl、Cookies和Set Cookies 我对使用(或至少探索)这些特性非常感兴趣。然而,虽然“提供支持”听起来很有趣,但它对实现这些特性并没有特别的帮助。关于如何使用修改后的CacheControl或ETags的任何帮助或提示?实际上,答案并不特定于CXF,而是纯JAX-R
我对使用(或至少探索)这些特性非常感兴趣。然而,虽然“提供支持”听起来很有趣,但它对实现这些特性并没有特别的帮助。关于如何使用修改后的CacheControl或ETags的任何帮助或提示?实际上,答案并不特定于CXF,而是纯JAX-RS:
// IPersonService.java
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
@GET
@Path("/person/{id}")
Response getPerson(@PathParam("id") String id, @Context Request request);
// PersonServiceImpl.java
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
public Response getPerson(String name, Request request) {
Person person = _dao.getPerson(name);
if (person == null) {
return Response.noContent().build();
}
EntityTag eTag = new EntityTag(person.getUUID() + "-" + person.getVersion());
CacheControl cc = new CacheControl();
cc.setMaxAge(600);
ResponseBuilder builder = request.evaluatePreconditions(person.getUpdated(), eTag);
if (builder == null) {
builder = Response.ok(person);
}
return builder.cacheControl(cc).lastModified(person.getUpdated()).build();
}
在即将发布的JAX-RS 2.0中,可以声明性地应用缓存控制,如中所述
你至少可以用Jersey测试一下。但不确定CXF和RESTEasy。CXF没有实现此处所述的动态筛选: 如果您使用直接返回自己的对象而不是CXF响应,则很难添加缓存控制头 通过使用自定义注释并创建一个CXF拦截器来读取此注释并添加标题,我找到了一种优雅的方法 首先,创建一个CacheControl注释
@Target(ElementType.METHOD )
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheControl {
String value() default "no-cache";
}
然后,将此注释添加到您的CXF操作方法中(如果您使用接口,则此注释可用于接口或实现)
然后创建一个CacheControl拦截器来处理注释并将标题添加到响应中
public class CacheInterceptor extends AbstractOutDatabindingInterceptor{
public CacheInterceptor() {
super(Phase.MARSHAL);
}
@Override
public void handleMessage(Message outMessage) throws Fault {
//search for a CacheControl annotation on the operation
OperationResourceInfo resourceInfo = outMessage.getExchange().get(OperationResourceInfo.class);
CacheControl cacheControl = null;
for (Annotation annot : resourceInfo.getOutAnnotations()) {
if(annot instanceof CacheControl) {
cacheControl = (CacheControl) annot;
break;
}
}
//fast path for no cache control
if(cacheControl == null) {
return;
}
//search for existing headers or create new ones
Map<String, List<String>> headers = (Map<String, List<String>>) outMessage.get(Message.PROTOCOL_HEADERS);
if (headers == null) {
headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
outMessage.put(Message.PROTOCOL_HEADERS, headers);
}
//add Cache-Control header
headers.put("Cache-Control", Collections.singletonList(cacheControl.value()));
}
}
公共类CacheInterceptor扩展了AbstractOutDatabindingInterceptor{
公共缓存拦截器(){
超级(阶段元帅);
}
@凌驾
public void handleMessage(Message outMessage)引发错误{
//搜索操作上的CacheControl批注
OperationResourceInfo resourceInfo=outMessage.getExchange().get(OperationResourceInfo.class);
CacheControl CacheControl=null;
对于(注释注释注释:resourceInfo.getOutAnnotations()){
如果(不指定CacheControl的实例){
cacheControl=(cacheControl)annot;
打破
}
}
//无缓存控制的快速路径
if(cacheControl==null){
返回;
}
//搜索现有标题或创建新标题
映射头=(Map)outMessage.get(Message.PROTOCOL\u头);
if(headers==null){
headers=newtreemap(String.CASE不区分大小写顺序);
outMessage.put(Message.PROTOCOL_头,头);
}
//添加缓存控制头
headers.put(“缓存控制”,Collections.singletonList(cacheControl.value());
}
}
最后,将CXF配置为使用拦截器,您可以在此处找到所有需要的信息:
希望这会有帮助
Loïc回答得很好。我唯一的意见是,您生成的EntityTag可能不需要此人的UUID。只有在同一资源的修订之间更改eTag才是重要的。假设ID是不可变的,UUID与资源路径是冗余的(尽管您的Impl调用该参数“name”,所以它可能不是不可变的。此外,您应该确保此值是特定于表示的。例如,如果一个资源的两个表示形式因媒体类型而异,请将媒体类型值与版本标识符一起使用,以生成特定于表示形式的ETag值。我从不使用响应对象-只需让CXF处理该部分即可。如何处理你可以不用它吗?@oligofren我以前从未使用过它们,但这是我找到的唯一解决方案。好吧。我提出了一个关于这个问题的新问题。也许其他人知道:)这更像是声明式应用过滤器,可以做缓存之类的事情,但无论如何,这是一个很大的改进。谢谢你让我(我们)知道。
public class CacheInterceptor extends AbstractOutDatabindingInterceptor{
public CacheInterceptor() {
super(Phase.MARSHAL);
}
@Override
public void handleMessage(Message outMessage) throws Fault {
//search for a CacheControl annotation on the operation
OperationResourceInfo resourceInfo = outMessage.getExchange().get(OperationResourceInfo.class);
CacheControl cacheControl = null;
for (Annotation annot : resourceInfo.getOutAnnotations()) {
if(annot instanceof CacheControl) {
cacheControl = (CacheControl) annot;
break;
}
}
//fast path for no cache control
if(cacheControl == null) {
return;
}
//search for existing headers or create new ones
Map<String, List<String>> headers = (Map<String, List<String>>) outMessage.get(Message.PROTOCOL_HEADERS);
if (headers == null) {
headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
outMessage.put(Message.PROTOCOL_HEADERS, headers);
}
//add Cache-Control header
headers.put("Cache-Control", Collections.singletonList(cacheControl.value()));
}
}