Difference between interceptors and decorators
In general, a decorator is used to add new functionality or modify existing functionality. It uses composition as an alternative to inheritance. Decorators often provide additional APIs (methods) that are not available in the decorated classes.
On the other hand, AOP (e.g. an interceptor) is used to enhance existing behavior. It does not add additional APIs and generally does not modify existing functionality. It is triggered by invocation of existing functionality and responds by taking some action; but the existing functionality as well as the existing API remain unchanged.
I am not familiar with the JEE implementations, so they may have blurred the lines between these two patterns. Important points to compare would be,
- Can
@Interceptor
introduce new methods or only execute around existing methods? - Can
@Interceptor
override existing methods or only append additional behavior? - Can
@Decorator
be applied across packages & class hierarchies, or is it constrained by one of these?
In addition to functional differences between the two patterns, it may also be interesting to consider potential performance differences. I would expect @Interceptor
to be considerably slower, since it needs to examine method calls at runtime, whereas @Decorator
invocations can be resolved at compile time.
Decorator
One difference would be, as your example shows it, with decorator you usually write 1 decorator per 1 decorated class/interface.
Decorator example
interface Worker {
void work();
}
class Decorated implements Worker {
public void work() {
}
}
class DecoratorByInheritance extends Decorated {
public void work() {
// pre
super.work();
// post
}
}
class DecoratorByComposition implements Worker {
Worker decorated;
DecoratorByComposition(Worker decorated) {
this.decorated = decorated;
}
public void work() {
// pre
this.decorated.work();
// post
}
}
Interceptor
With interceptors, which are part of the AOP concept, you write 1 interceptor for a bunch of classes / methods, e.g. you intercept all DAO methods and make sure a transaction is open before the invocation and closed after it.
Interceptor example
Declare a pointcut (what to match), here you match any method from the MyDao class that starts with insert, has any arguments and any return type.
@Pointcut("execution(* com.example.dao.MyDao.insert*(..))")
public void insertPointcut() {
}
Then you declare an around advice which references the pointcut
@Around(value = "com.example.SystemArchitecture.insertPointcut()")
public void interceptMethod(ProceedingJoinPoint pjp) {
// do pre-work
Object retVal = pjp.proceed();
// do post work
return retVal;
}
}
Interceptors are more flexible but imagine you change the method name, if you use a decorator, you'll probably get a compiler error, with interceptors, it will just not match and not execute your 'around' logic.
Decorators very similar to interceptors with two interesting differences:
A decorator must implement the interface it is decorating (and yet can be abstract, so it does not have to implement the methods)
A decorator can have a reference to the object it decorates. It is done through injection
Ref: https://blog.frankel.ch/cdi-an-overview-part-2