Spring Autowiring by Example

Spring Autowiring by Example

Spring is a Java library which delivers a useful feature: Inversion Of Control. Basically, instead of instantiating your Java services with new, Spring does it for you.

You may be wondering:

  • That’s nice, but isn’t new already enough?
  • How does the @Autowired annotation work?
  • How does Spring instantiated and lookup the right beans and services for me?

Good news! We’ve got you covered. I know how difficult Spring is to understand, I’ve been there. These carefully crafted real-world examples should speak to you.

When you application grows bigger and bigger, you usually create services and use them directly in your code via static methods like in the following:


public class MyService {
  private static MyService instance = null;

  public void foo() {

  }

  public static MyService getInstance() {
    if (instance == null) {
      instance = new MyService();
    }
    return instance;
  }
}

// Code which uses the service

MyService.getInstance().foo();

While this approach is quite reasonable in a small program, it has numerous disadvantages:

  • Unit-Testing is difficult: statically accessed resources are difficult to mock,
  • Maintainability: code is tightly coupled to the services it uses.

So, how can Spring solve those issues? The answer is: Autowiring. Let’s see how it works!

Maven Dependencies

First, let’s configure the Maven dependencies in our pom.xml:


<properties>
  <jackson.version>2.9.3</jackson.version>
  <spring.version>5.0.1.RELEASE</spring.version>
  <spring.boot.version>1.5.9.RELEASE</spring.version>
</properties>

<dependencies>
  <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>${spring.boot.version}</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
  </dependency>
</dependencies>

In this example, we’re going to use Spring Boot.

Bootstrapping via Spring

We need a bootstrap in our application with a main which delegates all the work to Spring:


package com.octoperf;

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

Basically, this tells Spring Boot to start the application by scanning all the classes under the com.octoperf package. For more information see the SpringBootApplication documentation.

Service Interface

Great! Now we are going to create a service which wraps the Jackson ObjectMapper. The purpose is to hide the Jackson API from the rest of our code. This is called hiding the implementation details.


package com.octoperf.tools.jackson.mapper;

import com.fasterxml.jackson.core.type.TypeReference;

import java.io.IOException;
import java.lang.reflect.Type;

public interface JsonMapperService {

  String toJson(Object instance) throws IOException;

  <T> T fromJson(String json, Class<T> clazz) throws IOException;
}

This service provides two methods:

  • serializing an object instance to Json,
  • and deserializing a json to an object instance.

I’m already hearing you asking: Why do we need to define an interface? Can’t we define the service directly? And that’s a good question!

There are multiple advantages of using an interface instead of a concrete class:

  • Decoupling: instead of depending on the Jackson service here, we depend on an API agnostic service interface. The code which uses this service doesn’t know it’s being implemented with Jackson underneath,
  • Testability: the code where the JsonMapperService is injected can be easily mocked. Interfaces are known to be easy to mock and work with.

Also, you may take a look at Why Impl Classes are Evil to better name your implementation classes.

Service Implementation

Now, let’s dive into the implementation which is pretty straight forward. The following example service illustrates the use of constructor injection (or constructor autowiring)


package com.octoperf.tools.jackson.mapper;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Set;

import static com.google.common.base.Preconditions.checkNotNull;

@Service
final class JacksonJsonMapperService implements JsonMapperService {

  private final ObjectMapper mapper;

  JacksonJsonMapperService(final ObjectMapper mapper) {
    super();
    this.mapper = checkNotNull(mapper);
  }

  @Override
  public String toJson(final Object instance) throws IOException {
    return mapper.writeValueAsString(instance);
  }

  @Override
  public <T> T fromJson(final String json, final Class<T> clazz) throws IOException {
    return mapper.readValue(json, clazz);
  }
}

As you can see, there are some interesting points to review here:

  • @Service annotation on JacksonJsonMapperService: it tells Spring this is a service, instantiate it,
  • JacksonJsonMapperService constructor: it requires an ObjectMapper.

