Over a million developers have joined DZone.

JSR-299 CDI Decorators for Spring beans

· Java Zone

Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code! Brought to you in partnership with ZeroTurnaround.

 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: download here
Sources: download here
API-Doc: view here

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

Dependencies

<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-context</artifactid>
<version>${spring.version}</version>
</dependency>
 
<dependency>
<groupid>org.springframework</groupid>
<artifactid>org.springframework.aop</artifactid>
<version>${spring.version}</version>
</dependency>
 
<dependency>
<groupid>javax.enterprise</groupid>
<artifactid>cdi-api</artifactid>
<version>1.0</version>
</dependency>
 
<dependency>
<groupid>cglib</groupid>
<artifactid>cglib</artifactid>
<version>2.2.2</version>
</dependency>

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
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName">

<context:component-scan base-package="com.mycompany.springapp.springcdi" scoped-proxy="targetClass">
<context:include-filter type="annotation" expression="javax.decorator.Decorator" />
</context:component-scan>

<bean class="com.schlimm.springcdi.decorator.DecoratorAwareBeanFactoryPostProcessor" />

</beans>

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

package com.mycompany.springapp.springcdi;
 
public interface MyService {
 
String sayHello();
 
}

This is your implementation of the service.

	
package com.mycompany.springapp.springcdi;
 
import org.springframework.stereotype.Component;
 
@Component
public class MyServiceImpl implements MyService {
 
public String sayHello() {
return "Hello";
}
 
}

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.

package com.mycompany.springapp.springcdi;
 
import javax.decorator.Decorator;
import javax.decorator.Delegate;
 
@Decorator
public class MyServiceSecurityDecorator implements MyService {
 
@Delegate
private MyService delegate;
 
public String sayHello() {
// do some security stuff
return delegate.sayHello();
}
 
}

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

package com.mycompany.springapp.springcdi;
 
import javax.decorator.Decorator;
import javax.decorator.Delegate;
 
@Decorator
public class MyServiceTransactionDecorator implements MyService{
 
@Delegate
private MyService delegate;
 
public String sayHello() {
// do some transaction stuff
return delegate.sayHello();
}
 
}

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.

package com.mycompany.springapp.springcdi;
 
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
@ContextConfiguration("/test-decorator-context.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class DecoratorTestCase {
 
// This injected bean will be a decorated MyServiceImpl
@Autowired
private MyService service;
 
@Test
public void testHelloWorld() {
 
Assert.assertTrue(service.sayHello().equals("Hello"));
 
}
}

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.

From http://niklasschlimm.blogspot.com/2011/08/jsr-299-cdi-decorators-for-spring-beans.html

The Java Zone is brought to you in partnership with ZeroTurnaround. Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code!

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}