Scala:实现Java';将AspectJ与建议或Python修饰符联系起来

Scala:实现Java';将AspectJ与建议或Python修饰符联系起来,java,python,scala,aop,aspectj,Java,Python,Scala,Aop,Aspectj,我在创业时一直广泛使用Java+AspectJ。我很想切换到Scala,但我有一个通用的设计模式,我不确定在Scala中实现的最佳方式 我们的应用程序大量使用AspectJ切入点,使用注释作为标记。 这与Python的decorator和decorator非常相似 我曾尝试在Scala中使用此技术,但在使用时遇到了问题。 即使我真的让它起作用了,它看起来也很像 我已经看到一些项目做了一些命名闭包魔术(我认为这就是他们正在做的) 替换@Transaction的示例: transaction { /

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

我们的应用程序大量使用AspectJ切入点,使用注释作为标记。 这与Python的decorator和decorator非常相似

我曾尝试在Scala中使用此技术,但在使用时遇到了问题。 即使我真的让它起作用了,它看起来也很像

我已经看到一些项目做了一些命名闭包魔术(我认为这就是他们正在做的)

替换@Transaction的示例:

transaction {
// code in here.
}
我不得不说,尽管我更喜欢注释,因为它看起来更具声明性。 什么是Scala方式声明性地“修饰”代码块?Scala方式应该是

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

transaction {
  println("inside transaction")
}
这张照片

start transaction
inside transaction
end transaction

与注释相比,使用事务方法方法还有其他优点。您可以添加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库时使用注释。此外,如果您使代码尽可能“功能化”,那么一切都是声明性的!;)

顺便说一句,我正在做一个演讲。其核心思想与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方法最终会更优越,因为它提供了一种在更高级别上应用功能的方法,而不是在更高阶函数上应用功能。当类级别的注释可以正常工作时,您不再需要用
transactional{}
secured{}
围绕每个方法。对于类似于安全性的问题,AspectJ方法允许更容易地执行安全审计

另一端是横切关注点,如跟踪、分析、监视、策略实施、审核、某些形式的并发控制(如Swing的/SWT/Android UI线程调度)等。这些关注点非常适合被切入点选择(有注释或通常没有注释)。单独使用高阶函数很难以一致的方式做到这一点

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


p、 我最近没有在Eclipse中尝试AspectJ+Scala(因为Eclipse中的Scala最近才开始工作)。但是使用Maven的外部构建在修复之后运行良好。

根据需要,使用类型参数定义它可能更好:
def transaction[a](f:=>a)=……
。这样做可以让您从包装的代码块中返回有用的值。很高兴看到我的猜测是正确的,按名称调用是最佳选择。@David:我完全同意,但我决定使用一个简单的示例。我相信您可以用AspectJ(即资源清理)完成刚才显示的所有操作。AspectJ功能更强大,因为它可以让你在方法上找到切入点,而这些方法甚至不知道它们被修饰过(即没有标记)。多么好的回答!我想真正的优势是不必在方面(相当于Java的一个奇怪的扩展)上乱动。你的AspectJ书非常有用。我现在对AspectJ最大的不满就是看着我的Eclipse在编写代码的时候要么大吃一惊要么大吃一惊。@adam谢谢!您是否正在使用SpringSource工具套件(STS)?如果没有,您可能想尝试一下,因为这将有助于排除配置问题。我的业务伙伴使用它。我使用Eclipse+STS插件。具有讽刺意味的是,我今天才开始使用SpringEhcache注释,所以您的示例恰到好处。