拦截器和装饰器之间的区别

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个装饰类/界面编写1个装饰器。

装饰器示例

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概念的一部分,你为一堆类/方法编写1个拦截器,例如,你拦截所有DAO方法,并确保事务在调用之前打开,并在调用后关闭。

拦截器示例

声明一个切入点(要匹配的内容),在这里您可以匹配 MyDao 类中以 insert 开头的任何方法,具有任何参数和任何返回类型。

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

然后,您声明一个引用切入点的周围建议

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

拦截器更灵活,但想象一下你更改方法名称,如果你使用装饰器,你可能会得到一个编译器错误,使用拦截器,它只是不匹配,不会执行你的“周围”逻辑。


答案 2

通常,修饰器用于添加新功能或修改现有功能。它使用组合作为继承的替代方法。修饰器通常提供在修饰类中不可用的其他 API(方法)。

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

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

  • 是否可以引入新方法或仅围绕现有方法执行?@Interceptor
  • 是否可以重写现有方法或仅追加其他行为?@Interceptor
  • 可以跨包和类层次结构应用,还是受其中一个层次结构的约束?@Decorator

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


推荐