Аспектно-ориентированное программирование. Основы
Продолжим тему, начатую в прошлой слесарно-программистской статье про AspectJ и open source расширение для этой библиотеки aspectj-scripting. В этой заметке рассмотрим какие задачи решает аспектно-ориентированное программирование (AOP) на примере и синтаксисе самой известной библиотеки среди адептов этой методологии. AspectJ — дитя Xerox PARC, теперь совершеннолетнее и живущее в Eclipse Foundation.(фото из статьи на 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 в проекте — чтобы не получилась кастрюля вишни с ложкой крема сверху!