拦截器和装饰器的区别

Difference between interceptors and decorators

Java中的拦截器和装饰器有什么区别吗?严格来说,我可以用装饰器实现拦截器无法实现的东西吗?

除了我必须检查方法名称以在拦截器中添加特定于方法的行为的问题:

拦截器:

@Nice
@Interceptor
public class NiceGreeterInterceptor {
  @AroundInvoke
  public Object decorate(InvocationContext ic) throws Exception {
    Method method = ic.getMethod();
    String methodName = method.getName();
    Object result = ic.proceed();
    if (methodName.equals("greet")) {
      return "NEW " + result;
    }
  }
}

装饰者:

@Decorator
public class GreeterDecorator implements Greeter {
  @Inject
  @Any
  @Delegate
  private Greeter greeter;

  @Override
  public String greet() {
    return "NEW " + greeter.greet();
  }
}

或者说我可以用拦截器重现装饰器的所有行为但是使用装饰器更舒服是合理的吗?

装饰器

一个区别是,正如您的示例所示,对于装饰器,您通常为每 1 个装饰器编写 1 个装饰器 class/interface。

装饰器示例

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
    }
}

拦截器

使用拦截器,它们是 AOP concept, you write 1 interceptor for a bunch of classes / methods, e.g. you intercept all DAO 方法的一部分,并确保事务在调用之前打开并在调用之后关闭。

拦截器示例

声明一个pointcut(匹配什么),在这里你匹配MyDao class中以insert开头的任何方法,有任何参数和任何 return 类型。

@Pointcut("execution(* com.example.dao.MyDao.insert*(..))")
public void insertPointcut() {
}

然后声明一个引用切入点

around advice
@Around(value = "com.example.SystemArchitecture.insertPointcut()")
public void interceptMethod(ProceedingJoinPoint pjp) {
        // do pre-work
        Object retVal = pjp.proceed();
        // do post work
        return retVal;
    }
}

拦截器更灵活但是假设你改变了方法名,如果你使用装饰器,你可能会得到一个编译器错误,使用拦截器,它不会匹配并且不会执行你的'around'逻辑.

装饰器与拦截器非常相似,但有两个有趣的区别:

  1. 装饰器必须实现它正在装饰的接口(而且可以是抽象的,所以它不必实现方法)

  2. 装饰器可以引用它所装饰的对象。它是通过注入完成的

参考:https://blog.frankel.ch/cdi-an-overview-part-2

一般而言,装饰器用于添加新功能或修改现有功能。它使用组合作为继承的替代方法。装饰器通常提供额外的 APIs(方法),这些在装饰的 classes 中不可用。

另一方面,AOP(例如拦截器)用于增强现有行为。它不会添加额外的 API 并且通常不会修改现有功能。它由现有功能的调用触发,并通过采取一些行动来响应;但现有功能以及现有 API 保持不变。

我不熟悉 JEE 实现,因此它们可能模糊了这两种模式之间的界限。要比较的要点是,

  • 可以@Interceptor引入新方法还是只围绕现有方法执行?
  • 能否@Interceptor覆盖现有方法或仅附加额外的行为?
  • 可以 @Decorator 跨包和 class 层次结构应用,还是受其中之一的限制?

除了两种模式之间的功能差异之外,考虑潜在的性能差异也可能很有趣。我希望 @Interceptor 会慢得多,因为它需要在运行时检查方法调用,而 @Decorator 调用可以在编译时解决。