拦截器和装饰器的区别

Posted

技术标签:

【中文标题】拦截器和装饰器的区别【英文标题】:Difference between interceptors and decorators 【发布时间】:2016-06-12 21:30:33 【问题描述】:

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 个装饰器。

装饰器示例

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 方法并确保事务在调用之前打开并在调用之后关闭。

拦截器示例

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

@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;
    

拦截器更灵活,但假设您更改了方法名称,如果您使用装饰器,您可能会收到编译器错误,使用拦截器,它将不匹配并且不会执行您的“环绕”逻辑。

【讨论】:

装饰器会自动应用于实现相同接口的类(使用自定义限定符除外)。所以不止一个装饰类。 同意,它是一对一或一对多,但同样,你有接口,所以它是面向对象的。使用 AOP,它更加灵活,因为您可以使用类型过滤器(例如代理某个类的所有方法)以及裸字符串 reg ex 匹配过滤器(例如代理所有以 select 开头并且是 com.example.dao 包的一部分的方法)。 您能否通过正则表达式匹配过滤器的示例将您的评论添加到您的答案中 已添加,没有完整的正则表达式支持,但您确实有一些通配符匹配。 谢谢。所以理论上我可以用拦截器重现装饰器的所有行为?【参考方案2】:

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

    装饰器必须实现它正在装饰的接口(但可以是抽象的,因此它不必实现方法)

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

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

【讨论】:

但是拦截器也可以通过ic.getTarget()获取引用【参考方案3】:

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

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

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

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

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

【讨论】:

抱歉,这个答案包含太多推测,在 Java/Jakarta EE 的上下文中没有用处。

以上是关于拦截器和装饰器的区别的主要内容,如果未能解决你的问题,请参考以下文章

python staticmethod,classmethod方法的使用和区别以及property装饰器的作用

Python 装饰器和装饰器模式有啥区别?

python闭包和装饰器的理解

6.5装饰器的类型

面试题新

Python装饰器的实现和万能装饰器