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!
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.