Monday, August 1, 2011

JSR-299 CDI Decorators for Spring beans

This blog is about my new Spring-CDI modules effort. It's pupose is to make useful CDI patterns like decorators or interceptors available to a Spring application. I do believe that the explicit pattern implementation in CDI is very useful. It makes it obvious and simple to use patterns for not so experienced developers. Therefore I decided to investigate how to make those patterns and the corresponding CDI annotations available for Spring managed beans. Here is the current status of my work. If you're interested and you have some time left, take a look or try out my early version of the Spring-CDI decorator module. The set-up is straight forward. You'll find all you need below.


Please notice: The intention of my blog is to share and discuss ideas. If you use any of this in your applications you're acting at your own risk.

JSR-299 decorator pattern implementation

Features

The decorator module provides the following features:

- Use JSR-299 @Decorator and @Delegate in Spring managed beans
- Support chains of multiple decorators for the same target delegate bean
- Allow to qualify decorators to decorate multiple implementations of the same interface with different decorators
- Support scoped beans, allow scoped decorators
- Integrate with Spring AOP, both dynamic JDK proxies and CGLIB proxies
- Allow definition of custom decorator and delegate annotations

Download Link

The Spring-CDI decorator module is an usual Spring IoC-container extension delivered as JAR archive. You can download the module JAR and put that on the classpath of your Spring application.

Compiled Spring-CDI decorator module JAR: Version v0.9.10
Sources: Version v0.9.10
API-Doc: Version v0.9.10

Everything is hosted on a git repository on Github.com.

Dependencies



Configuration

If the Spring-CDI decorator module JAR and its dependencies are on your classpath, all you need to do is:

(1) register DecoratorAwareBeanFactoryPostProcessor in your application context
(2) define an include-filter to include javax.decorator.Decorator as component annotation in your context:component-scan tag



Use Case

The following code snippets show how you can use the decorator pattern ones you have configured your Spring application as described above. For more complex scenarios see my unit test cases.

Let's assume you have a business interface called: MyService



This is your implementation of the service.



You want to do some transaction and security stuff, but you do not want to mess up the business code with it.

For security you'd write a decorator that points to the MyService business service.



To seperate the cross-cutting-concerns you write another decorator for transaction handling that points to the MyService business service.



Then you can just use standard Spring @Autowired annotation to make that work. The injected bean will be decorated with your new security and transaction decorator.



How it works

The core is the DecoratorAwareBeanFactoryPostProcessor that scans the registered bean definitions for existing decorators. It gathers meta data and stores that data in the DecoratorMetaDataBean. The DecoratorAwareBeanPostProcessor uses the meta data to wire the decorators into a chain and creates a CGLIB proxy that intercepts method calls to the target delegate bean. It redirects those calls to the decorator chain. The DecoratorAutowireCandidateResolver applies autowiring rules specific to the CDI decorator pattern. It also uses meta data to do that.


The two modes

The DecoratorAwareBeanFactoryPostProcessor accepts two runtime modes. The 'processor' (default) mode uses DecoratorAwareBeanPostProcessor and the DecoratorChainingStrategy to wire the decorator chain. The 'resolver' mode uses DecoratorAwareAutowireCandidateResolver to implement custom wiring logic based on complex wiring rules implemented in ResolverCDIAutowiringRules. The 'resolver' mode was just another option how one can implement such complex logic. I tried two different options and both work. The 'processor' alternative however implements simpler logic. Therefore it's my prefered mode at the moment.

Decorator Meta Data Model

The DecoratorAwareBeanFactoryPostProcessor scans bean definitions and stores meta data about the decorators and delegates in the application context. These are the model beans in their hierarchical access order:

DecoratorMetaDataBean.java: Top level entry point to the meta-data. Registered and available in the application context.
QualifiedDecoratorChain.java: A chain of decorators for the same target delegate bean.
DecoratorInfo.java: A decorator bean definition wrapper class.
DelegateField.java: Contains the delegate field of the decorator implementation.

Strategies

The Spring-CDI decorator module is easy to adopt by users through the use of strategy pattern in many places. These are the strategies that allow users to change processing logic if required:

DecoratorChainingStrategy.java: Wires the decorators for a specific target delegate bean.
DecoratorOrderingStrategy.java: Orders the decorators for a specific target delegate bean.
DecoratorResolutionStrategy.java: Scans the bean factory for available decorator beans.
DelegateResolutionStrategy.java: Searches the delegate bean for a specific decorator bean.

Decorator Autowiring Rules

The 'processor' mode and the 'resolver' mode both use a custom AutowireCandidateResolver applied to the current bean factory. The class is called DecoratorAwareAutowireCandidateResolver and it is applied to the bean factory in the DecoratorAwareBeanFactoryPostProcessor. The custom resolver works with different rule sets. In the 'processor' mode it works with a very simple rule set called BeanPostProcessorCDIAutowiringRules. In the 'resolver' mode it uses ResolverCDIAutowiringRules which is far more complex. If these rule sets are not sufficient for your autowiring logic, it's easy to apply additional rule sets by implementing a custom SpringCDIPlugin and adding it to the DecoratorAwareAutowireCandidateResolver.

Spring-CDI Plugin System

The Spring-CDI decorator module contains two infrastructure interfaces that allow the modularized approach of Spring-CDI project: SpringCDIPlugin and SpringCDIInfrastructure. When I implement additional modules - like the interceptor module - users can decide which modules to use and import into their projects. It's not required to add all Spring-CDI functionality if one only needs decorators.

5 comments:

  1. I think this project would be good for CDISource if you don't want to maintain it too much going forward. They already have a Spring/CDI bridge to interoperate between CDI and Spring.

    Also, can you think of any other design patterns that could be implemented in CDI? I am not sure something like that belongs in CDI proper, but it would be a good portable extension project.

    ReplyDelete
  2. Hi Reza, thx again for commenting.

    The Spring/CDI bridge is a different approach I think. It's connecting the two containers to each other. My solution is meant to enable Spring applications to use CDI annotations directly in Spring beans. I don't operate with a CDI container. You think it's still a good project for CDISource?

    I have to think about your patterns question and respond to that later. I am sure there are other patterns that make sence. And yes - just for the sake of diving into CDI a bit more - it's a good idea to come up with a portable extension for an additional pattern, good point ;-)

    Cheers,
    Niklas

    ReplyDelete
  3. Yes, it would still be a good addition to CDISource I think since it is CDI related. The best place would of course be the Spring framework itself :-).

    If you come up with more useful patterns, you could host that on CDISource too - perhaps naming it something like the CDI Patterns module...

    ReplyDelete
  4. I got an error in the source, non-parseable line #153 in DecoratorAwareBeanFactoryPostProcessor for UTF-8. It is the symbol between the "f" and "r" in the comment.

    Also, you need the servlet api in the pom.xml file:
    javax.servlet:servlet-api:2.5:provided

    Regards,
    Gordon Dickens

    twitter.com/gdickens
    linkedin.com/in/gordondickens
    Blog: technophile.gordondickens.com

    ReplyDelete
  5. Hi Gordon! That was a german comment, fixed that. Also added the servlet API dep.
    Thx for your comment!
    Cheers,
    Niklas

    ReplyDelete