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:

Java
@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:-

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.

XML
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
    </dependency>
Java
/*
* A Simple interface having just one method send()
* Our Service classes will implement this with their specific behavior.
*/
public interface NotificationHandler {
    void send();
}
Java
@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.

Java
@SpringJUnitConfig(ApplicationConfig.class)
public class TestOrderOnCollectionInjection {

  @Autowired
  private List<NotificationHandler> notificationHandlers;

  @Test
  public void testAllChannelSendingOrder() {
    notificationHandlers.forEach(NotificationHandler::send);
  }
}
Output:

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.

Java
    @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");
    }
Java
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.

Java
@SpringJUnitConfig(ApplicationConfig.class)
public class TestOrderOnBeanInjection {

  @Autowired
  private List<NotificationChannel> notificationChannels;

  @Test
  public void testNotificationChannelBeanOrder(){
    notificationChannels.forEach(channel -> System.out.println(channel.getName()));
  }
}
Output:

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.

Java
@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");
    }
}
Output:

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.

By |Last Updated: April 2nd, 2024|Categories: Spring Framework|