Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Injecting Domain Objects Instead of Infrastructure Components

DZone's Guide to

Injecting Domain Objects Instead of Infrastructure Components

· Java Zone
Free Resource

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

Dependency Injection is a widely used software design pattern in Java (and many other programming languages) that is used to achieve Inversion of Control. It promotes reusability, testability, maintainability and helps building loosely coupled components. Dependency Injection is the de facto standard to wire Java objects together, these days.
Various Java Frameworks like Spring or Guice can help implementing Dependency Injection. Since Java EE 6 there is also an official Java EE API for Dependency Injection available: Contexts and Dependency Injection(CDI).

We use Dependency Injection to inject services, repositories, domain related components, resources or configuration values. However, in my experience, it is often overlooked that Dependency Injection can also be used to inject domain objects.
A typical example of this, is the way the currently logged in user is obtained in Java many applications. Usually we end up asking some component or service for the logged in user.
The code for this might look somehow like the following snippet:

public class SomeComponent {
  @Inject
  private AuthService authService;
  
  public void workWithUser() {
    User loggedInUser = authService.getLoggedInUser();
    // do something with loggedInUser
  }
}

Here a AuthService instance is injected into SomeComponent. Methods of SomeComponent now use theAuthService object to obtain an instance of the logged in user.

However, instead of injecting AuthService we could inject the logged in user directly intoSomeComponent.
This could look like this:

public class SomeComponent {
  @Inject
  @LoggedInUser
  private User loggedInUser;

  public void workWithUser() {
    // do something with loggedInUser
  }
}

Here the User object is directly injected into SomeComponent and no instance of AuthService is required. The custom annotation @LoggedInUser is used to avoid conflicts if more than one (managed) bean of type User exist.

Both, Spring and CDI are capable of this type of injection (and the configuration is actually very similar). In the following section we will see how domain objects can be injected using Spring. After this, I will describe what changes are necessary to do the same with CDI.

Domain object injection with Spring
To inject domain objects like shown in the example above, we only have to do two little steps.
First we have to create the @LoggedInUser annotation: 

import java.lang.annotation.*;
import org.springframework.beans.factory.annotation.Qualifier;
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface LoggedInUser {
}

Please note the @Qualifier annotation which turns @LoggedInUser into a custom qualifier. Qualifiers are used by Spring to avoid conflicts if multiple beans of the same type are available.

Next we have to add a bean definition to our Spring configuration. We use Spring's Java configuration here, the same can be done with xml configuration.

@Configuration
public class Application {

  @Bean
  @LoggedInUser
  @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
  public User getLoggedInUser() {
    // retrieve and return user object from server/database/session
  }
}

Inside getLoggedInUser() we have to retrieve and return an instance of the currently logged in user (e.g. by asking the AuthService from the first snippet). With @Scope we can control the scope of the returned object. The best scope depends on the domain objects and might differ among different domain objects. For a User object representing the logged in user, request or session scope would be valid choices. By annotatinggetLoggedInUser() with @LoggedInUser, we tell Spring to use this bean definition whenever a bean with type User annotated with @LoggedInUser should be injected.

Now we can inject the logged in user into other components:

@Component
public class SomeComponent {

  @Autowired
  @LoggedInUser
  private User loggedInUser;
  
  ...
}

In this simple example the qualifier annotation is actually not necessary. As long as there is only one bean definition of type User available, Spring could inject the logged in user by type. However, when injecting domain objects it can easily happen that you have multiple bean definitions of the same type. So, using an additional qualifier annotation is a good idea. With their descriptive name qualifiers can also act as documentation (if named properly).

Simplify Spring bean definitions
When injecting many domain objects, there is chance that you end up repeating the scope and proxy configuration over and over again in your bean configuration. In such a situation it comes in handy that Spring annotations can be used on custom annotations. So, we can simply create our own @SessionScopedBeanannotation that can be used instead of @Bean and @Scope:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Bean
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public @interface SessionScopedBean {
}

Now we can simplify the bean definition to this:

@Configuration
public class Application {

  @LoggedInUser
  @SessionScopedBean
  public User getLoggedInUser() {
    ...
  }
}

Java EE and CDI
The configuration with CDI is nearly the same. The only difference is that we have to replace Spring annotations with javax.inject and CDI annotations.

So, @LoggedInUser should be annotated with javax.inject.Qualifier instead oforg.springframework.beans.factory.annotation.Qualifier (see: Using Qualifiers).
The Spring bean definition can be replaced with a CDI Producer method. Instead of @Scope the appropriateCDI scope annotation can be used.
At the injection point Spring's @Autowired can be replaced with @Inject.

Note that Spring also supports javax.inject annotations. If you add the javax.inject dependency to your Spring project, you can also use @Inject and @javax.inject.Qualifier. It is actually a good idea to do this because it reduces Spring dependencies in your Java code.

Conclusion
We can use custom annotations and scoped beans to inject domain objects into other components. Injecting domain objects can make your code easier to read and can lead to cleaner dependencies. If you only injectAuthService to obtain the logged in user, you actually depend on the logged in user and not onAuthService.
On the downside it couples your code stronger to the Dependency Injection framework, which has to manage bean scopes for you. If you want to keep the ability to use your classes outside a Dependency Injection container this can be a problem.
Which types of domain objects are suitable for injection highly depends on the application you are working on. Good candidates are domain objects you often use and which not depend on any method or request parameters. The currently logged in user is an object that might often be suitable for injection.

You can find the source of the shown example on GitHub.

Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.

Topics:

Published at DZone with permission of Michael Scharhag, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}