Spring – Composite interceptor to create dynamic interceptor/advice chain

The Spring Framework greatly supports the AOP programming model with various advice types like before, after and surround. AOP isn’t limited to use within Spring. It is a useful concept to implement cross cutting concerns. The Spring Framework also use implementations at the org.aopalliance.intercept.MethodInterceptor which specifies interception of calls on an interface on its way to the target. An intercepter implements one aspect like logging, transaction handling or retry behavior. To use interceptors, you have to configure the interceptor class in the Spring AOP configuration and define a pointcut expression.

In the Spring aop:config a pointcut is defined by an advice reference (i.e. an interceptor), a pointcut expression and the order of execution at a joinpoint. At application startup time the advices are wired to the invocation handler where the pointcut expression matches. After that the builded advice / interceptor chain is not easily changeable.

In my projects I reuse approved intercepter implementations. In in some cases I combine several interceptors to one interceptor or dynamically chains interceptors and use it with one pointcut expression. In view of the method invocation, the interceptors appears as one interceptor, or in other word as composition of interceptors. To support chaining of interceptors I implemented a CompositeInterceptor and a Interceptors utility class.

public class CompositeInterceptor
    implements MethodInterceptor, InitializingBean {

    private MethodInterceptor[] interceptors = new MethodInterceptor[0];
    private MethodInterceptor interceptorChain = Interceptors.EMPTY;

    @Override
    public Object invoke(MethodInvocation invocation)
        throws Throwable {
        return interceptorChain.invoke(invocation);
    }

    public void setInterceptors(MethodInterceptor[] interceptors) {
        this.interceptors = interceptors;
    }

    @Override
    public void afterPropertiesSet()
        throws Exception {
        interceptorChain = Interceptors.create(interceptors);
    }
}

This solution is decoupled from a proxy creation, you can configure a pointcut expression for the composite interceptor. In conjunction with proxy ceration Spring offers a similar to configure a interceptor chain (see
org.springframework.aop.framework.ProxyFactoryBean or org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator)

At the bottom of post you will find the utility class Interceptors to support easy interceptor chaining.

The Spring aop configuration bellow shows an example usage for chaining a timing and debug interceptor. The interceptor instances are defined as inner beans and injected as a list value.

...
	<!-- AOP config -->
	<aop:aspectj-autoproxy />

	<!-- declarative chained interceptor -->
	<bean id="debugInterceptorWithComposite" class="ch.nydi.aop.interceptor.CompositeInterceptor" >
		<property name="interceptors">
			<list>
				<bean class="ch.nydi.aop.interceptor.TraceInterceptor"/>
				<bean class="ch.nydi.aop.interceptor.TimerInterceptor"/>
			</list>
		</property>
	</bean>

	<aop:config>
		<aop:advisor
			advice-ref="debugInterceptor"
			pointcut="execution(public * ch.nydi.service.test.*.*(..))"
			order="1" />
	</aop:config>
...

OK, but where is the ‘dynamic’? The CompositeInterceptor is instanciated at application startup time, like all singleton spring beans. To make a dynamic interceptor chain we has to construct the interceptor chain at runtime. The Interceptors Utility allows to build the chain at runtime. The DebugInterceptor bellow builds a interceptor chain with two instance scoped interceptors (they have to be thread save) and a interceptor implemented as anonymous inner class.

public class DebugInterceptor
    implements MethodInterceptor {

    private final Logger logger = LoggerFactory.getLogger(DebugInterceptor.class);

    private final TimerInterceptor timerInterceptor = new TimerInterceptor();
    private final TraceInterceptor traceInterceptor = new TraceInterceptor();

    @Override
    public Object invoke(MethodInvocation invocation)
        throws Throwable {

        if (logger.isTraceEnabled()) {
            return Interceptors.create(traceInterceptor, timerInterceptor, new MethodInterceptor() {

                @Override
                public Object invoke(MethodInvocation invocation)
                    throws Throwable {
                    logger.info("Greetings from a anonymous interceptor");
                    return invocation.proceed();
                }
            }).invoke(invocation);
        }

        return invocation.proceed();
    }
}

And the Spring aop configuration

...
	<!-- AOP config -->
	<aop:aspectj-autoproxy />

	<!-- dynamic chained interceptor -->
	<bean id="debugInterceptor" class="ch.nydi.aop.interceptor.DebugInterceptor" />

	<aop:config>
 		<aop:advisor
 			advice-ref="debugInterceptorWithComposite"
 			pointcut="execution(public * ch.nydi.service.test.*.*(..))"
 			order="1" />
	</aop:config>
...

Well, this is not the breath taking use case for creating dynamic interceptor chains. I use this concept to propagate a ThreadLocal based mail session implementation to mail session consuming Spring beans. The mail session is valid during the intercepted methode invocation.  At this place I reuse a well tested ThreadLocalInterceptor implementation which allows stable handling of ThreadLocale value during a method invocation.

Handling ThreadLocale’s within interceptors and method callbacks goes beyond scope of this post, I will highlight that in an future post.

Visit spring-practice project at my github account to download the code examples (Also TimerInterceptor and TraceInterceptor)

Utility class Interceptors

public final class Interceptors
    implements MethodInterceptor {

    public static final MethodInterceptor EMPTY = new MethodInterceptor() {

        @Override
        public Object invoke(final MethodInvocation invocation)
            throws Throwable {
            return invocation.proceed();
        }
    };

    private final MethodInterceptor[] interceptors;

    /**
     * Creates an interceptor that chains other interceptors.
     *
     * @param interceptors
     *            instances of {@link MethodInterceptor}.
     * @return interceptor that enclose other interceptors or Interceptors.EMPTY instance if interceptors argument is
     *         null or empty
     */
    public static MethodInterceptor create(final MethodInterceptor... interceptors) {

        if (ArrayUtils.isEmpty(interceptors)) {
            return Interceptors.EMPTY;
        }

        final List<MethodInterceptor> flatlist = new ArrayList<MethodInterceptor>();
        for (final MethodInterceptor interceptor : interceptors) {
            assert (interceptor != null);

            if (interceptor instanceof Interceptors) {
                flatlist.addAll(Arrays.asList(((Interceptors) interceptor).interceptors));
            } else if (EMPTY != interceptor) {
                flatlist.add(interceptor);
            }
        }
        if (flatlist.isEmpty()) {
            return EMPTY;
        } else if (flatlist.size() == 1) {
            return flatlist.get(0);
        }
        return new Interceptors(flatlist.toArray(new MethodInterceptor[flatlist.size()]));
    }

    private Interceptors(final MethodInterceptor[] interceptors) {
        this.interceptors = interceptors;
    }

    @Override
    public Object invoke(final MethodInvocation invocation)
        throws Throwable {
        MethodInvocation nextInvocation = invocation;

        // chain the method invocation of interceptors in reverse order
        for (int i = interceptors.length - 1; i >= 0; i--) {
            final MethodInterceptor interceptor = interceptors[i];
            final MethodInvocation previousInvocation = nextInvocation;
            nextInvocation = new MethodInvocation() {

                @Override
                public Object proceed()
                    throws Throwable {
                    return interceptor.invoke(previousInvocation);
                }

                @Override
                public Object getThis() {
                    return invocation.getThis();
                }

                @Override
                public AccessibleObject getStaticPart() {
                    return invocation.getStaticPart();
                }

                @Override
                public Object[] getArguments() {
                    return invocation.getArguments();
                }

                @Override
                public Method getMethod() {
                    return invocation.getMethod();
                }
            };
        }
        return nextInvocation.proceed();
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *