Skip to content
Spring Annotations Demystified

Spring Annotations Demystified

If you're here, it's probably because you have never really understood the differences between those Spring annotations:

  • What is the Spring @Service annotation for?
  • What's the key difference between a class annotated with @Component and @Service?
  • How can I use @PostConstruct and @PreDestroy?

The bad news is Your search is over! It's finally time to get a better understanding of when and how to use those annotations.

The section above describe each annotation and the best way to use them to fully leverage the power of Spring Framework.

All those annotations are designed to:

  • annotate classes,
  • enable instantiation and autowiring of those annotated classes.

But, how do they differ from each other? Let's dive into Spring's internals to find out!

OctoPerf is JMeter on steroids!
Schedule a Demo

General Annotations

Configuration

The @Configuration annotation is used to mark a class as being a Java Spring Config. Here is a simple example:

@Configuration
class JacksonConfig {

  @Bean
  ObjectMapper objectMapper() {
    return new ObjectMapper();
  }

}

Spring Configurations aim only to provide bean instances. Configuration are usually discovered through component-scanning or Spring XML Configuration.

Bean

The @Bean annotation is exclusively reserved to be used to annotate a method of a @Configuration annotated class. This annotation indicates the method is returning a bean that must be registered within the application context.

Autowired

The @Autowired annotation is used to mark a constructor, a method or a field as eligible for Spring autowiring. Basically, autowiring is the process of injecting an instance of a wanted class or interface into another object. Let's see a real-world example:

@Slf4j
@Service
@FieldDefaults(level=PRIVATE, makeFinal=true)
final class DriverProvisioningService implements DockerCloudInstanceService {
  Map<DockerProviderDriver, DriverCloudInstanceService> services;
  DockerInstanceService instances;

  @Autowired
  DriverProvisioningService(
    final List<DriverCloudInstanceService> services,
    final DockerInstanceService instances) {
    super();
    this.services = services
      .stream()
      .collect(toMap(DriverCloudInstanceService::getDriver, Function.identity()));
    this.instances = checkNotNull(instances);
  }

...

}

In the example above, the DriverProvisioningService class is an implementation of DockerCloudInstanceService. The class is annotated with @Service, telling Spring this is a service. We'll see later what this means.

The constructor is annotated with @Autowired to tell Spring to instantiate the DriverProvisioningService with this constructor, and provide instances for the specified constructor parameters.

Please note that @Autowired is only required when:

  • multiple constructors are defined,
  • injecting a bean directly in a field,
  • or injecting a bean through a setter.

Most of the time, when performing constructor based bean injection, the @Autowired annotation can be ignored.

PostContruct and PreDestroy

The @PostContruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. Here is an example:

@Service
@FieldDefaults(level = PRIVATE, makeFinal = true)
final class SpringSampleService implements SampleService {
  private static final int SCROLL_SIZE = 1000;
  public static final List<SampleType> SAMPLE_INDICES = ImmutableList.of(
    HIT,
    HTTP,
    CONTAINER,
    MONITOR,
    ERROR
  );

  DatabaseIndiceService indices;

  SpringSampleService(final DatabaseIndiceService indices) {
    super();
    this.indices = requireNonNull(indices);
  }

  @PostConstruct
  void postContruct() throws IOException {
    System.out.println("Initializing bean using @PostContruct");
  }

  @PreDestroy
  public void destroy() throws Exception {
    System.out.println("Destroying bean using @PreDestroy");
  }

The method annotated by @PostConstruct must fulfill the following criterias:

  • must not have any parameter (except InvocationContext when annotating a method on a bean interceptor)
  • must have package or public visibility,
  • it can throw checked exceptions,

The postContruct method allows to perform actions once all the beans have been instantiated and autowired. It's a good way to execute some code on application startup.

Predestroy works exactly the same way, but enables to execute some arbitrary code before the application is shutdown.

Resource

The @Resource annotation marks a resource that is needed by the application. It's the equivalent of @Autowired from Spring, but @Resource is part of the JSR-250 like @PostConstruct and @PreDestroy.

public class MyService {
  @Resource(name="personRepo")
  private PersonRepository repository;
}

@Resource annotation has the following execution paths: (by order)

  • Match by Name,
  • Match by Type,
  • Match by Qualifier.

On the first match, the bean matching the configuration is injected.

Suppose we have a Spring configuration as following:

@Configuration
public class ApplicationContextTestResourceNameType {

