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.
How do we control the order of printers within the list using spring component scan?
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.
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.