Using Jersey's Dependency Injection in a Standalone application

前端 未结 2 690
清歌不尽
清歌不尽 2020-12-21 03:31

I have a interface here

interface Idemo{
  public int getDemo(int i);
}

And it\'s one implementation

class DemoImpl implem         


        
2条回答
  •  余生分开走
    2020-12-21 04:07

    The reason it works with Spring is that the test class is managed by the Spring container by using @RunWith(SpringJUnit4ClassRunner.class). The runner will inject all managed objects into the test object. JerseyTest is not managed this way.

    If you want, you can create your own runner, but you need to understand a bit how HK2 (Jersey's DI framework) works. Take a look at the documentation. Everything revolves around the ServiceLocator. In a standalone, you might see something like this to bootstrap the DI container

    ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
    ServiceLocator locator = factory.create(null);
    ServiceLocatorUtilities.bind(locator, new MyBinder());
    

    Then to get the service, do

    Service service = locator.getService(Service.class);
    

    In the case of the test class, we don't need to gain any access to the service object, we can simply inject the test object, using the ServiceLocator:

    locator.inject(test);
    

    Above, test is the test class instance that gets passed to us in our custom runner. Here is the example implementation of a custom runner

    import java.lang.annotation.*;
    import org.glassfish.hk2.api.*;
    import org.glassfish.hk2.utilities.*;
    import org.junit.runners.BlockJUnit4ClassRunner;
    import org.junit.runners.model.*;
    
    public class Hk2ClassRunner extends BlockJUnit4ClassRunner {
    
        private final ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
        private Class[] binderClasses;
    
        @Target({ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        public static @interface Binders {
    
            public Class[] value();
        }
    
        public Hk2ClassRunner(Class cls) throws InitializationError {
            super(cls);
            Binders bindersAnno = cls.getClass().getAnnotation(Binders.class);
            if (bindersAnno == null) {
                binderClasses = new Class[0];
            }
        }
    
        @Override
        public Statement methodInvoker(FrameworkMethod method, final Object test) {
            final Statement statement = super.methodInvoker(method, test);
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    ServiceLocator locator = factory.create(null);
                    for (Class c : binderClasses) {
                        try {
                            ServiceLocatorUtilities.bind(locator, c.newInstance());
                        } catch (InstantiationException | IllegalAccessException ex) {
                            throw new RuntimeException(ex);
                        }
                    }
                    locator.inject(test);
                    statement.evaluate();
                    locator.shutdown();
                }
            };
        }
    }
    

    In the runner, the methodInvoker is called for every test method, so we are creating a fresh new set of objects for each test method called.

    Here is a complete test case

    @Binders({ServiceBinder.class})
    @RunWith(Hk2ClassRunner.class)
    public class InjectTest {
    
        public static class Service {
    
            @Inject
            private Demo demo;
    
            public void doSomething() {
                System.out.println("Inside Service.doSomething()");
                demo.doSomething();
            }   
        }
    
        public static class Demo {
            public void doSomething() {
                System.out.println("Inside Demo.doSomething()");
            }
        }
    
        public static class ServiceBinder extends AbstractBinder {
            @Override
            protected void configure() {
                bind(Demo.class).to(Demo.class);
                bind(Service.class).to(Service.class);
            }
        }
    
    
        @Inject
        private Service service;
    
        @Test
        public void testInjections() {
            Assert.assertNotNull(service);
            service.doSomething();
        }
    }
    

提交回复
热议问题