    @Bean(name="namedPerson")
    public File namedPerson() {
        File namedFile = new File("namedFile.txt");
        return namedFile;
    }
}

Bean Annotations

Bean annotations are used to tell Spring to instantiate a given class and register it into the application context.

Scope

The @Scope annotation defines the scope of the instance of the bean being marked with. By default, Spring creates singleton for each bean. But, some beans can be session scoped, or even request scoped in web applications.

Here are the known scopes:

  • singleton: there is only one instance of the bean per Spring context,
  • prototype: there is one instance of the bean per injection in any other bean, or each time it's requested,
  • request: This scopes a bean definition to an HTTP request. Only valid in the context of a web-aware Spring ApplicationContext,
  • session: the bean's scope is narrowed to the HTTP session,
  • global-session: This scopes a bean definition to a global HTTP session.

99% of beans are usually singleton scoped. Let's see an example:

@Component
public class Person {

}

@Service
public class PersonService {
  @Autowired
  private Person person;
}

@Service
public class MembershipService {
  @Autowired
  private Person person;
}

In singleton scope, both injected person beans are the same instance. In prototype scope, each person instance will be unique.

Spring Component

@Component is truely the center piece of Spring autowiring mechanisms:

Added as of Spring 2.5, the @Component annotation is used to annotate a type (or class). @Component is the most general Spring Autowiring annotation. You can use it to annotate general purpose classes with no special semantic (unlike Database Repositories, long-life services etc.) like the following:

@Component
@FieldDefaults(level = PRIVATE, makeFinal = true)
final class SLAProfileEventListener {
  SLAProfileCrudService slas;
  VirtualUserCleanupService cleanup;

  SLAProfileEventListener(
    final SLAProfileCrudService slas,
    final VirtualUserCleanupService cleanup,
    final EventBusService eventBus) {
    super();
    this.slas = requireNonNull(slas);
    this.cleanup = requireNonNull(cleanup);
    eventBus.register(this);
  }

  @Subscribe
  @AllowConcurrentEvents
  public void onDeleteProject(final DeleteProjectEvent e) {
    final Project project = e.getProject();
    final List<String> ids = slas
      .findByProjectId(project.getId())
      .stream()
      .map(Entity::getId)
      .collect(toList());

    slas.deleteAllIds(ids);
  }
}

The class above is an event-bus listener. It listens to events being posted on an EventBus and subscribe to one of those (DeleteProjectEvent).

You can use @Component for pretty much anything that needs to be instantiated and autowired by Spring. For more specialized beans, the next coming annotations are best suited.

Spring Repository

The @Repository annotation is used to mark a DAO (Data Access Object) bean. Repositories are popular DAOs.

Here is an example using Spring Data JPA:

@Repository
public interface PersonRepository extends CrudRepository<Person, String> {

}

In this case, the Repository instance is generated by Spring. Spring uses Reflection as well as Java Proxy to generate the bytecode of an implementation on runtime. You can also create your own implementation (see Spring Data for more information).

For this reason, repositories are a special kind of beans.

Spring Service

The @Service annotation is a specialization of the @Component annotation. In an MVC Pattern, there are three components: (a simplified explanation)

  • Model: represents the services to create and manipulate data,
  • View: represents the view layer, which is the UI representation of the data,
  • Controller: the controller acts as a middle-ware between the Model and the View, orchestrating everything in-between.

The @Service annotated beans are part of the Model within the MVC architecture.

Here is an example of service:

@Service
@AllArgsConstructor(access = PACKAGE)
@FieldDefaults(level = PRIVATE, makeFinal = true)
final class ApmCrudDaoService extends AbstractCrudDaoService<Apm> implements ApmCrudService {
  @NonNull
  @Getter
  DatabaseRepository<Apm> repository;

  @Override
  public Class<Apm> clazz() {
    return Apm.class;
  }
}

The service here provides an abstraction layer between the business code and the lower-level repository code. It abstracts the queries to perform on the database by hiding them behind simple methods.

Services should be long-living instances, usually living as long as the application, and mostly be stateless. A service is expected to behave exactly the same way no matter what the previous calls were.

Final Words

We're reviewed a few of the annotations involved when using Spring Framework. Most of them are pretty simple to understand! We hope this little article gave you a more insight about which annotations are available, and how to use them.

Keep in mind the KISS principle: keep things simple, don't overuse advanced features. Most of the time @Component and @Service annotated beans are fine. Most spring configuration can be done with Java using @Configuration annotated classes and Bean methods.

Want to become a super load tester?
Request a Demo