Using @Context, @Provider and ContextResolver in JAX-RS

前端 未结 5 1178
感情败类
感情败类 2020-12-02 15:35

I\'m just getting acquainted with implementing REST web services in Java using JAX-RS and I ran into the following problem. One of my resource classes requires access to a s

相关标签:
5条回答
  • 2020-12-02 16:11

    If anyone is using Resteasy this is what worked for me.

    If you add something like this:

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

    into something like a jaxrs filter, it allows you to do something like this:

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

    This is specific to Resteasy, which doesn't have something like SingletonTypeInjectableProvider.

    0 讨论(0)
  • 2020-12-02 16:14

    A pattern that works for me: Add some fields on your Application subclass that provide the objects you need to inject. Then use an abstract base class to do the "injection":

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

    All your services that need to access the database may now extend ServiceBase and have the database available automatically via the protected field (or a getter, if you prefer that).

    This works for me with Undertow and Resteasy. In theory this should work across all JAX-RS implementations since injection of the Application is supported by the standard AFAICS, but I haven't tested it in other settings.

    For me, the advantage over Bryant's solution was that I don't have to write some resolver class just so I can get at my application-scoped singletons like the database.

    0 讨论(0)
  • 2020-12-02 16:20

    Implement a InjectableProvider. Most likely by extending PerRequestTypeInjectableProvider or SingletonTypeInjectableProvider.

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

    Would let you have:

    @Context StorageEngine storage;
    
    0 讨论(0)
  • 2020-12-02 16:24

    I don't think there's a JAX-RS specific way to do what you want. The closest would be to do:

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

    However, I think the @javax.ws.rs.core.Context annotation and javax.ws.rs.ext.ContextResolver is really for types related to JAX-RS and supporting JAX-RS providers.

    You may want to look for Java Context and Dependency Injection (JSR-299) implementations (which should be available in Java EE 6) or other dependency injection frameworks such as Google Guice to help you here.

    0 讨论(0)
  • 2020-12-02 16:27

    I found another way. In my case i want to provide the user currently logged in as a User entity from my persitence layer. This is the class:

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

    I only used implements ContextResolver<User> and @Provider to get this class discovered by Jax-Rs and get SecurityContext injected. To get the current user i use CDI with my Qualifier @CurrentUser. So on every place where i need the current user i type:

    @Inject
    @CurrentUser
    private User user;
    

    And indeed

    @Context
    private User user;
    

    does not work (user is null).

    0 讨论(0)
提交回复
热议问题