Scala:围绕建议或 Python 装饰器实现 Java 的 AspectJ

发布于 2024-11-07 05:39:27 字数 683 浏览 4 评论 0原文

我在创业过程中广泛使用了 Java + AspectJ。我很想切换到 Scala,但我有一个通用的设计模式,我不确定在 Scala 中实现的最佳方式。

我们的应用程序大量使用 AspectJ 切入点,并使用注释作为标记。 这与 Python 的装饰器非常相似,博客介绍了在这里

我曾尝试在 Scala 中执行此技术,但遇到问题 AspectJ + Scala< /a>. 即使我确实让它工作了,它看起来也不像 Scala。

我见过一些项目做了一些按名称调用关闭魔法(我认为这就是他们正在做的)。

替换 @Transaction 的示例:

transaction {
// code in here.
}

我不得不说,虽然我更喜欢注释,因为它看起来更具声明性。 Scala 声明式“装饰”代码块的方式是什么?

I have been using Java + AspectJ extensively for my startup. I would love to switch to Scala but I have a common design pattern that I am not sure entirely the best way to implement in Scala.

A tremendous amount of our application uses AspectJ pointcuts using annotations as the marker.
This is very similar to Python's decorator and blogged about it here.

I have tried doing this technique in Scala but had problems with AspectJ + Scala.
Even if I did get it to work it seems unScala like.

I have seen some projects do some call-by-name closure magic (I think thats what they are doing).

Example of a replace of @Transaction:

transaction {
// code in here.
}

I have to say though I prefer the annotation more as it seems more declarative.
What is the Scala way of declaratively "decorating" code blocks?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

廻憶裏菂餘溫 2024-11-14 05:39:27

顺便说一句,我将在 Scala Days 2011 上就同一主题发表演讲。核心思想与 Kim 和 Dean 的例子相同。然而,当涉及到全方位的横切关注点时,相似性和差异性就变得更加微妙。

一方面,存在一些并非真正交叉的问题,例如缓存。当宿主语言不支持高阶函数(例如 Java)时,将关注点实现为一个方面就变得很有吸引力。例如,使用 AspectJ 和注释方法,您可以说:

    @Cacheable(keyScript="#account.id")
    public double getNetWorth(Account account) {
       ... expensive computation
    }

但是使用 Scala 中的高阶函数,您可以这样做:

    def getNetWorth(account: Account) : Double = {
        cacheable(keyScript=account.id) {
          ... expensive computation
        }
    }   

Scala 方法要好得多,因为:

  • 缓存不太可能广泛适用。例如,一个类中的所有方法或包中所有类中的所有公共方法不太可能都是可缓存的。即使存在这种情况,keyScript 也不太可能相同或容易以通用形式表达。
  • AspectJ 方法使用注释作为拐杖来提供不错的实现。 Scala中的高阶函数可以直接表达意图。
  • AspectJ 方法需要使用外部语言(例如 OGNL 或 Spring 表达式语言)来计算密钥。使用 Scala,您可以只使用宿主语言。

中间,存在常见的横切关注点事务管理和安全性。从表面上看,它们看起来很像缓存。然而,我们在实践中发现,将这些功能应用于类的所有方法(具有通用子选择,例如具有公共访问权限的方法)或用注释标记的所有类的所有方法(例如 @Service )很常见。如果是这种情况,AspectJ 方法最终会更优越,因为它提供了一种在比高阶函数更高级别应用功能的方法。当类级注释就可以正常工作时,您不再需要用事务性{}安全{} 包围每个方法。对于类似安全的问题,AspectJ 方法提供了一种更简单的方法来执行安全审计。

另一端是横切关注点,例如跟踪、分析、监控、策略执行、审计、某些形式的并发控制(例如 Swing 的/SWT/Android UI 线程调度)等。这些非常适合由切入点选择(带注释,通常不带注释)。仅使用高阶函数很难以一致的方式执行相同的操作。

还有更多语义上的细微差别,但最重要的是,当您发现注释每个方法以应用横切关注点时,高阶函数可能是更好的方法。对于其他人来说,将 Scala 与 AspectJ 结合使用可能会提供一致且紧凑的解决方案。

ps 我最近没有在 Eclipse 中尝试过 AspectJ+Scala(因为 Eclipse 中的 Scala 最近才开始工作)。但使用 Maven 的外部构建在 http://lampsvn.epfl.ch/trac/scala 之后工作正常/ticket/4214 已修复。

Incidentally, I am giving a talk at Scala Days 2011 on the same topic. The core idea is the same as Kim's and Dean's example. However, when it comes to the full spectrum of crosscutting concerns, similarity and differences become more nuanced.

