Java技术升华jerseyRESTful webservice

Jersey 开发RESTful(九)Jersey中的注入

2018-01-24  本文已影响322人  叩丁狼教育

【原创文章,转载请注明原文章地址,谢谢!】

在本节中,我们会针对前面两篇Jersey文章的资源,请求参数绑定等做一些补充说明。

@Context注入特殊资源

在SpringMVC中,让我们影响深刻的有一个标签是@Autowire。能够注入一些非常特殊的对象,比如ApplicationEventPublisher,在Web环境下能注入ServletContext等等,在SpringMVC中,还能在每一个Controller方法参数中注入HttpServletRequest,HttpSession等特殊对象,其实在Jersey中也可以实现,需要用到@Context注解。

获取UriInfo

/**
 * 使用@Context获取请求上下文内容
 * 
 * @param ui
 * @return
 */
@GET
@Path("/formui")
public String formPojoParam(@Context UriInfo ui) {
    MultivaluedMap<String, String> qps = ui.getQueryParameters();
    MultivaluedMap<String, String> pps = ui.getPathParameters();
    System.out.println(qps);
    System.out.println(pps);
    return "success";
}

在JAX-RS中,一个UriInfo对象封装了应用相关信息和本次请求相关信息。并在UriInfo中提供了一些有用的方法:
比如针对请求GET localhost:8082/webapi/param/formui?name=haha

//获取资源路径:param/formui
    System.out.println(ui.getPath());
    //获取完整请求路径:http://localhost:8082/webapi/param/formui
    System.out.println(ui.getAbsolutePath());
    //获取请求根路径:http://localhost:8082/webapi/
    System.out.println(ui.getBaseUri());
    //获取匹配请求的资源类[cn.wolfcode.jersey._04parameters.ParameterRest@73ea3756]
    System.out.println(ui.getMatchedResources());
    //获取尝试匹配的资源路径[param/formui, param]
    System.out.println(ui.getMatchedURIs());
    //获取完整请求URIhttp://localhost:8082/webapi/param/formui?name=haha
    System.out.println(ui.getRequestUri());
    //获取请求参数列表{name=[haha]},类型为MultivaluedMap<String, String>
    System.out.println(ui.getQueryParameters());
    //获取路径参数列表{}
    System.out.println(ui.getPathParameters());

获取请求头信息

可以通过@Context直接获取请求头相关信息:

/**
 * 通过@Context获取请求头信息
 * 
 * @param headers
 * @return
 */
@GET
@Path("/headps")
public String headps(@Context HttpHeaders headers) {
    System.out.println(headers.getRequestHeaders());
    return "success";
}

在JAX-RS中,提供了HttpHeaders接口来描述一个请求头信息,在该接口中也提供了很多请求头相关的有用的方法:

//获取请求头中的指定值dfse-34fd-1ggd-34pe
    System.out.println(headers.getHeaderString("token"));
    //获取接受的语言[*]
    System.out.println(headers.getAcceptableLanguages());
    //获取接受的MIME类型[application/xml]
    System.out.println(headers.getAcceptableMediaTypes());
    //获取cookie信息
    System.out.println(headers.getCookies());
    //获取请求头中的指定值[dfse-34fd-1ggd-34pe]
    System.out.println(headers.getRequestHeader("token"));
    //获取请求头所有信息
    //{content-type=[application/x-www-form-urlencoded], accept=[application/xml], token=[dfse-34fd-1ggd-34pe], cache-control=[no-cache],
    System.out.println(headers.getRequestHeaders());

获取请求处理相关信息

@Context还可以获取一个Request对象:

/**
 * 通过@Context获取请求信息
 * 
 * @param headers
 * @return
 */
@GET
@Path("/request")
public String request(@Context Request request) {
    System.out.println(request);
    return "success";
}

但是注意,在JAX-RS中,这个Request对象并不代表的是请求对象本身,他是一个有关请求处理的辅助类,这个类主要的作用是用来做请求的预处理相关内容。那如果要真正获取请求,继续向下看。

获取Servlet相关对象

如果上面几个案例通过@Context获取的都是请求相关的抽象,那是因为我们之前说过,Jersey可以脱离Servlet环境运行。那么,如果我们的应用确实运行在Servlet环境下,获取Servlet相关对象,比如ServletContext,ServletRequest,ServletResponse就显得很有必要。@Context注解给我们提供了这个能力。

/**
 * 在Servlet环境下使用@Context注入Servlet对象
 * @param ctx
 * @param req
 * @param resp
 * @return
 */
