2010-06-15 2 views
26

JAX-RS를 사용하여 Java에서 REST 웹 서비스를 구현하는 데 익숙해졌으며 다음 문제가 발생했습니다. 내 리소스 클래스 중 하나는 StorageEngine 인터페이스 뒤에 추상화되어있는 저장소 백엔드에 대한 액세스가 필요합니다. 현재 StorageEngine 인스턴스를 REST 요청을 처리하는 리소스 클래스에 삽입하고 싶습니다.이 작업을 수행하는 좋은 방법은 @Context 주석과 적절한 ContextResolver 클래스를 사용하는 것이라고 생각했습니다.JAX-RS에서 @Context, @Provider 및 ContextResolver 사용

MyResource.java에서 :

class MyResource { 
    @Context StorageEngine storage; 
    [...] 
} 

StorageEngineProvider.java에서 :

@Provider 
class StorageEngineProvider implements ContextResolver<StorageEngine> { 
    private StorageEngine storage = new InMemoryStorageEngine(); 

    public StorageEngine getContext(Class<?> type) { 
     if (type.equals(StorageEngine.class)) 
      return storage; 
     return null; 
    } 
} 

내가 자동으로 제공 업체와 자원 클래스를 발견 com.sun.jersey.api.core.PackagesResourceConfig을 사용하고 따라하고있어 이것은 내가 지금까지 무엇을 가지고 로그를 보면 StorageEngineProvider 클래스를 멋지게 집계합니다 (타임 스탬프 및 불필요한 항목은 의도적으로 삭제되었습니다) :

,

하지만, 내 자원 클래스에 storage의 값은 항상 null합니다 - StorageEngineProvider의 생성자이나 그 getContext 방법은 지금, 뉴저지에 의해 불려도. 여기서 내가 뭘 잘못하고 있니?

답변

19

JAX-RS가 원하는대로 할 수있는 방법이 없다고 생각합니다.

@Path("/something/") 
class MyResource { 
    @Context 
    javax.ws.rs.ext.Providers providers; 

    @GET 
    public Response get() { 
     ContextResolver<StorageEngine> resolver = providers.getContextResolver(StorageEngine.class, MediaType.WILDCARD_TYPE); 
     StorageEngine engine = resolver.get(StorageEngine.class); 
     ... 
    } 
} 

그러나, 나는 @의 javax.ws.rs.core.Context 주석 생각하고 javax.ws.rs.ext.ContextResolver는 JAX-RS와 관련 유형에 대해 정말 : 가장 가까운 할 것 JAX-RS 제공 업체를 지원합니다.

Java Context and Dependency Injection (JSR-299) 구현 (Java EE 6에서 사용 가능) 또는 Google Guice와 같은 다른 종속성 주입 프레임 워크를 사용하면 도움이 될 수 있습니다.

+2

FWIW 당신은 ​​또한 약간 짧은 수행 할 수 있습니다 @Context ContextResolver을 storageEngineResolver; –

+0

테스트 및 프로덕션 컨텍스트에서 대체 백업 구현을 제공하는 것만 큼 무언가를하는 것이 그렇게 어렵다는 것은 얼마나 우스 꽝입니다! 최소한 이것은 다른 프레임 워크에서 드래그 할 필요없이 작동합니다. –

+1

Doug의 솔루션 (코멘트에서)이 나를 위해 작동하지 않았다 (RestEasy 포함). –

13

InjectableProvider을 구현하십시오. 대부분 PerRequestTypeInjectableProvider 또는 SingletonTypeInjectableProvider를 확장합니다.

@Provider 
public class StorageEngineResolver extends SingletonTypeInjectableProvider<Context, StorageEngine>{ 
    public MyContextResolver() { 
     super(StorageEngine.class, new InMemoryStorageEngine()); 
    } 
} 

은 당신이 할 수겠습니까 :

@Context StorageEngine storage; 
+2

저지 섬 특유의 좋은 제안입니다. CXF는 ['ContextProvider'] (http://sberyozkin.blogspot.co.uk/2012/03/custom-jax-rs-contexts-in-cxf-260.html)와 비슷한 것을합니다. –

0

나는 다른 방법을 발견했다. 제 경우에는 필자의 Persitence Layer에서 User 엔티티로 현재 로그인 한 사용자를 제공하고 싶습니다. 이것은 클래스 : 만 잭스-RS에 의해 발견 된이 클래스를 얻고 얻을 implements ContextResolver<User>@Provider을 사용

@RequestScoped 
@Provider 
public class CurrentUserProducer implements Serializable, ContextResolver<User> { 

    /** 
    * Default 
    */ 
    private static final long serialVersionUID = 1L; 


    @Context 
    private SecurityContext secContext; 

    @Inject 
    private UserUtil userUtil; 

    /** 
    * Tries to find logged in user in user db (by name) and returns it. If not 
    * found a new user with role {@link UserRole#USER} is created. 
    * 
    * @return found user or a new user with role user 
    */ 
    @Produces 
    @CurrentUser 
    public User getCurrentUser() { 
     if (secContext == null) { 
      throw new IllegalStateException("Can't inject security context - security context is null."); 
     } 
     return userUtil.getCreateUser(secContext.getUserPrincipal().getName(), 
             secContext.isUserInRole(UserRole.ADMIN.name())); 
    } 

    @Override 
    public User getContext(Class<?> type) { 
     if (type.equals(User.class)) { 
      return getCurrentUser(); 
     } 
     return null; 
    } 

} 

SecurityContext 주입. 현재 사용자를 얻으려면 CDI를 한정자 @CurrentUser과 함께 사용하십시오. 그래서 모든 장소에 내가 현재 사용자가 필요로하는 곳에 내가 입력 :

@Inject 
@CurrentUser 
private User user; 

그리고 실제로

@Context 
private User user; 

가 (사용자가 null) 작동하지 않습니다.나를 위해 작동

0

패턴은 : 당신이 주입하는 데 필요한 객체를 제공하여 응용 프로그램 서브 클래스에서 일부 필드를 추가합니다. 당신이 경우, 지금 ServiceBase를 확장하고 보호 필드를 통해 자동으로 사용할 수있는 데이터베이스 (또는 게터이있을 수 있습니다 데이터베이스에 액세스 할 필요가

public abstract class ServiceBase { 

    protected Database database; 

    @Context 
    public void setApplication(Application app) { 
     YourApplication application = (YourApplication) app; 
     database = application.getDatabase(); 
    } 
} 

귀하의 모든 서비스 : 그런 다음 "주입"을 수행하는 추상 기본 클래스를 사용 선호).

이 물러 및 RESTEasy가 나를 위해 작동합니다. 이론적으로이 은 응용 프로그램의 주입이 표준 AFAICS에서 지원되기 때문에 모든 JAX-RS 구현에 대해이 작동해야하지만 다른 설정에서는 테스트하지 않았습니다. 나를 위해

는 브라이언트의 솔루션을 통해 장점은 그냥 그래서 데이터베이스처럼 내에서 응용 프로그램 | 싱글에서 얻을 수있는 몇 가지 해결 클래스를 작성하지 않아도이었다.