Chain of responsibility using Spring @Autowired List

There is a way in Spring 3.1 to auto populate a typed List which is very handy when you want to push a bit the decoupling and the cleaning in your code.

To show you how it works, I will implement a simple chain of responsibility that will take care of printing some greetings for a passed User.

Let start from the (only) domain class we have, the User:
 
 
 
 
 

package com.marco.springchain;
public class User {

        private final String name;
        private final char gender;

        public User(String name, char gender) {
                super();
                this.name = name;
                this.gender = gender;
        }

        public String getName() {
                return name;
        }

        public char getGender() {
                return gender;
        }
}

Then we create an interface that defines the type for our command objects to be used in our chain:

package com.marco.springchain;
public interface Printer {

        void print(User user);
}

This is the generic class (the template) for a Printer implementation.

The org.springframework.core.Ordered is used to tell the AnnotationAwareOrderComparator how we want our List to be ordered.

You don’t need to implement the Ordered interface and to override the getOrder method if you don’t need your chain to have an execution order.

Also notice that this abstract class return Ordered.LOWEST_PRECEDENCE, this because I want some Printer commands to just run at the end of the chain and I don’t care about their execution order (everything will be clearer after, I promise!).

package com.marco.springchain;
import org.springframework.core.Ordered;
public abstract class GenericPrinter implements Printer, Ordered {

        public void print(User user) {
                String prefix = 'Mr';
                if (user.getGender() == 'F') {
                        prefix = 'Mrs';
                }
                System.out.println(getGreeting() + ' ' + prefix + ' ' + user.getName());
        }

        protected abstract String getGreeting();

        public int getOrder() {
                return Ordered.LOWEST_PRECEDENCE;
        }
}

This is our first real Printer command. I want this to have absolute precedence in the chain, hence the order is HIGHEST_PRECEDENCE.

package com.marco.springchain;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
public class HelloPrinter extends GenericPrinter {

        private static final String GREETING = 'Hello';

        @Override
        protected String getGreeting() {
                return GREETING;
        }

        @Override
        public int getOrder() {
                return Ordered.HIGHEST_PRECEDENCE;
        }
}

WelcomePrinter to be executed as first command (After High precedence ones ).

package com.marco.springchain;
import org.springframework.stereotype.Component;
@Component
public class WelcomePrinter extends GenericPrinter {

        private static final String GREETING = 'Welcome to the autowired chain';

        @Override
        protected String getGreeting() {
                return GREETING;
        }

        @Override
        public int getOrder() {
                return 1;
        }
}

GoodbyePrinter to be executed as second command

package com.marco.springchain;
import org.springframework.stereotype.Component;
@Component
public class GoodbyePrinter extends GenericPrinter {

        private static final String GREETING = 'Goodbye';

        @Override
        protected String getGreeting() {
                return GREETING;
        }

        @Override
        public int getOrder() {
                return 2;
        }
}

These 2 commands need to be executed after the others, but I don’t care about their specific order, so I will not override the getOrder method, leaving the GenericPrinter to return Ordered.LOWEST_PRECEDENCE for both.

package com.marco.springchain;
import org.springframework.stereotype.Component;
@Component
public class CleaningMemoryPrinter extends GenericPrinter {

        private static final String GREETING = 'Cleaning memory after';

        @Override
        protected String getGreeting() {
                return GREETING;
        }
}
package com.marco.springchain;
import org.springframework.stereotype.Component;
@Component
public class CleaningSpacePrinter extends GenericPrinter {

        private static final String GREETING = 'Cleaning space after';

        @Override
        protected String getGreeting() {
                return GREETING;
        }
}

This is the chain context.

Spring will scan (see the spring-config.xml) the package specified in the config file, it will see the typed (List<Printer>) list, and it will populate the list with an instance of any @Component that implements the type Printer.

To order the List we use AnnotationAwareOrderComparator.INSTANCE that use the getOrder method to re-order the List ( the object with the lowest value has highest priority (somewhat analogous to Servlet ‘load-on-startup’ values)).

package com.marco.springchain;
import java.util.Collections;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.stereotype.Component;
@Component
public class PrinterChain {

        @Autowired
        private List<Printer> printers;

        @PostConstruct
        public void init() {
                Collections.sort(printers, AnnotationAwareOrderComparator.INSTANCE);
        }

        public void introduceUser(User user) {
                for (Printer printer : printers) {
                        printer.print(user);
                }
        }
}

The spring-config.xml in the src/main/resources.

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns='http://www.springframework.org/schema/beans'
       xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
       xmlns:aop='http://www.springframework.org/schema/aop'
       xmlns:tx='http://www.springframework.org/schema/tx'
       xmlns:context='http://www.springframework.org/schema/context'
       xmlns:util='http://www.springframework.org/schema/util'
       xsi:schemaLocation='
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd' default-lazy-init='true'>

    <context:component-scan base-package='com.marco.springchain'/>
</beans>

Finally, a main class to test our chain.

package com.marco.springchain;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest {

        public static void main(String[] args) {
                ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext('spring-config.xml');
                PrinterChain printerChain = (PrinterChain) context.getBean('printerChain');
                printerChain.introduceUser(new User('Marco Castigliego', 'M'));
                printerChain.introduceUser(new User('Julie Marot', 'F'));
        }
}

OUTPUT:

Hello Mr Marco Castigliego
Welcome to the autowired chain Mr Marco Castigliego
Goodbye Mr Marco Castigliego
Cleaning space after Mr Marco Castigliego
Cleaning memory after Mr Marco Castigliego
Hello Mrs Julie Marot
Welcome to the autowired chain Mrs Julie Marot
Goodbye Mrs Julie Marot
Cleaning space after Mrs Julie Marot
Cleaning memory after Mrs Julie Marot

Hope you enjoyed the example.
 

Reference: Chain of responsibility using Spring @Autowired List from our JCG partner Marco Castigliego at the Remove duplication and fix bad names blog.

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

3 Responses to "Chain of responsibility using Spring @Autowired List"

  1. Vikram Hullukunte says:

    How do we control the order of printers within the list using spring component scan?

    • marco says:

      The Printers implement the Ordered interface and so they need to implement the method “public int getOrder()”

      Then, in a method marked as @PostConstruct, you use the Collections java util class to reorder the List based on the spring AnnotationAwareOrderComparator.

  2. Marco Castigliego says:

    From the java example you see in wikipedia link you provided :”In a ‘pure’ implementation of the chain of responsibility pattern, a logger would not pass responsibility further down the chain after handling a message. In this example, a message will be passed down the chain whether it is handled or not.”

    Exactly like in my example.

Leave a Reply


six − = 5



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books