@GET
@Path("/servlet")
public String servlet(@Context ServletContext ctx,
                      @Context HttpServletRequest req,@Context HttpServletResponse resp){
    System.out.println(ctx);
    System.out.println(req);
    System.out.println(resp);
    return "success";
}

但是再次注意,要获取Servlet相关对象,必须运行在Servlet环境下。

子资源定位器

在特殊情况下,我们可能会对某个重复使用的子资源进行抽象。在这种情况下,我们就可以把某个子资源单独的放到一个类中,由主资源类在特定的资源方法中返回即可。以下示例作为一个演示:

创建一个主资源类:

@Path("parent")
public class ParentResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Employee get(@Context ServletContext ctx) {
        System.out.println(ctx);
        return new Employee(1L, "哈哈", 2);
    }

    @Path("child")
    public ChildResource childResource(){
        return new ChildResource();
    }
}

注意,第一个资源parent,就是一个正常的资源方法,那么在请求/parent这个资源的时候,get方法就会被正常执行。但是第二个资源child,这个方法返回的是一个ChildResource对象,那么在请求/parent/child资源的时候,就不是直接在这个方法中处理了,而是继续交给ChildResource进行处理。
我们来看看ChildResource类:

public class ChildResource {

    @GET
    @Path("{version}")
    public String child(@PathParam("version") @DefaultValue("2.0") String version) {
        return version;
    }
}

在这个子资源类中,我们有一个资源路径{version},那么真正在处理/parent/child/或者/parent/child/{version} 资源的时候,就是由这个方法处理的。

当我们执行请求 GET /parent/child的时候,得到2.0这个值;
当执行请求GET /parent/child/3.0的时候,得到3.0这个值;

资源类的生命周期

我们一直忽略没有讨论的一个问题就是线程安全问题。我们知道,在Struts2中,每一个请求创建一个全新的Action实例,所以我们能够在Action的成员变量中绑定请求相关的参数;而在SpringMVC中,因为参数是绑定在方法列表中的,所以Controller可以是单例的,所以我们不在Controller中使用线程不安全的成员变量。

那么在Jersey中是怎么样的呢?Jersey的资源类是怎么处理请求的呢?

@GET
@Path("{id}")
public String pathParam(@PathParam("id") Long id) {
    System.out.println(this);
    return "success";
}

我们写了一个简单的测试方法,在方法中打印this,连续两次请求该方法,
cn.wolfcode.jersey._04parameters.ParameterRest@c065304
cn.wolfcode.jersey._04parameters.ParameterRest@e5a25a9
可以看到,每次请求都会创建一个全新的资源类对象。从这点上,我们可以理解为,Jersey默认的资源类的使用方式类似于Struts2。但我们知道,这种方式在性能上面表现的并不是很好,比如一百万请求过来,就需要创建一百万个对象来处理,而调用的仅仅是对象中的某几个方法。所以我们可以这样设置:

@Path("param")
@Singleton
public class ParameterRest {

我们在资源类上添加javax.inject.Singleton注解,再次请求,结果变为:
cn.wolfcode.jersey._04parameters.ParameterRest@2f831231
cn.wolfcode.jersey._04parameters.ParameterRest@2f831231

按照这种使用方式,我们就和使用SpringMVC的controller类似了。同样的,在子资源类中,也可以使用@Singleton进行注解。

注入的位置

前面介绍的所有参数绑定注解,包括@PathParam,@QueryParam,@FormParam,@BeanParam,@HeaderParam,@Context等都可以在主资源类,或者子资源类的属性,构造方法参数,资源方法参数,Setter,子资源方法参数中。
比如:

public class ChildResource {
@Context
private ServletContext ctx;

@GET
@Path("{version}")
public String child(@PathParam("version") @DefaultValue("2.0") String version) {
    return version;
}
}

但是需要注意一点,如果资源类被标记为@Singleton的,不要在类属性中标记和请求相关的线程不安全的内容。虽然官方文档上介绍,可以这样使用:

@Path("resource")
@Singleton
public class MySingletonClass{

    @Context
    Request request;
}

就算Request是线程相关的,但是官方文档中解释这种方式是允许的,因为注入的是Request的代理,在执行阶段才是真正使用的是当前的请求。不过我的建议还是按照SpringMVC的注入方式,线程相关的内容都通过方法参数注入,ServletContext这种全局的对象,才在类级别注入。

小结

在本节中,我们讨论了@Context标签的使用,也了解了Jersey中资源类的生命周期和建议使用方式。当然,后面在把Jersey和Spring或者使用Springboot集成Jersey之后,这些资源类还是会交回给容器来管理。

WechatIMG7.jpeg
上一篇 下一篇

猜你喜欢

热点阅读