0,0
рейтинг
1 марта 2011 в 03:59

Разработка → Знакомство с АОП

Парадигмы программирования


В современном мире IT-разработки существует довольно большое множество различных подходов к написанию программ. Так, например, кому-то нравиться представлять программу в виде последовательности действий, а кто-то считает, что программа должна представлять собой множество объектов, общающихся друг с другом. Совокупности этих идей и понятий образуют своего рода стиль написания программы, который принято назвать – парадигма программирования.

У каждой парадигмы есть свои особенности, однако, главным фактором, различающим их, является понятие основной единицы программы. Вот самые популярные из них:
  • инструкция (императивное программирование, FORTRAN/C/PHP),
  • функция (функциональное программирование, Haskell/Lisp/F#/Scala),
  • прототип (прототипное программирование, JavaScript),
  • объект (объектно-ориентированное программирование, С++/Java),
  • факт (логическое программирование, PROLOG).

Стоит заметить, что в общем случае язык программирования однозначно не определяет используемую парадигму: на том же PHP можно писать как императивные, так и объектно-ориентированные программы.

В этой статье я хочу рассказать о сравнительно молодой, но крайне, на мой взгляд, полезной парадигме программирования – аспектно-ориентированном программировании.



Основы АОП


Рассмотри некоторую сферическую службу в вакууме (например, web-сервис), реализующую следующий метод:
public BookDTO getBook(Integer bookId) {
  BookDTO book = bookDAO.readBook(bookId);
  return book;
}

Метод довольно прост и очевиден: чтение информации о некоторой книге по её идентификатору. Но давайте подумаем, чего тут не хватает? Первым делом нам стоит задуматься о логировании – без него, как вы сами понимаете, в web-службе никуда:
public BookDTO getBook(Integer bookId) {
  LOG.debug("Call method getBook with id " + bookId);

  BookDTO book = bookDAO.readBook(bookId);

  LOG.debug("Book info is: " + book.toString());
  return book;
}

Далее необходимо реализовать обработку исключений (сделать так, что бы слой служб возвращал соответствующие ему исключения, скрывая исключения нижележащих слоёв):
public BookDTO getBook(Integer bookId) throws ServiceException {
  LOG.debug("Call method getBook with id " + bookId);
  BookDTO book = null;

  try {
    book = bookDAO.readBook(bookId);
  } catch(SQLException e) {
    throw new ServiceException(e);
  }


  LOG.debug("Book info is: " + book.toString());
  return book;
}

Так же не стоит забывать о проверке прав доступа:
public BookDTO getBook(Integer bookId) throws ServiceException, AuthException {
  if (!SecurityContext.getUser().hasRight("GetBook"))
    throw new AuthException("Permission Denied");


  LOG.debug("Call method getBook with id " + bookId);
  BookDTO book = null;

  try {
    book = bookDAO.readBook(bookId);
  } catch(SQLException e) {
    throw new ServiceException(e);
  }

  LOG.debug("Book info is: " + book.toString());
  return book;
}

Кроме того имеет смысл кешировать результат работы:
public BookDTO getBook(Integer bookId) throws ServiceException, AuthException {
  if (!SecurityContext.getUser().hasRight("GetBook"))
    throw new AuthException("Permission Denied");

  LOG.debug("Call method getBook with id " + bookId);
  BookDTO book = null;
  String cacheKey = "getBook:" + bookId;

  try {
    if (cache.contains(cacheKey)) {
      book = (BookDTO) cache.get(cacheKey);
    } else {

      book = bookDAO.readBook(bookId);
      cache.put(cacheKey, book);
    }

  } catch(SQLException e) {
    throw new ServiceException(e);
  }

  LOG.debug("Book info is: " + book.toString());
  return book;
}

Можно продолжать совершенствовать данный метод, но для начала — достаточно. В ходе наших доработок мы получили метод в 10 раз (с 2 до 20 LOC) превышающий исходный размер. Самое интересное, что объём бизнес-логики в нём не изменился – это всё та же 1 строка. Остальной код реализует некоторую общую служебную функциональность приложения: логирование, обработку ошибок, проверку прав доступа, кеширование и так далее.

В принципе, переплетение бизнес-логики со служебным функционалом не так страшно, пока ваше приложение невелико. Однако, чем сложнее становится программа, тем более тщательно следует подходить к её архитектуре в целом и выделении общей функциональности в частности. Именно поэтому, наблюдая за эволюцией языков программирования, сначала мы видим появление функций, потом модулей, затем объектов. Однако, практика показывает, что для выделения некоторой общей функциональности, упомянутых выше парадигм недостаточно. Такую функциональность называют «сквозной» или «разбросанной», в виду того, что её реализация действительно разбросана по разным частям приложения. Примерами сквозной функциональности, как мы уже видели выше, могут служить:

Основной задачей аспектно-ориентированного программирования (АОП) является модуляризация сквозной функциональности, выделение её в аспекты. Для этого языки, поддерживающие концепцию АОП, реализуют следующие средства для выделения сквозной функциональности:
  • аспект (aspect) – модуль или класс, реализующий сквозную функциональность. Аспект изменяет поведение остального кода, применяя совет в точках соединения, определённых некоторым срезом. Так же аспект может использоваться для внедрения функциональности;
  • совет (advice) – дополнительная логика — код, который должен быть вызван из точки соединения. Совет может быть выполнен до, после или вместо точки соединения;
  • точка соединения (join point) — точка в выполняемой программе (вызов метода, создание объекта, обращение к переменной), где следует применить совет ;
  • срез (pointcut) — набор точек соединения. Срез определяет, подходит ли данная точка соединения к заданному совету;
  • внедрение (introduction) — изменение структуры класса и/или изменение иерархии наследования для добавления функциональности аспекта в инородный код;
  • цель (target) – объект, к которому будут применяться советы;
  • переплетение (weaving) – связывание объектов с соответствующими аспектами (возможно на этапе компиляции, загрузки или выполнения программы).


Пример использования (AspectJ)


AspectJ является аспектно-ориентированным расширением/framework’ом для языка Java. На данный момент это, пожалуй, самый популярный и развивающийся АОП движок.

Рассмотрим реализацию аспекта логирования с его помощью:
@Aspect
public class WebServiceLogger {
  private final static Logger LOG =
    Logger.getLogger(WebServiceLogger.class);

  @Pointcut("execution(* example.WebService.*(..))")
  public void webServiceMethod() { }

  @Pointcut("@annotation(example.Loggable)")
  public void loggableMethod() { }

  @Around("webServiceMethod() && loggableMethod()")
  public Object logWebServiceCall(ProceedingJoinPoint thisJoinPoint) {
    String methodName = thisJoinPoint.getSignature().getName();
    Object[] methodArgs = thisJoinPoint.getArgs();

    LOG.debug("Call method " + methodName + " with args " + methodArgs);

    Object result = thisJoinPoint.proceed();

    LOG.debug("Method " + methodName + " returns " + result);
  
    return result;
  }
}

Первым делом создаётся аспект логирования методов сервисов – класс WebServiceLogger, помеченный аннотацией Aspect. Далее определяются два среза точек соединения: webServiceMethod (вызов метода, принадлежащего классу WebService) и loggableMethod (вызов метода, помеченного аннотацией @Loggable). В завершении объявляется совет (метод logWebServiceCall), который выполняется вместо (аннотация Around) точек соединения, удовлетворяющих срезу («webServiceMethod() && loggableMethod()»).

В коде совета происходит получение информации о текущем методе (точке соединения), логирование начала выполнения метода, непосредственный вызов запрошенного метода, логирование и возвращение результата работы.

AspectJ обладает довольно большим объёмом поддерживаемых срезов точек соединения. Ниже приведены основные из них:
  • execution(static * com.xyz..*.*(..)) – выполнение кода любого статического метода в пакете com.xyz;
  • call(void MyInterface.*(..)) – вызов любого метода, возвращающего void, интерфейса MyInterface;
  • initialization(MyClass || MyOtherClass) – инициализация класса MyClass или MyOtherClass;
  • staticinitialization(MyClass+ && !MyClass) – статическая инициализация класса, имя которого начинается на MyClass, но не сам MyClass;
  • handler(ArrayOutOfBoundsException) – выполнение обработчика исключения ArrayOutOfBoundsException;
  • get/set(static int MyClass.x) — чтение / запись свойства x класса MyClass;
  • this/target(MyClass) – выполнение точки соединения, соответствующей объекту типа MyClass;
  • args(Integer) – выполнение точки соединения, в которой доступен аргумент типа Integer;
  • if(thisJoinPoint.getKind().equals(«call»)) – совпадает со всеми точками соединения, в которых заданное выражение истинно;
  • within/withincode(MyClass) — совпадает со всеми точками соединения, встречающимися в коде заданного класса;
  • cflow/cflowbelow(call(void MyClass.test())) – совпадает со всеми точками соединения, встречающимися в потоке выполнения заданного среза;
  • @annotation(MyAnnotation) – выполнение точки соединения, цель которой помечена аннотацией @MyAnnotation.

Что же касается советов, то их количество намного меньше, но они полностью покрывают всё необходимое множество ситуаций:
  • before – запуск совета до выполнения точки соединения,
  • after returning — запуск совета после нормального выполнения точки соединения,
  • after throwing — запуск совета после выброса исключения в процессе выполнения точки соединения,
  • after — запуск совета после любого варианта выполнения точки соединения,
  • around – запуск совета вместо выполнения точки соединения (выполнение точки соединения может быть вызвано внутри совета).

Подробнее о конструкциях AspectJ можно прочитать в соответствующем разделе [1,2] официальной документации.

Для того, что бы использовать аспекты AspectJ их придётся скомпилировать и «вшить» в основные классы с помощью специального компилятора AJC.

Продукт бесплатный. Распространяется под Eclipse License.



Пример использования (PostSharp)


PostSharp является аспектно-ориентированным framework’ом для платформы .NET. Существуют и другие реализации АОП для .NET, однако, судя по сравнениям с сайта PostSharp, лидирующую позицию занимает именно он.

Рассмотрим, как с помощью него описать аспект обработки исключений. Первым делом необходимо создать класс, расширяющий соответствующий аспект:

public class ExceptionDialogAttribute : OnExceptionAspect
{
  public override void OnException(MethodExecutionEventArgs eventArgs)
  {
    string message = eventArgs.Exception.Message;
    Window window = Window.GetWindow((DependencyObject)eventArgs.Instance);
    MessageBox.Show(window, message, "Exception");
    eventArgs.FlowBehavior = FlowBehavior.Continue;
  }
}


Строго говоря, аспекты в терминологии PostSharp – это, как мы можем видеть, аспект и совет в терминологии АОП.

Для того, что бы указать срез точек пересечения для данного аспекта необходимо в файл настроек сборки (AssemblyInfo.cs) добавить следующую строку:

[assembly: ExceptionDialog ( AttributeTargetTypes="Example.WorkflowService.*",
         AttributeTargetMemberAttributes = AttributeTargetElements.Public )]


Или же явно пометить интересующие вас методы атрибутом ExceptionDialog:

[ExceptionDialog]
public BookDTO GetBook(Integer bookId)


Вот собственно и всё: теперь все выброшенные в соответствующих методах исключения будут обрабатываться созданным аспектом.

В виду того, что PostSharp частично склеивает понятия совета и аспекта, последних у него получается довольно немало. Подробно с ними можно познакомиться в документации. Ниже приведены основные из них:
  • OnMethodBoundary/OnMethodInvocation – обращение к методу (начало, конец, выход, выход с исключением);
  • OnFieldAccess – обращение к свойству;
  • OnException – обработка исключения;
  • Composition – внедрение кода;

Для работы PostSharp’у необходим компилятор и библиотека, которые нужно подключить к проекту. Вшивание аспектов основано на post-обработке байт-кода во время сборки приложения.

Продукт платный. Есть Community Edition.



От теории к практике


И так, мы только что увидели, как красиво и эффективно можно решить проблему «выноса за скобки» сквозного функционала в вашем приложении. Однако, это всё теория. На практике всё, естественно, немного иначе :)

