4

I've done projects using Spring Rest. Now we have a small rest project and planning to do with Jersey JAX-RS. I'm new to this and referred SO and other blogs to successfully implement a Rest api with dependency injection.

Have following code.

AppConfig.java

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/")
public class AppConfig extends Application {    
    @Override
    public Set<Class<?>> getClasses() {
        System.out.println("AppConfig");
        final Set<Class<?>> s = new HashSet<Class<?>>();
        s.add(Controller.class);
        s.add(AppFeature.class);
        return s;
    }
}

AppBinder.java

import org.glassfish.hk2.utilities.binding.AbstractBinder;

public class AppBinder extends AbstractBinder {
    @Override
    protected void configure() {
        System.out.println("AppBinder");
        bind(ReflectionService.class).to(ReflectionService.class);
    }
}

AppFeature.java

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;

public class AppFeature implements Feature {
    @Override
    public boolean configure(FeatureContext context) {
        System.out.println("AppFeature");
        context.register(new AppBinder());
        return true;
    }
}

Controller.java

@Path("/")
public class Controller {   
    @Inject
    Service service;    
    public Controller(){
        System.out.println("Controller created");
    }
    // other methods
}

Service.java

@Singleton
public class Service    
    public Service(){
        System.out.println("Service instance created");
    }
    // other methods
}

I assume that each instance of Controller and Service is created on Tomcat 8 server startup and dependency injection is done. But during startup, got this on console

INFO: Registering the Jersey servlet application, named com.sample.auto2.AppConfig, at the servlet mapping /*, with the Application class of the same name.

AppConfig

AppConfig

Nov 15, 2016 12:22:20 PM org.glassfish.jersey.server.ApplicationHandler initialize INFO: Initiating Jersey application, version Jersey: 2.6 2014-02-18 21:52:53...

AppFeature

AppBinder

Nov 15, 2016 12:22:21 PM org.apache.catalina.startup.HostConfig deployDirectory

Each time, we send a request, got following in console

Service instance created

Controller created

My Questions

  1. Service,Controller constructors is being called whenever we send an http request; does it create instances in each request or just calling constructor?
  2. Why System.out in AppConfig is called twice?
  3. Is there a better way for setting up my small project, which does not have any db access and only three post endpoints?

EDIT:

As per the links provided by @Harikrishnan, added @Singleton for Controller class. Now the constructors called only once (at the very first request - Why not during server startup!!).

But why Service class constructor called on each request (earlier before adding @Singleton to Controller), even if its singleton? Also the other problems remain.

EDIT 2:

Thanks @peeskillet. So these are the results for me.

  1. This called constructors only once at very first request

    bind(ReflectionService.class).to(ReflectionService.class).in(Singleton.class);
    bind(Controller.class).to(Controller.class).in(Singleton.class);
    
  2. This give the error on http request

    bind(ReflectionService.class).to(ReflectionService.class).in(Immediate.class);
    
    java.lang.IllegalStateException: Could not find an active context for org.glassfish.hk2.api.Immediate
    

    But it's ok, because

  3. This called constructor on server startup and only once

    bind(new ReflectionService()).to(ReflectionService.class);
    bind(new Controller()).to(Controller.class);
    
  4. By this, injection done on startup, but 404 error on http request. (Thought Controller is configured in AppBinder, then why in AppConfig)

    @ApplicationPath("/")
    public class AppConfig extends ResourceConfig  {    
        public AppConfig() {
            register(new AppBinder());
        }
    }
    
  5. And this make it run, as you said!

    @ApplicationPath("/")
    public class AppConfig extends ResourceConfig  {        
        public AppConfig() {
            register(new AppBinder());
            register(Controller.class);
        }
    }
    

FINALLY THESE ARE ALL I NEEDED

public class AppBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(new ReflectionService()).to(ReflectionService.class);
        bind(new Controller()).to(Controller.class);
    }
}

@ApplicationPath("/")
public class AppConfig extends ResourceConfig  {    
    public AppConfig() {
        register(new AppBinder());
        register(Controller.class);
    }
}
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
Ramanujan R
  • 1,601
  • 2
  • 23
  • 43

2 Answers2

5

Service,Controller constructors is being called whenever we send an http request; does it create instances in each request or just calling constructor?

Unfortunately, the @Singleton doesn't have any effect when binding using the AbstractBinder. We need to explicitly say it should be a singleton

bind(ReflectionService.class).to(ReflectionService.class).in(Singleton.class);

The default behavior is a "per lookup" scope, which means that a new instance is created every time the service is requested

(at the very first request - Why not during server startup!!)

This is how it works. There is also an ImmediateScope, which will cause it to be created on startup

 bind(ReflectionService.class).to(ReflectionService.class).in(ImmediateScope.class)

Or you can just use an instance instead of class, and it will automatically be a singleton

bind(new ReflectionService()).to(ReflectionService.class)

Service,Controller constructors is being called whenever we send an http request; does it create instances in each request or just calling constructor?

This is the default behavior. A new instance of the resource class for each request. As mentioned, if you only want once instance, then mark it as a @Singleton

Why System.out in AppConfig is called twice?

Not sure, probably just required for Jersey internal processing on bootstrap

Is there a better way for setting up my small project, which does not have any db access and only three post endpoints?

Your set up is fine, but if you are using Jersey then you should use the ResourceConfig (extension of Application) class instead of Application

@ApplicationPath("/")
public class AppConfig extends ResourceConfig {
    public AppConfig() {
        register(new AppBinder());
        register(Controller.class);
    }
}

With this, you don't need to wrap the AppBinder in the AppFeature. Jersey already knows how to process the AppBinder

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thanks @peeskillet. That was very helpful. I would like to mark my observations too. Cant comment, as its too long. So editing your answer – Ramanujan R Nov 16 '16 at 05:02
  • 1
    I just edited your post instead of adding it to mine. Oh and for the Immediate scope. I forget you also need to configure [this](http://stackoverflow.com/a/28123656/2587435). Also you don't need to add the controller to the binder – Paul Samsotha Nov 16 '16 at 05:15
  • Removing controller from binder creates its object on each request without `@Singleton`. Then adding `@Singleton` creates it only once on first request. But i need it during server startup, and worked with adding controller to binder. Is there any issue for that? – Ramanujan R Nov 16 '16 at 05:37
  • See the link in my comment – Paul Samsotha Nov 16 '16 at 05:37
  • You could also use `register(new Controller())` – Paul Samsotha Nov 16 '16 at 05:40
  • 1
    But really, if you want, you could simply just do `register(new Controller(new Service())` and forget the binder. No injection needed – Paul Samsotha Nov 16 '16 at 05:44
  • Ok @peeskillet. Thanks – Ramanujan R Nov 16 '16 at 06:04
0

By default Jersey creates a new instance of the resource class for every request. So if you don't annotate the Jersey resource class, it implicitly uses @RequestScoped scope

Please read https://stackoverflow.com/a/20505899/721597

Also look at Jersey 2 singleton dependency injection creates multiple instances

Community
  • 1
  • 1
Harikrishnan
  • 3,664
  • 7
  • 48
  • 77