Code for this blog can be found on github at
https://github.com/galapagosfinch

Thursday, March 15, 2012

TIP: Collections and "Autowire by Type"

I recently found a coworker who hadn't seen the bulk injection feature in Spring.  We had a need to dispatch to one of many beans in our code based on some logic.  I pointed out that with a little bit of refactoring, the selection logic can live along side the transformer that needs to be called, and the service won't need to change as we add more transformers.  Here's how.



The transformer instances all extend a common interface/base class:

public interface DomainTransformer<T extends CommonDomain> {

   String getSupportedMessageType();
   Class<T> getDomainClass();
   boolean isSupported(String messageType);
}

public abstract class AbstractDomainTransformer<T extends CommonDomain> implements DomainTransformer<T> {
   public boolean isSupported(String messageType) {
      return getSupportedMessageType().equalsIgnoreCase(messageType);
   }
}

@Service
public class FloodTransformer extends AbstractDomainTransformer<Flood> {
   @Override
   public String getSupportedMessageType() {
      return "flood";
   }

   @Override
   public Class getDomainClass() {
      return Flood.class;
   }
}

The service can have injected all the instances of DomainTransformer that are registered with the ApplicationContext.  This is the "secret sauce" that makes it easy:

@Service("domainTransformationService")
@Transactional
public class DomainTransformationServiceImpl implements DomainTransformationService {

 @Autowired
 private List<DomainTransformer> transformerList = Collections.emptyList();

And then, we delegate decision making to the autowired instances:

private DomainTransformer getTransformerForMessageType(String messageType) throws NoSuchFieldException {
 for (DomainTransformer transformer : transformerList) {
  if (transformer.isSupported(messageType)) {
   return transformer;
  }
 }
 // TODO throw better exception
 throw new NoSuchFieldException("unknown message type: " + messageType);
}

Simple enough! The service code will not need to be modified if a new type of transformer is added to the system. New transformers merely needs to adhere to the DomainTransformer interface and be published to the same ApplicationContext.  Once that's done, it will be injected into the List<> along with the other DomainTransformer instances.

This has a lot of uses, especially around your framework where you want to allow the business layer to add features without the framework having to change.  Bulk injection is a great way to keep classes from having to know every class they will be dispatching to.

No comments:

Post a Comment