What does Spring then? It does the following:

  • Discover JacksonJsonMapperService is annotated with @Service and decides to instantiate it,
  • Lookup ObjectMapper in the Spring context and inject it in the constructor,
  • Register JacksonJsonMapperService as an instance of JsonMapperService.

Spring does everything through Java Reflection. On a practical standpoint, it performs instantiation and injection through a generic code which is completely decoupled from your own code.

Why isn’t there any @Autowired annotation? As of Spring 4, this annotation is not required anymore when performing constructor autowiring.

Most of the time, the service implementation should:

  • Have a package-protected class,
  • Be in a maven module separated from the interface.

See Separation of Concerns for more information.

Field Autowiring

What about Field Autowiring? It would look like this:


@Service
final class JacksonJsonMapperService implements JsonMapperService {

  @Autowired
  private ObjectMapper mapper;

  @Override
  public String toJson(final Object instance) throws IOException {
    return mapper.writeValueAsString(instance);
  }

  @Override
  public <T> T fromJson(final String json, final Class<T> clazz) throws IOException {
    return mapper.readValue(json, clazz);
  }
}

The code is obviously more concise. However, this kind of injection is discouraged because the JacksonJsonMapperService is then mutable. The reference to the ObjectMapper could be changed within the service code.

Field autowiring may be useful if you experience circular dependency injection.

Setter Autowiring

What about Setter Autowiring? Let’s see this case too:


@Service
final class JacksonJsonMapperService implements JsonMapperService {

  private ObjectMapper mapper;

  @Override
  public String toJson(final Object instance) throws IOException {
    return mapper.writeValueAsString(instance);
  }

  @Override
  public <T> T fromJson(final String json, final Class<T> clazz) throws IOException {
    return mapper.readValue(json, clazz);
  }

  @Autowired
  public void setMapper(final ObjectMapper mapper) {
    this.mapper = checkNotNull(mapper);
  }
}

While this approach perfectly works too, it’s not recommended for the same reason as before: prefer immutable instead of mutable ones.

Setter autowiring may be useful if you experience circular dependencies.

Spring Java Configuration

Another approach is to use a separate Spring Java Config like this one:


package com.octoperf.tools.jackson.mapper;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.inject.Singleton;
import java.util.Set;

import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
import static com.fasterxml.jackson.databind.MapperFeature.SORT_PROPERTIES_ALPHABETICALLY;

@Module
@Configuration
public class JacksonConfig {
  
  @Bean
  @Primary
  ObjectMapper objectMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    mapper.findAndRegisterModules();
    mapper.setSerializationInclusion(NON_NULL);
    mapper.enable(SORT_PROPERTIES_ALPHABETICALLY);
    return mapper;
  }

  @Bean
  static JsonMapperService jsonMapperService(final ObjectMapper mapper) {
    return new JacksonJsonMapperService(mapper);
  }
}

Let’s decrypt what’s done here:

  • JacksonConfig defines methods annotated with @Bean: those methods provide new instances of different beans,
  • ObjectMapper is defined as @Primary because Spring Boot already provides one, but we need our own.
  • methods in Spring Java Configuration can be either static or instance bound.

By defining a JacksonConfig, we must remove the @Service annotation on the JacksonJsonMapperService. Don’t use both or you will end up with Spring blowing at your face that 2 instances of your bean have been instantiated. (and Spring doesn’t know which one to inject in services that require it)

Optional Dependencies

Optional dependencies are dependencies which are not required when autowiring the services and/or beans. By default, Spring throws an exception when a service that should be autowired by the dependencies cannot be found:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [com.fasterxml.jackson.databind.ObjectMapper] found for dependency: 
expected at least 1 bean which qualifies as autowire candidate for this dependency. 
Dependency annotations: 
{@org.springframework.beans.factory.annotation.Autowired(required=true)}

