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
tagUse 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.
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.
ReplyDeleteAlso, 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.
Hi Reza, thx again for commenting.
ReplyDeleteThe 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
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 :-).
ReplyDeleteIf you come up with more useful patterns, you could host that on CDISource too - perhaps naming it something like the CDI Patterns module...
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.
ReplyDeleteAlso, 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
Hi Gordon! That was a german comment, fixed that. Also added the servlet API dep.
ReplyDeleteThx for your comment!
Cheers,
Niklas