Introduction
In this tutorial, we will learn about the @Order
annotation in Spring. The @Order
annotation in Spring defines the sorting order of beans or components. Before Spring 4.0, this annotation supported only the ordering of AspectJ aspects. Since Spring 4.0, it supports many kinds of components e.g. even for collection injection.
1. The @Order annotation in Spring
Right from the Spring source code:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
/**
* The order value.
* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
* @see Ordered#getOrder()
*/
int value() default Ordered.LOWEST_PRECEDENCE;
}
@Order
annotation has one optional argument of type Integer
. Default value is Ordered.LOWEST_PRECEDENCE
. Lower the value, the higher the precedence.
Remember that Ordered.LOWEST_PRECEDENCE
is a constant equal to Integer.MAX_VALUE
. Hence, it can also have negative values up to Integer.MIN_VALUE
. But in general, avoid using negatives.
2. Use of @Order annotation
Many times we face situations where we require the beans or dependencies to be injected in a particular order. Some of the common use-cases are:-
- Ordering the AspectJ advice
- For injecting the collection of ordered beans, components and/or services
- Applying the filters in an ordered way in Spring Security
- Sequencing the execution
CommandLineRunner
orApplicationRunner
in Spring Boot
2.1. Example of using @Order
in @Service
injection
For this, I’m taking a very simple and intuitive example. Here, we have a NotificationHandler
interface. This interface has one method send()
. We also have four different implementations of this interface. Basically, each implementation represents a different channel or medium to send notifications.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
/*
* A Simple interface having just one method send()
* Our Service classes will implement this with their specific behavior.
*/
public interface NotificationHandler {
void send();
}
@Service
@Order(1)
class SmsNotification implements NotificationHandler {
@Override
public void send() {
System.out.println("SMS Notification Handler");
}
}
@Service
@Order(3)
class EmailNotification implements NotificationHandler {
@Override
public void send() {
System.out.println("Email Notification Handler");
}
}
@Service
@Order(2)
class SlackNotification implements NotificationHandler {
@Override
public void send() {
System.out.println("Slack Notification Handler");
}
}
@Service
// Not an Ordered Service, will be the last in the sequence
class TwitterNotification implements NotificationHandler {
@Override
public void send() {
System.out.println("Twitter Direct Message Notification Handler");
}
}
So, let’s now try sending a notification to all possible channels i.e. through all available implementations of NotificationHandler
. To do so, we will need List<NotificationHandler>
to be injected into our test case. After service injection, we will call thesend()
method of every instance and observe their execution order in logs.
In our ordering, we have given the highest priority to SmsNotification
by giving @Order(1)
as compared to others. So, that should be the first in the output. Also, we have given no precedence to TwitterNotification
so this should come in the last always.
@SpringJUnitConfig(ApplicationConfig.class)
public class TestOrderOnCollectionInjection {
@Autowired
private List<NotificationHandler> notificationHandlers;
@Test
public void testAllChannelSendingOrder() {
notificationHandlers.forEach(NotificationHandler::send);
}
}
SMS Notification Handler Slack Notification Handler Email Notification Handler Twitter Direct Message Notification Handler
And there you go. It printed in the exact order we mentioned.
2.2. Example of using @Order
in @Bean
injection
So now will see an example of bean injection ordering using @Order
annotation. Like the above example, here we are trying to get the names of all available notification channels/mediums.
@Bean
@Order(2)
public NotificationChannel email(){
return new NotificationChannel("Email");
}
@Bean
@Order(4)
public NotificationChannel twitter(){
return new NotificationChannel("Twitter");
}
@Bean
@Order(3)
public NotificationChannel slack(){
return new NotificationChannel("Slack");
}
@Bean
@Order(1)
public NotificationChannel sms(){
return new NotificationChannel("Sms");
}
public class NotificationChannel {
private String name;
public NotificationChannel(String channelName) {
this.name = channelName;
}
public String getName() {
return name;
}
}
Alright, we are done with the coding. Let’s figure out the output now.
@SpringJUnitConfig(ApplicationConfig.class)
public class TestOrderOnBeanInjection {
@Autowired
private List<NotificationChannel> notificationChannels;
@Test
public void testNotificationChannelBeanOrder(){
notificationChannels.forEach(channel -> System.out.println(channel.getName()));
}
}
Sms Email Slack Twitter
So we can see that @Order
worked correctly on beans too.
3. Ordering works at the time of injection only
Since you have annotated them with @Order
you believe that their instantiation will also follow the same order, right? However, that’s not the case. Though the concept of @Order
looks very simple, sometimes people get confused about its behavior.
The ordering works only at the time of injecting the beans/services, not at the time of their creation.
Simply put, when the bean instances are getting created, the order is not what you had mentioned. In reality, it is based on their class name. By no surprise that the spring uses Reflections internally. So, Spring starts to scan the classes and instantiates them in alphabetical order of their class name. Hence, the ordering is not taken into consideration at this moment. It is applied only at the time of their dependency injection.
Since, we already have our above @Order
code working correctly, we’ll change in that only. So, here I’m adding a constructor to all the four classes which implemented NotificationHandler
. By running the same test case, let’s observe their logs.
@Service
@Order(1)
class SmsNotification implements NotificationHandler {
public SmsNotification() {
System.out.println("SmsNotification Service created.");
}
@Override
public void send() {
System.out.println("SMS Notification Handler");
}
}
@Service
@Order(3)
class EmailNotification implements NotificationHandler {
public EmailNotification() {
System.out.println("EmailNotification Service Created.");
}
@Override
public void send() {
System.out.println("Email Notification Handler");
}
}
@Service
@Order(2)
class SlackNotification implements NotificationHandler {
public SlackNotification() {
System.out.println("SlackNotification Service created.");
}
@Override
public void send() {
System.out.println("Slack Notification Handler");
}
}
@Service
// Not an Ordered Service, will be the last in the sequence
class TwitterNotification implements NotificationHandler {
public TwitterNotification() {
System.out.println("TwitterNotification Service created.");
}
@Override
public void send() {
System.out.println("Twitter Direct Message Notification Handler");
}
}
EmailNotification Service Created. SlackNotification Service created. SmsNotification Service created. TwitterNotification Service created. SMS Notification Handler Slack Notification Handler Email Notification Handler Twitter Direct Message Notification Handler
So, clearly we can see that the order of creation is different that order of injection.
Conclusion
We learned to use @Order
annotation in Spring with examples. We also explored the common confusion related to the ordering. If you liked reading this, please share it with others.