代码之家  ›  专栏  ›  技术社区  ›  Tamás

在JAX-RS中使用@Context、@Provider和ContextResolver

  •  28
  • Tamás  · 技术社区  · 14 年前

    我刚刚熟悉使用JAX-RS在Java中实现RESTWeb服务,遇到了以下问题。我的一个资源类需要访问存储后端,该后端被抽象到 StorageEngine 接口。我想注入电流 实例转换为服务REST请求的资源类,我认为这样做的一个好方法是使用 @Context 注释和适当的 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 类(时间戳和不必要的东西故意省略):

    INFO: Root resource classes found:
        class MyResource
    INFO: Provider classes found:
        class StorageEngineProvider
    

    然而 storage 在我的资源类中 null -也不是 也不是它的 getContext 方法由Jersey调用。我做错什么了?

    4 回复  |  直到 14 年前
        1
  •  19
  •   Bryant Luk    14 年前

    我认为没有一种特定于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上下文和依赖注入(JSR-299)实现(应该在javaee6中提供)或其他依赖注入框架(如googleguice)来帮助您。

        2
  •  14
  •   Chase    11 年前

    实施 InjectableProvider . 很可能是通过扩展PerRequestTypeInjectableProvider或SingletonTypeInjectableProvider。

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

    会让你有:

    @Context StorageEngine storage;
    
        3
  •  2
  •   dermoritz    8 年前

    我找到了另一条路。在我的例子中,我想提供当前作为用户实体从我的persitence层登录的用户。 这是一节课:

    @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;
        }
    
    }
    

    我只使用 implements ContextResolver<User> @Provider SecurityContext 注入的。 要获取当前用户,我将CDI与限定符一起使用 @CurrentUser . 所以在我需要输入当前用户的每个地方:

    @Inject
    @CurrentUser
    private User user;
    

    @Context
    private User user;
    

    不工作(用户为空)。

        4
  •  1
  •   jsolum    5 年前

    如果有人在使用Resteasy,这就是我的工作原理。

    如果您添加以下内容:

    ResteasyContext.pushContext(StorageEngine.class, new StorageEngine());
    

    @GET
    @Path("/some/path")
    public Response someMethod(@Context StorageEngine myStorageEngine) {
     ...
    }
    

    这是Resteasy特有的,它没有 SingletonTypeInjectableProvider .

        5
  •  0
  •   Fabian Streitel    8 年前

    一个适合我的模式:在应用程序子类中添加一些字段,这些字段提供需要注入的对象。然后使用抽象基类进行“注入”:

    public abstract class ServiceBase {
    
        protected Database database;
    
        @Context
        public void setApplication(Application app) {
            YourApplication application = (YourApplication) app;
            database = application.getDatabase();
        }
    }
    

    这适用于我与Undertow和Resteasy。从理论上讲 可以跨所有JAX-RS实现工作,因为标准AFAICS支持应用程序的注入,但我没有在其他设置中测试它。

    对我来说,与Bryant的解决方案相比,它的优点是我不必编写一些解析器类,这样我就可以获得应用程序范围内的单例,比如数据库。