javacdihk2

Service injection issue with HK2: why doesn't @Inject work despite @Contract on interface and @Service on implementation?


I am using the HK2 framework to manage my services, and I have annotated my service interface with @Contract and the service implementation with @Service. However, when I try to inject the service into my code using @Inject, it doesn't work. I receive an error indicating that the service was not found. Why is this happening? Have I missed something in the HK2 configuration or in my code? Thank you in advance for your help.

My dependencies:

<dependency>
    <groupId>jakarta.inject</groupId>
    <artifactId>jakarta.inject-api</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.hk2</groupId>
    <artifactId>hk2-utils</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.hk2</groupId>
    <artifactId>hk2-api</artifactId>
</dependency>
<dependency>
    <groupId>org.glassfish.hk2</groupId>
    <artifactId>hk2-metadata-generator</artifactId>
</dependency>

IUserService:

@Contract
public interface IUserService {
    List<User> getAllUsers();
}

UserService:

@Service
public class UserService implements IUserService {

    @Override
    public List<User> getAllUsers() {
        User user1 =new User();
        User user2 =new User();
        User user3 =new User();
        // ... setup users fields
        return List.of(user1, user2, user3);
    }
}

UserResource:

public class UserResource {
    @Inject
    private IUserService service;

    public List<User> getAllUsers() {
        return service.getAllUsers();
    }
}

App:

public class App {
    public static void main(String[] args) {
        UserResource userResource = new UserResource();
        System.out.println(userResource.getAllUsers());
    }
}

Solution

  • HK2 isn't magic, and needs to know what your service bindings are. I highly recommend using the automatic service population as described here.

    You can also manually bind classes or service descriptions into your main, which is difficult to maintain but does the job.

    Firstly, add @Service to your UserResources (only hk2 services can inject other hk2 services [there are exceptions]):

    @Service
    public class UserResource {
        @Inject
        private IUserService service;
    
        public List<User> getAllUsers() {
            return service.getAllUsers();
        }
    }
    

    Then fix up your main by getting a ServiceLocator and populating it with your services. Then get your UserResource FROM hk2 rather than newing it up yourself. In the end hk2 is a lazy system meaning no service is created without you first asking for a service yourself [again, there are some exceptions to that rule]:

    public class App {
        public static void main(String[] args) {
            ServiceLocator locator = ServiceLocatorFactory.getInstance().create("myServiceLocator");
            
            ServiceLocatorUtilities.addClasses(locator
                 , UserService.class
                 , UserResource.class);
            
            UserResource myUserResource = locator.getService(UserResource.class);
        }
    }