One solution could be to mark the dependency as **not required**:

@Service
final class JacksonJsonMapperService implements JsonMapperService {

  @Autowired(required=false)
  private ObjectMapper mapper;

  @Override
  public String toJson(final Object instance) throws IOException {
    if (mapper != null) {
      return mapper.writeValueAsString(instance);
    }
    return null;
  }

  @Override
  public <T> T fromJson(final String json, final Class<T> clazz) throws IOException {
    if (mapper != null) {
      return mapper.readValue(json, clazz);
    }
    return null;
  }
}

While this is perfectly valid, this approach usually leads to cluttered code:

  • What should we do when the dependency is not there?
  • How should the code behave: throw an exception or return null instead?

Think twice when using optional dependencies. This is usually a bad practice and should be avoided.

Using @Qualifier

The @Qualifier annotation can be useful in a situation where you need to differentiate instances of the same type / interface.

Let’s see an example.


package com.octoperf.commons.eventbus.spring;

import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.SubscriberExceptionHandler;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;

@Configuration
class EventBusConfig {

  @Bean(destroyMethod="shutdown")
  @ConditionalOnMissingBean
  ListeningExecutorService listeningExecutorService() {
    return MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(4));
  }

  @Bean
  EventBus syncEventBus() {
    return new EventBus();
  }

  @Bean
  AsyncEventBus asyncEventBus(final ListeningExecutorService executor) {
    return new AsyncEventBus(executor);
  }
}


As you see in the configuration above, there are two instances of EventBus being wired. Any attempt to autowire an EventBus without @Qualifier would lead to an exception thrown from Spring.

To overcome this, let’s now see how the implementation service uses the @Qualifier annotation:


package com.octoperf.commons.eventbus.spring;

import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.octoperf.commons.eventbus.api.EventBusService;
import lombok.NonNull;
import lombok.experimental.FieldDefaults;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import static java.util.Objects.requireNonNull;
import static lombok.AccessLevel.PRIVATE;

@Service
@FieldDefaults(level=PRIVATE, makeFinal=true)
final class EventBusOrchestrationService implements EventBusService {
  AsyncEventBus async;
  EventBus sync;

  EventBusOrchestrationService(
    @Qualifier("asyncEventBus") final AsyncEventBus async,
    @Qualifier("syncEventBus") final EventBus sync) {
    super();
    this.async = requireNonNull(async);
    this.sync = requireNonNull(sync);
  }

  @Override
  public void register(final Object listener) {
    async.register(listener);
    sync.register(listener);
  }

  @Override
  public void postEvent(final Object event) {
    async.post(event);
  }

  @Override
  public void syncPostEvent(final Object event) {
    sync.post(event);
  }

}

@Qualifier allows to make a distinction between the two instances. Any attempt to autowire EventBusOrchestrationService without qualifier would end up with the following exception:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'eventBusOrchestrationService': 
Unsatisfied dependency expressed through constructor parameter 1; 
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.google.common.eventbus.EventBus' available: 
expected single matching bean but found 2: syncEventBus,asyncEventBus

Please, be careful when using @Qualifier. It’s quite dangerous because it can lead to nasty errors. It’s generally a bad practice to require a qualifier: it requires the service which is autowired to know which implementations are available. @Qualifier scope should be as narrow as possible, like in the example above.

You can even create your own Qualifier derived from Spring’s @Qualifier annotation:


