2013-05-07 62 views
5

我很難理解Jersey的注入機制。 JAX-RS規範(http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-520005)規定,應用程序子類,根資源類和提供程序可以通過@Context進行注入。Jersey @Context範圍

我現在有一個在啓動時實例化的類,它有一個在每個請求上調用的方法。在該方法內我需要訪問當前的UriInfo對象。問題是,這個方法不是從我的代碼中調用的。所以我無法直接將UriInfo傳遞給該方法。

其實我是想這樣做:

public class MyClass implements ThirdPartyInterface { 

    // not possible because class is no Application subclass, root resource class or provider 
    @Context 
    private UriInfo uriInfo; 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

我嘗試這樣做。顯然沒有成功:

public class MyClass implements ThirdPartyInterface { 

    private UriInfo uriInfo; 

    public MyClass(UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

@Provider 
@Produces(MediaType.WILDCARD) 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    @Context 
    private UriInfo uriInfo; 

    private MyClass myClass; 

    private ThirdPartyClass thirdPartyClass; 

    public MyBodyWriter() { 
     // uriInfo is null at this time :(
     myClass = new MyClass(uriInfo); 

     thirdPartyClass = new ThirdPartyClass(); 
     thirdPartyClass.register(myClass); 
    } 

    public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException { 
     // execute() calls MyClass#methodCallebByThirdPartyCode() 
     thirdPartyClass.execute(); 
    } 
} 

我能想到的唯一的解決方法是這樣的。我不認爲它很乾淨:

public class MyClass implements ThirdPartyInterface { 

    private UriInfo uriInfo; 

    public void setUriInfo(final UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    public void methodCallebByThirdPartyCode() { 
     Uri requestUri = uriInfo.getRequestUri(); 

     // do something 
    } 
} 

@Provider 
@Produces(MediaType.WILDCARD) 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    @Context 
    private UriInfo uriInfo; 

    private MyClass myClass; 

    private ThirdPartyClass thirdPartyClass; 

    public MyBodyWriter() { 
     myClass = new MyClass(); 

     thirdPartyClass = new ThirdPartyClass(); 
     thirdPartyClass.register(myClass); 
    } 

    public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException { 
     myClass.setUriInfo(uriInfo); 

     // execute() calls MyClass#methodCallebByThirdPartyCode() 
     thirdPartyClass.execute(); 

     myClass.setUriInfo(null); 
    } 
} 

我希望有一個更好的解決方案,但也許我完全在錯誤的軌道上。

謝謝!

+0

也許你只需要'ContainerRequestFilter'? – Willy 2013-05-08 02:34:54

+0

我不知道這是否適合我的情況。規範說「提供者類由JAX-RS運行時實例化」。但是我需要在施工時參考對象,將它傳遞給第三方服務。 – 2013-05-08 10:01:35

回答

3

晚的答案,但一個很好的問題......所以讓我們去:

您可以使用注射一org.glassfish.hk2.api.Factoryjavax.inject.Provider。我不知道自哪個版本可用,所以也許你必須升級你的jersery版本。對於以下樣本,我使用jersey 2.12

首先,你必須實現並註冊/綁定廠家爲MyClass

MyClassFactory:通過ResourceConfig

import javax.inject.Inject; 
import javax.ws.rs.core.UriInfo; 
import org.glassfish.hk2.api.Factory; 
// ... 

public class MyClassFactory implements Factory<MyClass> { 

    private final UriInfo uriInfo; 

    // we will bind MyClassFactory per lookup later, so 
    // the constructor will be called everytime we need the factory 
    // meaning, uriInfo is also per lookup 

    @Inject 
    public MyClassFactory(final UriInfo uriInfo) { 
     this.uriInfo = uriInfo; 
    } 

    @Override 
    public MyClass provide() { 
     return new MyClass(uriInfo) 
    } 

    @Override 
    public void dispose(UriInfo uriInfo) { 
     // ignore 
    } 

} 

報名:

import org.glassfish.hk2.api.PerLookup; 
import org.glassfish.hk2.utilities.binding.AbstractBinder; 
import org.glassfish.jersey.server.ResourceConfig; 
// ... 

public class MyResourceConfig extends ResourceConfig { 

    public MyResourceConfig() { 
     register(new AbstractBinder() { 
      @Override 
      protected void configure() { 
       bindFactory(MyClassFactory.class).to(MyClass.class).in(PerLookup.class); 
       // ... bind additional factories here 
      } 
     }); 
     // ... 
    } 

} 

現在你可以注入MyClass的每查詢提供商,資源等
但注意:Afaig有兩種方法es只有一個將最終按照供應商的意圖工作...

import javax.inject.Inject; 
import javax.ws.rs.Produces; 
import javax.ws.rs.ext.MessageBodyWriter; 
import javax.ws.rs.ext.Provider; 
// ... 

@Provider 
@Produces("application/foo-bar") 
public class MyBodyWriter implements MessageBodyWriter<MyView> { 

    // first approache - don't do it! 
    // will only injected once, cause MyBodyWriter is only instantiated once 
    @Inject 
    private MyClass myClass; 

    // second approache - works fine! 
    private final javax.inject.Provider<MyClass> provider; 

    // MyBodyWriter instantiate once 
    // get an inject provider here 
    @Inject 
    public MyBodyWriter(javax.inject.Provider<MyClass> myClassProvider) { 
     this.provider = myClassProvider; 
    } 

    @Override 
    public boolean isWriteable(Class<?> t, Type g, Annotation[] a, MediaType m) { 
     return t == MyView.class; 
    } 

    @Override 
    public long getSize(MyView t, Class<?> c, Type g, Annotation[] a, MediaType m) { 
     // deprecated by JAX-RS 2.0 and ignored by Jersey runtime 
     return 0; 
    } 

    @Override 
    public void writeTo(MyView v, Class<?> c, Type t, Annotation[] a, MediaType m, MultivaluedMap<String, Object> s, OutputStream o) throws IOException, WebApplicationException { 

     // attention: its not per lookup !!! 
     MyClass myClassDirectInjected = myClass; 
     System.out.println(myClassDirectInjected); // same instance everytime 

     // but this is ;) 
     MyClass myClassFromProvider = provider.get(); 
     System.out.println(myClassFromProvider); // it's a new instance everytime 

     // ... 
    } 

} 

希望這有點幫助。