On one end of the spectrum, there are not-really-crosscutting concerns such as caching. When the host language doesn't support higher-order functions (for example, Java), implementing the concern as an aspect becomes attractive. For example, with AspectJ and annotation approach, you can say:

    @Cacheable(keyScript="#account.id")
    public double getNetWorth(Account account) {
       ... expensive computation
    }

But with higher-order function in Scala, you can do:

    def getNetWorth(account: Account) : Double = {
        cacheable(keyScript=account.id) {
          ... expensive computation
        }
    }   

The Scala approach is much better, because:

  • Caching is unlikely to be widely applicable. For example, it is unlikely that all method in a class or all public methods in all classes in a package are cacheable. And even if there is such situation, the keyScript is unlikely to be the same or easily expressible in a generic form.
  • The AspectJ approach uses annotation as a crutch to offer a decent implementation. With higher-order function in Scala, the intention is expressed directly.
  • The AspectJ approach needs to use an external language (such as OGNL or Spring Expression Language) to compute the key. With Scala, you can just use the host language.

In the middle, there are common crosscutting concerns transaction management and security. On the surface, they look much like caching. However, we find in practice that applying these functionalities to all methods of a class (with generic sub-selection such as those with public access) or all methods of all classes marked with an annotation (say @Service) is common. If such is the case, the AspectJ approach ends up being superior, since it provides a way to apply the functionality at a higher level that higher-order functions. You no longer have to surround each and every method with transactional {} or secured {}, when a class-level annotation will do just fine. For security-like concerns, AspectJ approach allows an easier way to perform security auditing.

On the other end of the spectrum are crosscutting concerns such as tracing, profiling, monitoring, policy enforcement, auditing, some forms of concurrency control (such as Swing's/SWT/Android UI thread dispatching), etc. These lend themselves very well to be selected by a pointcut (with and often without annotations). It is very difficult to do the same in a consistent manner using higher-order functions alone.

There are more semantic nuances, but the bottom line is that when you find annotating every method to have a crosscutting concern applied, higher-order function is likely to be a better approach. For others, using Scala with AspectJ is likely to provide consistent and compact solution.

p.s. I haven't tried AspectJ+Scala in Eclipse lately (since Scala in Eclipse started to work only recently). But external build using Maven worked fine after http://lampsvn.epfl.ch/trac/scala/ticket/4214 was fixed.

唱一曲作罢 2024-11-14 05:39:27

scala 方式是

def transaction(f: =>Unit) = {
  println("start transaction")
  f
  println("end transaction")
}

transaction {
  println("inside transaction")
}

这样打印

start transaction
inside transaction
end transaction

The scala way would be

def transaction(f: =>Unit) = {
  println("start transaction")
  f
  println("end transaction")
}

transaction {
  println("inside transaction")
}

This prints

start transaction
inside transaction
end transaction
有木有妳兜一样 2024-11-14 05:39:27

与注释相比,使用事务方法方法还有其他优点。您可以添加 catch 和finally 子句以确保正确的资源清理。稍微扩展 Kim 的示例:

def transaction(f: =>Unit) = {
  println("start transaction")
  try {
    f
    println("end successful transaction")
  } catch {
    case ex => 
      // rollback?
      println("end failed transaction")
  } finally {
      // cleanup?
      println("end cleanup")
  }      
}

transaction {
  println("inside transaction")
}

您还可以调用方法体内的事务,但不能注释方法内部的块。当然,您可以使用注释将该内部块设置为另一个方法调用。

从我的 Java 时代起,我就了解注释和 XML 配置文件的吸引力,但现在,我更喜欢将所有内容编写为“正常”代码,因为这样具有一致性和更强的表达能力。我仅在调用需要注释的 Java 库时才使用注释。另外,如果您使代码尽可能“功能化”,那么一切都是声明性的! ;)

There are other advantages with using the transaction method approach vs. annotations. You can add catch and finally clauses to ensure proper resource cleanup. To extend Kim's example a bit:

def transaction(f: =>Unit) = {
  println("start transaction")
  try {
    f
    println("end successful transaction")
  } catch {
    case ex => 
      // rollback?
      println("end failed transaction")
  } finally {
      // cleanup?
      println("end cleanup")
  }      
}

transaction {
  println("inside transaction")
}

You can also make calls to transaction inside method bodies, whereas you can't annotate a block internal to a method. Of course, you could just make that internal block another method call with an annotation.

I understand the attraction of annotations, and XML configuration files for that matter, from my Java days, but these days, I prefer having everything written as "normal" code, because of the uniformity and greater expressive power. I only use annotations when I'm calling a Java library that requires them. Also, if you make your code as "functional" as possible, then everything is declarative! ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文