三、Jersey中的@Context注入

原文:https://blog.csdn.net/wolfcode_cn/article/details/80797074

一、@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相关对象,比如ServletContextServletRequestServletResponse就显得很有必要。@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之后,这些资源类还是会交回给容器来管理

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值