@Qualifier
@Target({
  ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyQualifier {
     
    String value();
 
}

This could be a good compromise: provide your own qualifier annotations alongside your service interface. Developers who require your service will then be aware that multiple ones are available, and one must be chosen.

To finish, Spring is also capable of disambiguation by using bean names:



@Service
@FieldDefaults(level=PRIVATE, makeFinal=true)
final class EventBusOrchestrationService implements EventBusService {
  AsyncEventBus async;
  EventBus sync;

  EventBusOrchestrationService(
    final AsyncEventBus asyncEventBus,
    final EventBus syncEventBus) {
    super();
    this.async = requireNonNull(asyncEventBus);
    this.sync = requireNonNull(syncEventBus);
  }

  @Override
  public void register(final Object listener) {
    async.register(listener);
    sync.register(listener);
  }

  @Override
  public void postEvent(final Object event) {
    async.post(event);
  }

  @Override
  public void syncPostEvent(final Object event) {
    sync.post(event);
  }

}

Remember the names of the methods in the EventBusConfig , asyncEventBus() and syncEventBus(). By reusing the name of those methods in the constructor parameters, Spring is capable of resolving the right beans to inject. You can try this unit-test if you want:


package com.octoperf.commons.eventbus.spring;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.assertNotNull;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={EventBusConfig.class, EventBusOrchestrationService.class})
public class EventBusOrchestrationSpringTest {
  
  @Autowired
  private EventBusOrchestrationService service;
  
  @Test
  public void shouldPostEvent() {
    assertNotNull(service);
  }
}

It runs Spring autowiring isolated within the maven module. The test succeeds when the service is properly instantiated and autowired.

Collection Autowiring

This is one of the most powerful ways to use Spring to write Extensible code which follows the Open/Closed Principle. It’s also known as List autowiring or Autowire List of beans.

Don’t worry, let’s see a concrete example!

We’re going to improve our JsonMapperService to allow third party code to register type mappings.

package com.octoperf.tools.jackson.mapper;

@FunctionalInterface
public interface JsonRegistrator {

  void register(JsonRegistry mapper);
}

A JsonRegistrator receives a JsonRegistry which can be used to register sub-types:



@FunctionalInterface
public interface JsonRegistry {

  void registerSubtype(Class<?> clazz, String name);
}

Our JacksonJsonMapperService is then going to implement JsonRegistry:


package com.octoperf.tools.jackson.mapper;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Set;

import static com.google.common.base.Preconditions.checkNotNull;

@Service
final class JacksonJsonMapperService implements JsonMapperService, JsonRegistry {

  private final ObjectMapper mapper;

  JacksonJsonMapperService(final ObjectMapper mapper, final Set<JsonRegistrator> registrators) {
    super();
    this.mapper = checkNotNull(mapper);
    for (final JsonRegistrator registrator : registrators) {
      registrator.register(this);
    }
  }

...

  @Override
  public void registerSubtype(final Class<?> clazz, final String name) {
    mapper.registerSubtypes(new NamedType(clazz, name));
  }
}


See how a Set<JsonRegistrator> is injected in the constructor? Spring automatically resolves and instantiates all JsonRegistrator instances before instantiating the JacksonJsonMapperService service. It packs them into a Set and provides it to the service.

It’s a great way to implement the Strategy Pattern! Here is an example JsonRegistrator implementation:


package com.octoperf.workspace.entity;

import com.octoperf.tools.jackson.mapper.JsonRegistrator;
import com.octoperf.tools.jackson.mapper.JsonRegistry;
import org.springframework.stereotype.Component;

@Component
final class WorkspaceAccessRegistrator implements JsonRegistrator {

  @Override
  public void register(final JsonRegistry mapper) {
    mapper.registerSubtype(AdminAccess.class, "AdminAccess");
    mapper.registerSubtype(TesterAccess.class, "TesterAccess");
    mapper.registerSubtype(ViewerAccess.class, "ViewerAccess");
  }
}


See how this makes your code extensible? Developers used to modify your service to include their subtype mappings. Now, they can simply create a JsonRegistrator bean to do this. It’s a powerful example of the Open/Closed Principled in action!

That’s already the end of our tutorial! Feel free to share your autowiring techniques using Spring!

By - CTO.
Tags: Java Spring

Comments

 

Thank you

Your comment has been submitted and will be published once it has been approved.

OK

OOPS!

Your post has failed. Please return to the page and try again. Thank You!

OK