SeamFramework.orgCommunity Documentation
Interceptors are a powerful way to capture and separate concerns which are orthogonal to the type system. Any interceptor is able to intercept invocations of any Java type. This makes them perfect for solving technical concerns such as transaction management and security. However, by nature, interceptors are unaware of the actual semantics of the events they intercept. Thus, interceptors aren't an appropriate tool for separating business-related concerns.
The reverse is true of decorators. A decorator intercepts invocations only for a certain Java interface, and is therefore aware of all the semantics attached to that interface. This makes decorators a perfect tool for modeling some kinds of business concerns. It also means that a decorator doesn't have the generality of an interceptor. Decorators aren't able to solve technical concerns that cut across many disparate types.
Suppose we have an interface that represents accounts:
public interface Account {
public BigDecimal getBalance();
public User getOwner();
public void withdraw(BigDecimal amount);
public void deposit(BigDecimal amount);
}
Several different Web Beans in our system implement the
Account
interface. However, we have a common legal
requirement that, for any kind of account, large transactions must be
recorded by the system in a special log. This is a perfect job for a
decorator.
A decorator is a simple Web Bean that implements the type it
decorates and is annotated @Decorator
.
@Decorator
public abstract class LargeTransactionDecorator
implements Account {
@Decorates Account account;
@PersistenceContext EntityManager em;
public void withdraw(BigDecimal amount) {
account.withdraw(amount);
if ( amount.compareTo(LARGE_AMOUNT)>0 ) {
em.persist( new LoggedWithdrawl(amount) );
}
}
public void deposit(BigDecimal amount);
account.deposit(amount);
if ( amount.compareTo(LARGE_AMOUNT)>0 ) {
em.persist( new LoggedDeposit(amount) );
}
}
}
Unlike other simple Web Beans, a decorator may be an abstract class. If there's nothing special the decorator needs to do for a particular method of the decorated interface, you don't need to implement that method.
All decorators have a delegate attribute. The type and binding types of the delegate attribute determine which Web Beans the decorator is bound to. The delegate attribute type must implement or extend all interfaces implemented by the decorator.
This delegate attribute specifies that the decorator is bound to
all Web Beans that implement Account
:
@Decorates Account account;
A delegate attribute may specify a binding annotation. Then the decorator will only be bound to Web Beans with the same binding.
@Decorates @Foreign Account account;
A decorator is bound to any Web Bean which:
has the type of the delegate attribute as an API type, and
has all binding types that are declared by the delegate attribute.
The decorator may invoke the delegate attribute, which has much the same
effect as calling InvocationContext.proceed()
from an
interceptor.
We need to enable our decorator in
web-beans.xml
.
<Decorators>
<myapp:LargeTransactionDecorator/>
</Decorators>
This declaration serves the same purpose for decorators that the
<Interceptors>
declaration serves for interceptors:
it enables us to specify a total ordering for all decorators in our system, ensuring deterministic behavior, and
it lets us enable or disable decorator classes at deployment time.
Interceptors for a method are called before decorators that apply to that method.