SeamFramework.orgCommunity Documentation

第 8 章 装饰器

8.1. 委托属性
8.2. 激活装饰器

拦截器能够以一种强大的方式来捕获和分离关注点,拦截器和类型系统的关系是正交的。任何拦截器都能够拦截任何Java类型的调用。这种特性能够让拦截器很好的处理技术关注点,例如事务管理和安全。不过,拦截器无法领会其所拦截时间的真实语义。因此,拦截器并不是分离业务相关的关注点的好工具。

对装饰器来说,反之亦然。一个装饰器只拦截特定Java接口的调用,因此它能够领会这个接口关联的语义。装饰器的特性使其成为某些业务关注点的理想的建模工具。这也意味着装饰器并不拥有一个拦截器的普遍性。装饰器无法解决跨越多个不同类型的技术关注点。

假定我们有一个表现帐户的接口:

public interface Account {

    public BigDecimal getBalance();
    public User getOwner();
    public void withdraw(BigDecimal amount);
    public void deposit(BigDecimal amount);
}

我们系统实现中可以有多个不同的Web Beans实现 Account 接口。然而我们有一个通用的法律要求对于任何帐户,大的交易必须由系统在一个特定的日志中记录。装饰器非常适合处理这种工作。

装饰器是一个简单的Web Bean,能够实现其装饰的类型。装饰器使用 @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) );
        }
    }
    
}

和其他简单的Web Beans不同,一个装饰器可以是一个抽象类。如果被装饰的接口中一个特殊方法对装饰器没有用处,那么装饰器可以不实现这个方法。

所有装饰器都有一个 委托属性 。委托属性的类型和绑定类型决定了装饰器绑定到哪个Web Bean上。委托属性类型必须实现或者继承装饰器实现的所有接口。

下面的委托属性指定了装饰器绑定到所有实现 Account 接口的Web Beans:

@Decorates Account account;

一个委托属性能够指定一个绑定注释。装饰器只能绑定具有相同绑定的Web Beans。

@Decorates @Foreign Account account;

一个装饰器可以绑定到任何符合下面条件的Web Bean上:

装饰器可以调用委托属性,这和从一个拦截器中调用 InvocationContext.proceed() 具有相同效果。

我们需要在 web-beans.xml 文件中激活我们的装饰器。


<Decorators>
    <myapp:LargeTransactionDecorator/>
</Decorators
>

这个声明对于装饰器来说和<Interceptors>声明对于拦截器来说是一样的:

一个方法的拦截器在这个方法的装饰器调用之前调用。