HK2 Factory invoked prior to Jersey filter when @Context is used for setter/field/constructor injection

后端 未结 1 782
情书的邮戳
情书的邮戳 2021-01-04 21:06

I\'ve been able to inject into my jersey resource from a filter as per How to inject an object into jersey request context?. This allows me to successfully inject into a me

相关标签:
1条回答
  • 2021-01-04 21:23

    Oddly it only works for me with @PreMatching on the filter (which limits access to some things you may or may not need). Not quite sure what's going on under the hood, that cause it not to work without it :-(. Below is a complete test using Jersey Test Framework.

    import java.io.IOException;
    import javax.inject.Inject;
    import javax.inject.Singleton;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.container.ContainerRequestContext;
    import javax.ws.rs.container.ContainerRequestFilter;
    import javax.ws.rs.container.PreMatching;
    import javax.ws.rs.core.Application;
    import javax.ws.rs.core.Context;
    import javax.ws.rs.ext.Provider;
    import org.glassfish.hk2.utilities.binding.AbstractBinder;
    import org.glassfish.jersey.process.internal.RequestScoped;
    import org.glassfish.jersey.server.ResourceConfig;
    import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
    import org.glassfish.jersey.test.JerseyTest;
    import org.junit.Assert;
    import org.junit.Test;
    
    public class FilterInjectionTest extends JerseyTest {
    
        private static final String MESSAGE = "Inject OK";
        private static final String OBJ_PROP = "myObject";
    
        public static class MyObject {
    
            private final String value;
    
            public MyObject(String value) {
                this.value = value;
            }
    
            public String getValue() {
                return value;
            }
        }
    
        @PreMatching
        @Provider
        public static class MyObjectFilter implements ContainerRequestFilter {
    
            @Override
            public void filter(ContainerRequestContext context) throws IOException {
                MyObject obj = new MyObject(MESSAGE);
                context.setProperty(OBJ_PROP, obj);
            }
        }
    
        public static class MyObjectFactory
                extends AbstractContainerRequestValueFactory<MyObject> {
    
            @Override
            @RequestScoped
            public MyObject provide() {
                return (MyObject) getContainerRequest().getProperty(OBJ_PROP);
            }
    
            @Override
            public void dispose(MyObject t) {
            }
        }
    
        @Path("method-param")
        public static class MethodParamResource {
    
            @GET
            public String getResponse(@Context MyObject myObject) {
                return myObject.getValue();
            }
        }
    
        @Path("constructor")
        public static class ConstructorResource {
    
            private final MyObject myObject;
    
            @Inject
            public ConstructorResource(@Context MyObject myObject) {
                this.myObject = myObject;
            }
    
            @GET
            public String getResponse() {
                return myObject.getValue();
            }
        }
    
        @Path("field")
        public static class FieldResource {
    
            @Inject
            private MyObject myObject;
    
            @GET
            public String getResponse() {
                return myObject.getValue();
            }
        }
    
        @Override
        public Application configure() {
            ResourceConfig config = new ResourceConfig();
            config.register(MethodParamResource.class);
            config.register(MyObjectFilter.class);
            config.register(ConstructorResource.class);
            config.register(FieldResource.class);
            config.register(new AbstractBinder() {
                @Override
                protected void configure() {
                    bindFactory(MyObjectFactory.class)
                            .to(MyObject.class).in(Singleton.class);
                }
            });
            return config;
        }
    
        @Test
        public void methoParamInjectionOk() {
            String response = target("method-param").request().get(String.class);
            Assert.assertEquals(MESSAGE, response);
            System.out.println(response);
        }
    
        @Test
        public void costructorInjectionOk() {
            String response = target("constructor").request().get(String.class);
            Assert.assertEquals(MESSAGE, response);
            System.out.println(response);
        }
    
        @Test
        public void fieldInjectionOk() {
            String response = target("field").request().get(String.class);
            Assert.assertEquals(MESSAGE, response);
            System.out.println(response);
        }
    }
    

    UPDATE

    The solution, without having to make it a @PreMatching filter, is to inject with javax.inject.Provider. This will allow you to lazily retrieve the object. I guess what happens with the constructor and field injection is that right after matching the resource class, it it immediately created and injected. Because the filter hasn't been called yet, there is no object for the factory. It works for the method injection, because it is just like any other method call. The object is passed to it when the method is called. Below is the example with the javax.inject.Provider

    @Path("constructor")
    public static class ConstructorResource {
    
        private final javax.inject.Provider<MyObject> myObjectProvider;
    
        @Inject
        public ConstructorResource(javax.inject.Provider<MyObject> myObjectProvider) {
            this.myObjectProvider = myObjectProvider;
        }
    
        @GET
        public String getResponse() {
            return myObjectProvider.get().getValue();
        }
    }
    
    @Path("field")
    public static class FieldResource {
    
        @Inject
        private javax.inject.Provider<MyObject> myObjectProvider;;
    
        @GET
        public String getResponse() {
            return myObjectProvider.get().getValue();
        }
    }
    
    0 讨论(0)
提交回复
热议问题