Scala: Реализация Java AspectJ вокруг рекомендаций или декораторов Python - PullRequest
7 голосов
/ 14 мая 2011

Я широко использовал Java + AspectJ для своего запуска. Я бы хотел перейти на Scala, но у меня есть общий шаблон проектирования, который я не уверен, что это лучший способ реализовать его в Scala.

Огромное количество нашего приложения использует AspectJ pointcut, используя аннотации в качестве маркера. Это очень похоже на декоратор Python и написал об этом здесь .

Я пытался использовать эту технику в Scala, но у меня были проблемы с AspectJ + Scala . Даже если бы я заставил это работать, это кажется unScala как.

Я видел, как некоторые проекты совершали магию закрытия по имени (я думаю, именно это они и делают).

Пример замены @Transaction:

transaction {
// code in here.
}

Я должен сказать, что предпочитаю аннотацию, так как она кажется более декларативной. Что такое способ Scala декларативно "декорировать" блоки кода?

Ответы [ 3 ]

12 голосов
/ 14 мая 2011

Кстати, я выступаю на Scala Days 2011 по той же теме .Основная идея такая же, как у Кима и Дина.Тем не менее, когда дело доходит до полного спектра сквозных проблем, сходство и различия становятся более нюансированными.

На одном конце спектра , существуют не совсем сквозные проблемы, такие как кеширование,Когда основной язык не поддерживает функции более высокого порядка (например, 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 Expression Language) для вычисления ключа.В Scala вы можете просто использовать язык хоста.

В середине распространены общие проблемы управления транзакциями и безопасности.На первый взгляд, они очень похожи на кеширование.Однако на практике мы находим, что применение этих функций ко всем методам класса (с общим подвыбором, например, с открытым доступом) или ко всем методам всех классов, помеченных аннотацией (скажем, @Service), является распространенным явлением.Если это так, то подход AspectJ в конечном итоге превосходит, поскольку он предоставляет способ применить функциональность на более высоком уровне, чем функции более высокого порядка.Вам больше не нужно окружать каждый метод transactional {} или secured {}, когда аннотация на уровне класса будет работать нормально.Применительно к проблемам безопасности подход AspectJ позволяет упростить аудит безопасности.

На другом конце спектра - это общие задачи, такие как трассировка, профилирование, мониторинг, применение политик,аудит, некоторые формы управления параллелизмом (такие как диспетчеризация потоков Swing / SWT / Android) и т. д. Они очень хорошо подходят для выбора с помощью pointcut (с аннотациями и часто без них).Очень трудно сделать то же самое согласованным образом, используя только функции более высокого порядка.

Существует больше семантических нюансов, но суть в том, что, когда вы обнаружите, что для каждого метода применяется аннотирование с применением сквозной задачи,функция более высокого порядка, вероятно, будет лучшим подходом.Для других использование Scala с AspectJ, вероятно, обеспечит согласованное и компактное решение.

ps Я не пробовал AspectJ + Scala в Eclipse в последнее время (так как Scala в Eclipse начал работать только недавно).Но внешняя сборка с использованием Maven работала нормально после исправления http://lampsvn.epfl.ch/trac/scala/ticket/4214.

8 голосов
/ 14 мая 2011

Путь scala будет

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

transaction {
  println("inside transaction")
}

Это печатает

start transaction
inside transaction
end transaction
5 голосов
/ 14 мая 2011

Есть и другие преимущества использования метода транзакций по сравнению с аннотациями.Вы можете добавить предложения catch и finally, чтобы обеспечить правильную очистку ресурса.Чтобы немного расширить пример Кима:

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

Вы также можете делать вызовы транзакции внутри тела метода, тогда как вы не можете аннотировать внутренний блок для метода.Конечно, вы могли бы просто сделать этот внутренний блок еще одним вызовом метода с аннотацией.

Я понимаю привлекательность аннотаций и файлов конфигурации XML в этом отношении с моих дней Java, но сейчас я предпочитаю иметьвсе написано как «нормальный» код, из-за единообразия и большей выразительной силы.Я использую аннотации только тогда, когда я вызываю библиотеку Java, которая требует их.Кроме того, если вы сделаете свой код максимально функциональным, тогда все будет декларативным!;)

...