Dependency Injection using CDI with Jersey with/without abstract binding

非 Y 不嫁゛ 提交于 2019-12-13 03:00:17

问题


First of all, this is not opinionated question and I have read most of related questions in SO. I am seeking advise if below implemented solution is the right approach/method.

I have read many tutorials on how to implement DI in a jersey-based webapp and most of them recommend that its a must to create a beans.xml in WEB-INF/* in order to enable CDI but, I wonder if using Jersey's AbstractBinder achieve the same result?

I have a jersey-webapp that has the following in web.xml

    <servlet>
    <servlet-name>Test Jersey</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>

    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>com.test.config.AppConfig</param-value>
    </init-param>

And com.test.config.AppConfig as follow

public class AppConfig extends ResourceConfig {
    public AppConfig() {
        AbstractBinder binder = new AbstractBinder() {
            @Override
            protected void configure() {                    
              bind(Impl.class).to(Interface.class).in(Singleton.class);
            }
        };
        register(binder);
        register(MultiPartFeature.class);
        packages("..."); //packages
    }
}

and then I annotate the interfaces and the implementation gets injected

@Inject 
private SomeInterface someInterface; 

Above works just fine. Whatever that I want to be injected, I include it in the binder and then specify an injection point and it gets injected.

There is no beans.xml in WEB-INF/ directory and I wonder if using AbstractBinder inside AppConfig that extends ResourceConfig eliminate the need to declare beans.xml ?

Adding beans.xml would probably enable scanning of classes that would pave the way for DI when we annotate classes with @Component or @ManagedBean.

Regardless, I would be happy to hear your feedback/advise/suggestions/recommendations on whether to

  1. Stick with existing solution (shown above) for DI in Jersey because .... ?
  2. Switch to annotating classes (that needs to be injected) and use annotation-discovery of beans.xml because ... ?
  3. Jersey uses HK2 by default, is it worth using a different DI container or HK2 is good enough?
  4. What is your view on Jersey's Spring DI in comparison with JavaEE 6 CDI only for DI purposes?

There are many tutorials stating that CDI is not supported by Tomcat? but worked above using AbstractBinder and I guess its because I programmatically bind? Any comments.


回答1:


I do not have a clear answers and possibly there doesn't exist a correct one. Not least because Weld SE support was introduced in version 2.15 of Jersey and this certainly not without any reason. But I would like to give it a try:

  1. The shown solution works fine for non-complex project structures but declaring every single binding might not be the best solution
  2. You don't need to use beans.xml. Annotations and auto-binding works fine with some additional effort (see below)
  3. I'm not sure about this, but would say Weld seems to be more advanced. And of course you could mix CDI with some effort.
  4. (no answer here)

Here the example, which I think could be interesting:

Dependencies (Maven):

<dependency>
  <groupId>org.glassfish.hk2</groupId>
  <artifactId>hk2-metadata-generator</artifactId>
  <version>2.5.0-b05</version> <!-- HK2 version int. used by Jersey 2.23.2  -->
</dependency>
<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-servlet</artifactId>
</dependency>

An application event listener:

import org.glassfish.hk2.api.*;
import org.glassfish.jersey.server.*;

@Provider
public class ApplicationListener implements ApplicationEventListener {

    @Inject ServiceLocator serviceLocator;

    @Override
    public void onEvent(ApplicationEvent event) {
        switch (event.getType()) {
        case INITIALIZATION_FINISHED:
            onInitFinished();
            break;
        case DESTROY_FINISHED:
        case INITIALIZATION_APP_FINISHED:
        case INITIALIZATION_START:
        case RELOAD_FINISHED:
        default:
            break;
        }
    }

    @Override
    public RequestEventListener onRequest(RequestEvent requestEvent) { return null; }

    public void onInitFinished() {
        populate(serviceLocator);
    }

    private void populate(ServiceLocator serviceLocator) {
        DynamicConfigurationService dcs = serviceLocator.getService(DynamicConfigurationService.class);
        Populator populator = dcs.getPopulator();
        try {
            populator.populate();
        } catch (IOException | MultiException e) {
            throw new MultiException(e);
        }
    }
}

A contract:

import org.jvnet.hk2.annotations.Contract;

@Contract
public interface ExampleService {
    void executeSomething();
}

One or more services:

import javax.inject.Named;
import org.jvnet.hk2.annotations.Service;

@Service
@Named("bar")
public class BarService implements ExampleService {

    @Override
    public void executeSomething() { /* doBar */ }

}

Usage:

@Path("/")
public class TestResource {

    // either ...
    @Inject
    @Named("bar")
    private ExampleService bar;

    // or ...
    @Inject
    private IterableProvider<ExampleService> services;

}

Just an option to get rid of beans.xml (which I've never used or have seen) or declaration within ResourceConfig, but it might find interested parties :)

Additionally, it seems like Jersey 3.0 is comming ^^

Have a nice day!



来源:https://stackoverflow.com/questions/39003933/dependency-injection-using-cdi-with-jersey-with-without-abstract-binding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!