(фото из статьи на lurkmore про евроремонт)
Вероятно, статья не будет интересна тем кто часто использует в своей работе AOP и понимает что это такое. В комментариях приветствуются как конструктивные замечания так и веселый холивар!
Disclaimer: Я не теоретик, поэтому рассказанное в статье субъективно, пропущено через призму опыта.
Зачем все эти сложности, другой подход к проектированию систем, когда есть объектно-ориентированное программирование? Новый синтаксис, какие-то аспекты, срезы(pointcut) — ведь все в итоге превращается в те же инструкции выполняемые jvm.
Вопрос в удобстве разработки, легкости модификации, тестирования и стоимости поддержки системы. Аспекты — это дополнение к ООП программам, другая парадигма при разработке и проектировании программ, не противоречащая использованию объектной модели в приложении. Аналогия, которая сразу приходит на ум — анализ звуковых данных. В временной области с исходным сигналом сложно выполнить обработку и анализ. Но при переводе в частотное представление легко отфильтровать шумы определенной частоты, выделить ноты в мелодии, усилить звук голоса и т.п. Так же и аспекты позволяют легко работать со многими задачами в другом представлении программы и выделить сквозную функциональность в виде аспекта. При умелом применении AOP помогает «распутать» «запутанный» код программы.
Возникает возможность убрать инструкции ведения журнала операций(логгинга) и обработки системных ошибок из бизнес-логики приложения, уменьшить количество бойлерплейт кода, проверять права доступа пользователя при обращении к определенным методам или полям класса, декларативно управлять транзакциями базы данных, выполнять свое инструментирующее профилирование интересующего вас участка кода, в котором учитывается не только факт вызова и время выполнения операции но и некоторый контекст, кеширование «тяжелых» операций бизнес логики если у них нет побочного эффекта, поиск мест программы в которых конструирование объектов определенного типа. Этим не ограничивается список того, для чего может быть удобно AOP. Например, в проекте на работе, я применяю аспекты для тестирования распределенного приложения, сбора метрик в нем, имитации ошибок базы данных и таймаутов внутри jdbc драйвера oracle и сетевых взаимодействий.
Начнем с понятия что такое advice в AspectJ — это то как будет применяться аспект в срезе кода(pointcut): перед срезом (BEFORE), после (AFTER), после успешного возврата (AFTER RETURNING), в случае ошибки в точке среза (AFTER THROWING), или полный контроль над ситуацией (AROUND) где самостоятельно надо вызывать исходный код в срезе, передавать параметры и обрабатывать ошибки выполнения в точке среза.
Pointcut или срез — это описание того, где мы будем внедрять в исходную программу аспект. Синтаксис достаточно богатый и позволяет описать сложные правила, определяющие точки среза. Например, конструирование объекта, блоки статической инициализации, вызов конструктора, доступ к полям объекта на чтение/запись, конструирование объекта, блок catch, методы с какой-либо аннотацией, вызов метода с параметрами определенного типа, имя метода по маске и т.п. + логические операции в синтаксисе pointcut. Конечно, волшебства в программировании нет, что добавляет ограничения на то что инлайнится в байт коде и не доступно в pointcut. Так же не все мыслимые точки среза можно указать декларативно, что иногда требует анализа контекста вызова в коде реализации аспекта.
Параметр точки соединения (join point) в аспекте позволяет получить значения аргументов, узнать место где фактически произошел вызов аспекта в срезе, получить this объекта и т.п. В случае с AROUND advice тут же можно выполнить фактический вызов, изменить его параметры и получить возращаемое значение или обработать ошибку.
Все то, что на первый взгляд кажется магией, в AspectJ либо реализуется плагином во время сборки проекта (weaving to bytecode), либо java агентом с помощью модификации байт кода программы во время загрузки классов(load-time weaving). AspectJ — зрелый AOP фреймворк с огромным комьюнити, множеством публикаций про него, хорошей документацией, достаточно стабильный, интегрированный в разнообразные системы сборки, интеграция в Spring, с хорошей поддержкой в IDE.
В aspectj-scripting (расширении AspectJ java агента) есть возможность для аспектов во время выполнения загружать классы из maven репозитария, считывать конфигурацию агента не только из файла и classpath, но и с http сервера. Это может сильно помочь в тестировании, профилировании и модификации распределенного java приложения. Код доступен на github, а агент в центральном репозитарии. В прошлой публикации использовал эту библиотеку для модификации поведения maven plugin Про другие примеры использования аспектно-ориентированного программирования в практике, расскажу в следующих статьях.
Как вишенка на торте украшает его и делает более вкусным, так же и аспектно-ориентированное программирование в объектно-ориентированной программе упрощает разработку, модификацию и тестирование. Важно не увлекаться применением AOP в проекте — чтобы не получилась кастрюля вишни с ложкой крема сверху!
Комментарии (12)
igor_suhorukov Автор
03.04.2015 13:48+1Какие примеры были бы интересны? В следующих публикациях постараюсь учесть
TimReset
03.04.2015 14:24+1Проблема, с который мы недавно столкнулись и которую пробовал решить с помощью аспектов — логгирование CallableStatement. Т.е. стандартный код:
CallableStatement c = con.createCallableStatement("{call package.procedure(?,?,?,?)}"); c.setString(1,"a"); c.setInteger(2,2); c.setStruct(3,struct); c.execute();
И я пробовал с помощью аспектов получить {call package.procedure(?,?,?,?)} и потом отследить вызов set методов с логгированием переданных параметров и при вызове execute() записывать эти параметры в лог.
В итоге не осилил — при включении аспектов на нашем проекте компиляция стала занимать какое-то нереальное время. И пошёл по другому пути — сделал wrapper вокруг connection, а он возвращает wrapper вокруг CallableStatement который уже производит логгирование. Кода получилось очень мало — большая часть кода wrapper была сгенерирована IDEA, а я в итоге написал сохранение параметров в set методах и вывод их в лог при выполнении execute().
Но мне бы было интересно, если бы Вы показали на этом примере, как можно применить аспекты.
Успешный пример их применения у меня был на гораздо меньшем проекте — там нужно было логировать просто время выполнения определённых методов (проект был по нагрузочному тестированию). И там было всё гораздо проще — написал pointcut для поиска этих методов, сохранял текущее время, вызывал метод, писал разницу между текущим временем и сохранённым.
Но в примере с CallableStatement по другому — нужно не просто подменить вызов метода своим, а ещё и отследить что вызывались set методы у объекта и отследить вызов exceute().
P.S. Спасибо за статьи по аспектам!igor_suhorukov Автор
03.04.2015 14:30+2Отличная задача)
Такое решал с помощью ThreadLocal. Напишу пример
afiskon
03.04.2015 14:43Мы сейчас пишем примерно так (язык Scala):
class SomeClass { def someMethod = measure("SomeClass.someMethod") { // ... } }
А хотелось бы:
class SomeClass { def someMethod = measure { // ... } }
И чтобы в метод measure передавалось имя класса и имя метода. Пробовали извлекать их из стэктрейсов, но при нагрузочном тестировании из-за этого возникают постоянные блокировки, производительность просаживается. Было бы интересно посмотреть, как решить проблему с помощью AOP.igor_suhorukov Автор
03.04.2015 15:06Со Scala я года 3 как не сталкивался. Если бы вы сделали простой тест и проект дня него на SBT/maven, то я бы попробовал бы решить эту задачу
afiskon
03.04.2015 14:40+1AspectJ — это очень хорошо, используем в проекте для сборка метрик и трассировки в распределенном приложении (через библиотеку Kamon). Не представляю, как бы жили без него.
С нетерпением жду новых постов, посвященных AOP!igor_suhorukov Автор
03.04.2015 15:09Смотрел на kamon — интересный проект. Из разработчиков видел комиты наших соотечественников. В моем проекте на гитхабе есть зависимость на их артефакт — перепакованный sigar с нативными библиотеками внутри
zencd
Как-то неожиданно оборвалось повествование
igor_suhorukov Автор
В 3 ночи захотелось спать) К тому же я не претендую на монографию. Примеры будут в следующих статьях
igor_suhorukov Автор
Что по, вашему мнению, стоило бы рассказать еще про основы?
isden
Суть то понятна, хотелось бы увидеть примеры решения задач с сабжем и без (т.е. чтобы оценить профит). Я «краем глаза» уже сталкивался с AspectJ в одном проекте, но заметного профита заметить не удалось.
igor_suhorukov Автор
Я вас понял. Есть задачи по тестированию, которые без АОП сделать можно только самостоятельно модифицируя байткод приложения или написав кодогенератор для исходников. осмотрите статью по ссылке. По-моему показательно как можно добраться в потроха плагина