Прежде всего, в обоих случаях для компиляции и «вшивания» (weaving) аспектов придётся использовать специальный компилятор и тащить вместе с проектом дополнительные библиотеки. Вроде бы, это не проблема: компилятор легко скачивается и интегрируется в среду (например, при использовании maven’a задача сведётся всего лишь к добавлению плагина aspectj-maven-plugin), а множество зависимостей – обычное дело, по крайней мере для Java-приложений (решаемая с помощью того же maven’a). Однако, необходимость включения в проект чего-то, что требует отдельной компиляции, да ещё и не имеет широкого распространения, зачастую отпугивает разработчиков, не смотря на все потенциальные плюсы.

В данном случае решением проблемы может стать Spring Framework [1,2]. Данный фреймворк имеет много достоинств, однако в рамках данной статьи нас интересует его AOP-составляющая. Spring Framework реализует ограниченную AOP-функциональность на чистом Java (C#) без использования сторонних библиотек с помощью создания прокси-объектов (JDK Dynamic Proxy, CGLIB). Другими словами в Spring AOP можно использовать только точки соединения типа «выполнение метода». Однако, как показывает практика, данное ограничение не играет значительной роли, так как для решения большинства задач, требуется точки соединения именно этого типа.

Кроме того, Spring Framework поддерживает конфигурирование приложений c помощью @AspectJ аннотаций, а так же интеграцию аспектов скомпилированных непосредственно с помощью AspectJ.

У себя в компании мы используем именно Spring AOP. Учитывая прочие заслуги Spring Framework, на мой взгляд, он является самой доступной и удобной площадкой для работы с AOP, внося значительный вклад в его популяризацию и развитие.



Резюме


Подводя итоги, хочется выделить три основные мысли относительно АОП:
  • Основная цель АОП — выноса «общей» (сквозной) функциональности «за скобки» (модуляризация сквозной функциональности);
  • Для Java AOP доступен через проект AspectJ, для .NET – через PostSharp;
  • Наиболее простая и проверенная реализация AOP – Spring AOP.


Ссылки по теме


Михаил Крестьянинов @krestjaninoff
карма
128,2
рейтинг 0,0

Похожие публикации

Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (70)

  • +4
    Cool story, Bro! Как раз читаю спринговый мануал и эти аспекты как то не особо интуитивно понятны (были).
    • +6
      Да, статью в избранное!

      Кстати, для себя вывел рецепт первого комментария на Хабре, которым пользуются многие:
      1) похвалить автора;
      2) упомянуть, что тоже этим занимался/занимаешься/будешь заниматься;
      3) сказать, что без статьи ничего/что-то не получалось;
      4) профит.

      Может стоит разнообразить, как думаете?
      А то какой-то аналог первон@*а получается.
      • +8
        Ну а почему бы не похвалить автора если он этого действительно заслуживает? Или что, мне ждать пока выскажется десяток-другой критиков?
        Меня искренне умиляют индивиды, ищущие в этом подоплеку.
        • –9
          Ну ладно, простите что минусанул коммент и умильнул вас :)
          Просто мне кажется, можно похвалить авторов и иначе, и побогаче!
        • +16
          многие не знают, что на самом деле означает фраза cool story, bro! вот цитата из urban dictionary:

          An appropriate response to a story that went nowhere and/or was completely uninteresting.
          «This one time me and my brother were eating sandwiches and he was like 'dude, I hate mustard' and I was like 'yeah.'»
          «Cool story bro.»


          похвалы тут никакой нет, только сарказм.
          • 0
            ну вот, спасибо за минус в карму.
      • 0
        Это еще ничего, раньше встречалось часто нечто в духе «под кат». =)
  • +5
    Все отлично, но может кто-то сможет написать реальный, работающий пример? Такое банальное, как, например гостевая?
    • +1
      Такая же мысль, для общей завершенности статьи не хватает кода веб-сервиса в стиле АОП.
      • +5
        Интерпретатор брейнфака ^_^
  • +2
    В плане разработки, бесспорно, использовать АОР удобно. Но тогда назревает такой вопрос, как же это все скажется на быстродействии приложения?
    • 0
      сильно не скажется. тем более когда аспекты интегрируются изменением байткода — тогда это все равно что пример из первой части статьи
    • +1
      Приведу пример — практически все приложения под MacOS/iOS пишутся на Obj-C, а там вместо вызова методов используется посылка сообщений (наследство от smalltalk). Так вот практически каждое приложение в том или ином виде имеет у себя какую-то дополнительную обработку этих сообщений (по сути это похоже на техническую реализацию АОП). При этом особой нагрузки это не накладывает и считается нормой, причем в таком виде реализованы все фреймворки и визуальные элементы управления.

      p.s.
      Вы легко можете перехватывать сообщения (читай вызовы методов), менять их параметры и передавать дальше, передавать в другие обьекты, пересылать даже по сети, блокировать, обрамлять дополнительным кодом, создавать дополнительную логику и т.д.
      Так что можно сказать что АОП довольно просто и часто используется в мак программах, хотя и скрывается под именами таких паттернов как прокси/декораторы, адаптеры, цепочки пересылки, фасады, уведомления и вариации.
    • 0
      Спасибо, буду иметь в виду.
  • +2
    Эта парадигма молодая только в Java мире. Аналогичный подход давно используется в Lisp и встроен в язык (макросы, иногда первоклассные функции)на порядок удобнее Java библиотек, работающих через reflection либо модификацию байт-кода.

    Проблема как всегда в том, что мало у кого есть время и желание знакомиться с другими языками. После умножения на «модность» и поддержку корпоративного мира получается очередное изобретение велосипеда.
    • 0
      А она и в ava мире не молодая, ей уже 10 лет как минимум.

      PS. По-моему, макросы в Лисп — это всё-таки другое.
      • 0
        Макросы — это другое. Но в Лиспе с 88-го года советы есть.

        Заодно (карма не позволяет чаще комментить) хотелось бы заметить что в том же Лиспе (Common Lisp) ОО-парадигма реализована на порядок лучше чем в Java, не говоря уже о C++.

        Базовй Common Lisp (без библиотек) позволяет использовать его как императивный или ОО. ФП там с небольшой натяжкой, не всё можно сделать чистыми функциями, в отличие от Haskell.

        Это фактическая ошибка — исправьте, пожалуйста.
        • +1
          Если поставивший минус намекает что основная единица программы — это функция, то тоже мимо. «Функции» Лиспа в подавляющем большинстве случаев оказываются процедурами. Первого класса, возвращающие значение, но процедуры.

          Если же он намекал на что-то другое, то хотелось бы услышать на что именно. Не стесняйтесь, пальцы не отсохнут. Гарантирую.
          • 0
            Извиняюсь, что лезу в разговор, но не могли бы Вы дать ссылки, где рассказывается, чем принципиально отличается процедура от функции, чтобы я мог восполнить пробелы в своих знаниях. Пока их хватает только на то, что процедура это ничего не возвращающая функция (ну или возвращающая void).
            • 0
              Определения противоречивы. В одних источниках функция *может* вернуть результат, в других — *должна* вернуть результат.

              (defun test (x)
              (if (> x 0)
              t
              (values)))

              При передаче в эту функцию положительного аргумента, она вернёт t (true), отрицательного — не вернёт ничего. Как её называть? :)

              По теме: Программа на CL может быть и просто скриптом. Фанаты «ненормального программирования» могут писать и на макросах.

              Вообще, классификация в статье достаточно странная. С этой точки зрения (основная единица кода) можно просто поменять местами C и Common Lisp. И будет больше правды, т.к. код на C обязательно должен быть в функциях или процедурах, а на Лиспе — может быть и в макросах и просто в toplevel'е.
            • 0
              В тех языках, где есть собственно понятие процедуры — отличие одно, и реальное: функция возвращает значение и сама по себе является элементарным вычислимым выражением. Некоторые языки требуют, чтоб её значение любого выражения было обязательно использовано — передано или присвоено. Процедура значений не возвращает, и является самостоятельной командой.
      • 0
        Да, макросы шире чем «советы», но позволяют легко их реализовывать.

        Найти код макроса намного проще, чем найти код, который реализует соответствующую аннотацию в java подходе.
    • 0
      AOP в buzzword-языка[ соотвествует такому понятию, как комбинация методов в CLOS — объектной системе Common Lisp. И если посмотреть на тех, кто придумывал CLOS и кто потом пропогандировал AOP, то пересечение этих множеств будет непустым ;)

      Кратко поясню, что в CLOS можно добавлять :around, :before и :after-методы, которые вписываются в схему вызовов по стандартному алгоритму, позволяя без проблем реализовать все описанное в статье :)

      И напомню, что стандарт ANSI Common Lisp (включающий в себя CLOS) выпущен в 1994 г., но первые реализации появились ещё в 80-х (конечно, они не были 100% совместимыми с последующим стандартом, но обсуждаемая функциональность там была).
    • 0
      Когда я вижу как джависты начинают вытворять вещи подобные вышеописанному компилятору для встраивания АОП в язык мне страсть как хочется воскликнуть: «Понеслась пизда по кочкам» Реализация 80% части описанного АОП-фреймворка в Clojure — лиспе поверх JVM — это, бл**ь, 70 строк кода zahardzhan.github.com/2010/clojure-hooks.html.
  • 0
    Правильно ли я понимаю, что смысл АОП заключается в том, чтобы дать разработчику возможность создавать типовые декораторы для вызовов функций/методов/конструкторов и всего остального, что назвали «точками соединения», и применять их к интересующим точкам соединения автоматизированно, при помощи дополнительного набора инструкций?
    • 0
      Мне тоже ровно так же показалось. А ещё стало интересно, насколько код читаемым получается. Не рождается ли не просто write only язык, а write only подход?

      Я так понимаю, что, наверное, IDE должна многое показывать. Есть ли специализированные IDE? Под все платформы?
    • 0
      Что-то типа того
  • +1
    В ООП есть замечательный паттерн «chain of responsibility»
    По крайней мере кэширование, логирование и проверку прав с ним делать на ура
    • +1
      В последней «ссылке по теме» в одном из пунктов COR как раз сравнивается с AOP
      • 0
        Мне этот пункт показался каким-то неубедительным…
        • 0
          Дело даже не в том, что я там против АОП по религиозным соображениям или еще почему-то. Я уже слышал эту аббревиатуру и честно пытался понять, что же это такое, и не может ли оно мне пригодиться. Статья сделала смысл технологии чуть более понятным, последняя ссылка — еще сильнее разъяснила, но я пока склонен верить части перечисленных по этой ссылке мифов. Возможно, дело в языке программирования. Я достаточно давно занимаюсь разработкой на perl и знаю, какими комбинациями ООП-приемов, паттернов проектирования и средств языка действовать, чтобы быстро и прозрачно реализовать нужный функционал, включая декораторы. Те же атрибуты функций позволяют легко добиться эффекта, сильно похожего на приведенный в статье пример из java. Возможно, если бы я выбрал более строгий язык, АОП был бы актуален.
          Или я все-таки чего-то не понимаю? Буду рад, если кто-то сумеет разъяснить.
    • +2
      А так же есть signal-slot, event-driven programming, но все они решают одну проблему — устранение дублирования и зависимостей при реализации сквозной функциональности.

      Пример из жизни: когда вы заходите в магазин, что бы купить продуктов, вы заходите и покупаете продукты, не обращая внимания на охрану и камеры видео-наблюдения. Это все потому, что охрана (security) и камеры (log) — это сквозная функциональность. Вы в этом случае — объект наблюдения, на действия которого «подписываются» другие заинтересованные объекты.

      Есть проблема понимания того, какая функциональность для в каждом конкретном случае (домене) является сквозной, а какая — специфичной.
    • 0
      В ООП есть много замечательных паттернов, которые по-сути обозначают одно и тоже :)
  • 0
    Спасибо за просвещение! Давно в голове крутилась мысль, что подобная парадигма просто обязана существовать, т.к. бизнес логика рано или поздно просто тонет в «обвязке» из служебного кода.

    правда, что-то для Qt ничего ничего подобного не найду
    • 0
      Хм, привязка множества слотов к одному сигналу и возможность генерировать свои сигналы в слоте разве не что-то подобное?
      • 0
        сигналы-слоты не упростят ту же задачу логирования, например. Или я чего-то не понимаю :)
        • 0
          Ну, вместо
          @Loggable
          public void method(int arg)
          {
            doSomething();
          }

          надо будет написать
          public void method(int arg)
          {
            emit enterMethod(arg);
            doSomething();
            emit leaveMethod(arg);
          }

          А уж на enter/leave можем повесить и несколько видов логирования (или не вешать), и проверку прав.

          Не так красиво, но управление кодом, имхо, упрощает даже по сравнению с традиционным DI (одна и только одна реализация интерфейса). Сложность вызывает использование чего-то (или ничего) вместо вызова метода или оборачивание его в конструкции типа try… catch, но вот перед и после нет проблем.
          • 0
            вместо emit точно также можно просто вызывать обычные методы, содержащие пересекающую логику. сигналы конечно гибче, но принципиально разницы нет.

            Я вот думаю, нельзя ли как-то в шаблон или макрос эти эмиты завернуть?
  • +3
    >… судя по сравнениям с сайта PostSharp, лидирующую позицию занимает именно он.

    Фраза напомнила об одном небезизвестном браузере, который тоже по сравнениям со своего сайта лучший в мире :) (Ничего личного, просто позабавила формулировка)
  • 0
    Я только начал изучать Spring, но у меня сложилось устойчивое мнение, что аспектами лучше добавлять функциональность, чем делать декораторы. Декораторы естественнее делать в виде аннотаций. А например в Spring Roo аспекты используются для добавления функционала в сущности или скафолда контроллеров. Например, реализация таких методов как геттеров/сеттеров, toString() и пр.
    • 0
      А можете привести пример с сеттерами-геттерами? Плз…
      • +2
        Spring Roo сам генерирует геттеры/сеттеры для сущностей, помеченных аннотацией @RooJavaBean. Cущность выглядит примерно так:
        @RooJavaBean //
        @RooToString
        @RooEntity
        public class PackageName {

        @NotNull
        @Column(unique = true)
        private String name;
        }


        Для нее он генерирует аспект типа такого:
        privileged aspect PackageName_Roo_JavaBean {

        public String PackageName.getName() {
        return this.name;
        }

        public void PackageName.setName(String name) {
        this.name = name;
        }

        }


        Красиво и понятно, без всякой магии :)
        • 0
          Благодарю, хороший пример. Похоже, АОП полностью открывается именно в нединамических языках. В динамических, как правило, есть возможность добиться того же самого привычными встроенными средствами.
  • +2
    Назвать статью понятной — сложно. Даются общие определения, какие-то примеры, но суть куда-то ускользает.
    Все это ИМХО, конечно. Я бы с целью ознакомления аудитории сделал иначе — привел бы примеры на псевдокоде
    и на этих примерах объяснил бы, где тут срезы, где советы и т.д.
  • 0
    Вот интересно, для платформы .NET каких-нибудь IOC-контейнеров — два десятка, а АОР фреймворков — кот наплакал
    • 0
      Если нужно только то, что описано в статье, то это делается через IoC-контейнеры. Например в unity такая фича есть, смотри здесь: msdn.microsoft.com/en-us/library/ff647107.aspx
  • +3
    Мне всегда интересно было, почему ООП выделяют в отдельную парадигму. Я лично не знаю ни одного языка, где был бы ООП без какой-либо другой парадигмы, например императивной или функциональной.
    • +1
      Скорее это разные измерения: динамический-статический, комплируемый — интерпретируемый, императивный-декларативный, ООП — не ООП :)
      • 0
        вот с такой терминологией я согласен, просто все это называют одним словом — парадигмой, как бы подразумевая что это единая характеристика.
        • 0
          С вики:
          Термин «парадигма программирования» впервые применил Роберт Флойд в своей лекции[2] лауреата премии Тьюринга.

          Флойд отмечает, что в программировании можно наблюдать явление, подобное парадигмам Куна, но, в отличие от них, парадигмы программирования не являются взаимоисключающими


          ru.wikipedia.org/wiki/Парадигма_программирования
          • 0
            Да, я это читал. У меня другая мысль: некоторые парадигмы являются самостоятельными (есть языки, которые основаны только на этой парадигме), а некоторые нет (то есть могут допольнять другую парадигму)
  • +2
    Не очень понял, но это то же самое, что декораторы в Python?
    • +2
      По сути — да.
      Если говорить формально, то AOP — это парадигма, а декораторы — это синтаксис. И в python AOP удобнее всего реализуется при помощи декораторов.
  • 0
    А различные хуки, middleware и т. п. к АОП имеют отношение или это нето совсем другое? Цели-то вроде бы те же.
  • +2
    Не могу сказать что являюсь противником или последователем данной технологии. У всего есть свои плюсы и минусы. Но недолюбливаю AOP за его «скрытность». Когда читаешь метод, должно быть понятно что он делает. И довольно неприятно каждый раз, натыкаясь на аннотацию AOP вспоминать что этот метод может НЕЯВНО (в коде то в методе этого НЕТ) делать ещё что-то. И это скажите спасибо если метод аннотирован, а если используется любимое spring-way-xml-programming, то всё — тушите свет. Что на самом деле делает код, можно понять только после поллитры. =\

    PS: Мнение рождено не на пустом месте. Пришлось пару лет поработать на проекте, где кэшинг (и ещё что-то, уже не помню что) активно прикручивали на Аспектах. Не могу сказать чтобы код блистал понятностью своей.

    PPS: А за статью спасибо, конечно, она довольно понятно всё объясняет. Только вот определения Аспекта и Совета выглядят (для меня по крайней мере) весьма размыто.
    • 0
      Ну, вообще говоря, «вспоминать что этот метод может НЕЯВНО (в коде то в методе этого НЕТ) делать ещё что-то» приходится постоянно, если метод использует хотя бы стандартную библиотеку, а не является «вещью в себе». Хотя я сталкивался только с аннотациями, xml описания у меня как-то не прижились в php :)
      • 0
        Либо я Вас не понял, либо наоборот.

        Когда метод что-то использует, он делает это ЯВНО, вызывая что-то ЯВНО. (Ну или как там правильно говорить, посылая сообщения ;)). В случае же с AOP в методе НЕТ явного обращения к кэшу, или логгирования (ну или ещё какой хитрой логики). Оно происходит «за сценой», скрыто от вас при прочтении. Это, намой взгляд, ужасный недостаток. ДАЖЕ с аннотациями (которые какбэ намекают, ага).

        Ну а насчёт xml… Это как раз spring-way (по крайней мере был раньше). Автор в статье как раз про него и говорит в конце…
        • 0
          Ну вызывает явно, но что вызывает мы можем посмотреть, только открыв вызываемый код, в этом отношении не вижу разницы между:
          @Loggable
          public void method() {...}

          и
          public void method() {Log.writeForMethod; ...}

          С одной стороны ужасный, с другой сосредотачивает внимание на основной задаче метода.

          Активно используемый мною php-фреймворк позволяет использовать для конфигурации, например, DI три способа YAML, XML и нативный php-код, предпочитаю первый и последний, XML как-то уж слишком многословен, имхо, за деревьями не видно леса.
          • 0
            Очень печально, что Вы не видите разницу между явно написанным кодом и неявно.

            PS: Ещё один момент: нормальное IDE (вроде Eclips'а), для джавы, даст вам в один щелчок перейти к телу «writeForMethod», а вот определение @Loggable хоть и откроется, но читабельность его будет ниже (пойди ещё разбери чего там в классе нафигачили).

            В общем, если вам нравится — пользуйте, конечно, но крайне рекомендую ознакомиться и с точкой зрения противников метода. Вы правильно говорите, иногда за деревьями не видно леса. Вот AOP зачастую бывает теми деревьями. Как правило всегда можно проще и читабельнее…
            • 0
              Да я только теоретизирую, на практике использовать АОП в обозримом будущем вряд ли будет возможность.
    • 0
      а как это себя ведет при рефакторинге кода?

      насколько я понял аспекты привязываются к методам по именам. что происходит, если имя метода меняется — авторизация перестает проверятся, логи — записываться? есть-ли для таких случаев поддержка со стороны среды разработки, предупреждения какие-то?
      • 0
        Хороший вопрос. Я с рефакторингом этих методов не сталкивался. Надо будет посмотреть :)

        В целом, если построено на аннотациях, должно по идее работать нормально.
  • 0
    Полезно и другие мнения узнать, наверное.
  • 0
    Скажите, а обязательно point-cut задавать как аннотацию к методу? Ведь, насколько я понял, роль метода заключается лишь в том, что он задает имя point-cut, которое, очевидно, можно было бы задавать и в самой аннотации.
    • +1
      Нет, совершенно не обязательно. В случае с Spring AOP срез можно определить как в описании совета (например, в аннотации @Around), так и в XML-конфиге.
      • 0
        Ну насчет XML-конфига я и не сомневался (Java такая Java :) ), а насчет advice это интересно. Спасибо!
      • 0
        Я так понимаю, в примере вы выбрали этот способ, чтобы явно показать разделение между point-cut и advice? Но это немного путает: непонятно, что методы webserviceMethod и loggableMethod ничего не выполняют, а по сути являются метаданными.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.