Quickstart

Structure

Within DiVine, there are three key concepts: services, containers and dependencies.

  • Services are class definitions, that share a common logic. Most common services are either singleton (share one instance throughout your program) or transient (have unique instances).

  • Containers are collections of dependencies. A container is responsible for instantiating a service, managing its lifecycle and handling shared values.

  • Dependencies are instances of services or given values, that are stored in a container.

Usage

Service definitions

The following code shows an example of a simple service, that is field-injected in another service.

@Service
class Logger {
    public void log(String message) {
        System.out.println("LOG: " + message);
    }
}

@Service
class Application {
    @Inject
    private Logger logger;
    
    public void logic() {
        logger.log("Starting application");
    } 
}

Note that DiVine requires services to have a @Service annotation, called the service descriptor. This tells the dependency injector, how the annotated service behaves.

The @Inject annotation tells DiVine, to inject the dependency into the target field.

Global values

You may also store shared values in the container. You can get rid of your magic values in your code. If you want to access a constant, just request it from the container.

void initConfig() {
    // store shared values in the container
    Container.set("SECRET_KEY", "abc123");
}

@Service
class ApiController {
    // inject shared value into the parent service
    @Inject(token = "SECRET_KEY")
    private String secretKey;
    
    public void authenticate() {
        // you can also manually request values from the container
        requestToken(secretKey, Container.get("OTHER_VALUE"));
    }
}

Note that these values are shared within the container. If you want parts of your code to share different values for a key, you may use different containers. If you want to share a value accross all of your containers, you may use the global container,.

Improved testing

Using DiVine can improve the way that you can test your applications. By using interfaces as services, you can easily use a mock implementation.

You can register mock implementations in your test files, therefore you do not need to modify anything in your application code for testing.

@Service(implementation = MongoDBUserController.class)
interface UserController {
    User getUser(String userId);
}

@Service
class MongoDBUserController implements UserController {
    @Override
    public User getUser(String userId) {
        return userCollection.find(new Document("userId", userId)).first();
    }
}

Inside your application, you can request the UserController dependency as such:

void myUserHandler() {
    UserController controller = Container.get(UserController.class);
    User user = controller.getUser("test");
    System.out.println("Welcome user, " + user.getName());
}

Inside your test files you could do the following code:

@Service
class UserControllerMock implements UserController {
    @Override
    public User getUser(String userId) {
        return createUserMock(userId);
    }
}

@BeforeAll
public void mockUserController() {
    Container.implement(UserController.class, UserControllerMock.class);
}

@Test
public void test_UserController_getUser() {
    UserController controller = Container.get(UserController.class);
    User user = controller.getUser("test");
    asserEquals("ExampleUser", user.getName());
}

Note that @Service(implementation = MyImpl.class) tells DiVine, to implement the interface with MyImpl by default, unless specified otherwise.

Advanced usage

DiVine features a vast range of features that ensure that you have the best experience while using the dependency injector. You may navigate through the following pages that showcase some of the most important utilities, that you can use to improve your code.

Last updated