Skip to content

Latest commit

 

History

History
64 lines (51 loc) · 5.69 KB

guide-dependency-injection.asciidoc

File metadata and controls

64 lines (51 loc) · 5.69 KB

Dependency Injection

ATTENTION: You are on the develop branch. This has been renamed to master. The develop branch will not be maintained anymore. It is only left here to avoid broken links to existing content. Please update links to point to the master branch. For details look at issue #320.

Dependency injection is one of the most important design patterns and is a key principle to a modular and component based architecture. The Java Standard for dependency injection is javax.inject (JSR330) that we use in combination with JSR250.

There are many frameworks which support this standard including all recent Java EE application servers. We recommend to use Spring (also known as springframework) that we use in our example application. However, the modules we provide typically just rely on JSR330 and can be used with any compliant container.

Key Principles

A Bean in CDI (Contexts and Dependency-Injection) or Spring is typically part of a larger component and encapsulates some piece of logic that should in general be replaceable. As an example we can think of a Use-Case, Data-Access-Object (DAO), etc. As best practice we use the following principles:

  • Separation of API and implementation
    We create a self-contained API documented with JavaDoc. Then we create an implementation of this API that we annotate with @Named. This implementation is treated as secret. Code from other components that wants to use the implementation shall only rely on the API. Therefore we use dependency injection via the interface with the @Inject annotation.

  • Stateless implementation
    By default implementations (CDI-Beans) shall always be stateless. If you store state information in member variables you can easily run into concurrency problems and nasty bugs. This is easy to avoid by using local variables and separate state classes for complex state-information. Try to avoid stateful CDI-Beans wherever possible. Only add state if you are fully aware of what you are doing and properly document this as a warning in your JavaDoc.

  • Usage of JSR330
    We use javax.inject (JSR330) and JSR250 as a common standard that makes our code portable (works in any modern Java EE environment). However, we recommend to use the springframework as container. But we never use proprietary annotations such as @Autowired instead of standardized annotations like @Inject. Generally we avoid proprietary annotations in business code (common and logic layer).

  • Simple Injection-Style
    In general you can choose between constructor, setter or field injection. For simplicity we recommend to do private field injection as it is very compact and easy to maintain. We believe that constructor injection is bad for maintenance especially in case of inheritance (if you change the dependencies you need to refactor all sub-classes). Private field injection and public setter injection are very similar but setter injection is much more verbose (often you are even forced to have javadoc for all public methods). If you are writing re-usable library code setter injection will make sense as it is more flexible. In a business application you typically do not need that and can save a lot of boiler-plate code if you use private field injection instead. Nowadays you are using container infrastructure also for your tests (see spring integration tests) so there is no need to inject manually (what would require a public setter).

  • KISS
    To follow the KISS (keep it small and simple) principle we avoid advanced features (e.g. AOP, non-singleton beans) and only use them where necessary.

Example Bean

Here you can see the implementation of an example bean using JSR330 and JSR250:

@Named
public class MyBeanImpl implements MyBean {
  @Inject
  private MyOtherBean myOtherBean;

  @PostConstruct
  public void init() {
    // initialization if required (otherwise omit this method)
  }

  @PreDestroy
  public void dispose() {
    // shutdown bean, free resources if required (otherwise omit this method)
  }
}

It depends on MyOtherBean that should be the interface of an other component that is injected into the field because of the @Inject annotation. To make this work there must be exactly one implementation of MyOtherBean in the container (in our case spring). In order to put a Bean into the container we use the @Named annotation so in our example we put MyBeanImpl into the container. Therefore it can be injected into all setters that take the interface MyBean as argument and are annotated with @Inject.

In some situations you may have an Interface that defines a kind of "plugin" where you can have multiple implementations in your container and want to have all of them. Then you can request a list with all instances of that interface as in the following example:

  @Inject
  private List<MyConverter> converters;

Please note that when writing library code instead of annotating implementation with @Named it is better to provide @Configuration classes that choose the implementation via @Bean methods (see @Bean documentation). This way you can better "export" specific features instead of relying library users to do a component-scan to your library code and loose control on upgrades.

Bean configuration

Wiring and Bean configuration can be found in configuration guide.