Я не буду описывать суть и принципы АОП, а опишу только те проблемы, с которыми я столкнулся, и решения которых заняло много времени.
У меня было в распоряжении Spring, WebLogic, google.com и проект, куда я хотел внедрить АОП логирование. Скажу сразу, до этого я никогда не работал с АОП.
Проблема № 1
Spring AOP – использует proxy-based подход.
Если у нас есть класс (СlassA) с методами (methodA, methodB), при этом methodB() вызывает methodA() и аспект (допустим after) который должен выполняться при вызове methodA():
public class ClassA { public void methodA() { System.out.println("methodA"); } public void methodB() { System.out.println("methodB"); methodA(); } } public class AspectClass { public void aspectMethodA() { System.out.println("Aspect on method A"); } }
И некий класс который в рамках какой-то логики делает вызов этих методов:
public void execute() { // ..... classA.methodA(); classA.methodB(); // ..... }
Результат такого вызова (используя стандартный Spring AOP) будет:
methodA Aspect on method A methodB methodA
И все, второй раз аспект не сработает. В документации хорошо описан принцип работы Spring-AOP, прочитав его, все встает на свои места. Это отправная точка.
Проблема № 2
Методы должны быть public. Тут без комментариев.
Так вот, почитав документацию и другую познавательную литературу я нашел следующее решение:
- Load-time weaving (LTW).
- Compile-time weaving (CTW).
Поскольку я нашел хорошую документацию по LTW, я решил использовать именно его. Цена вопроса:
- Теперь у нас нет одного .xml файла, куда мы красиво складываем наши pointcut-ы, aspect-ы.
- Нужно добавить новый aop.xml, где мы должны указать наши weaver-ы (классы которые непосредственно учувствуют в процессе), aspects-ы.
- Pointcut-ы тепер указываются непосредственно над aspect-ами.
@Before( "execution(* com.solutions. web.test.WebTestClass.testA())") public void testALog() {}
- Над классами аспектов появляется аннотация
@Aspect
. - Нужно добавить аргумент при запуске JM/WebLogic:
-javaagent:${PATH_TO_LIB }/aspectjweaver.jar
Примечание
Если посмотреть на пример приведенный в документации (aop.context):
<weaver> <include within="foo.*"/> </weaver> <aspects> <aspect name="foo.ProfilingAspect"/> </aspects>
Да все работает, но одно НО — мы редко будем хранить наш выполняющий код и непосредственно код аспектов в одном классе/пакете. Эту маленькую деталь они упустили в описании. Так вот, если у нас есть класс (ClassA) и аспект (AspectA) которые находятся в разных пакетах, то валидной конфигурацией будет следующий aop.xml:
<weaver> <include within="com.example.ClassA"/> <!-- путь к конкретному классу --> <include within="com.log.* "/> <!—путь к пакету с аспектами> </weaver> <aspects> <aspect name="com.log.AspectA"/> </aspects>
В теге <weaver>
следует указать все классы к которым будут применены аспекты + пакет со всеми аспектами.
Проблема № 3
LTW нельзя применить на EAR/APP уровне.
«As Costin said, there is unfortunately nothing we can do about this. Load-time weaving only works for specific deployment units such as WARs, and even there it is considered an advanced feature that won’t work in all runtime environments.”
Конкретно этот комментарий я искал очень долго.
Решением этой проблемы как вы догадались, и является использование CTW. Цена вопроса:
- Больше нет хоть какого-то конфигурационного файла, где мы можем посмотреть все наши aspect-ы и pointcut-ы.
- Cкладывая АОП систему логирования в один пакет можно найти все pointcut-ы, но все равно это неудобно и занимает много времени.
- Нужно использовать ajc-компайлер, соответственно подключать его к сборщикам проекта (ant, maven, gradle…).
Проблема № 4
CTW+LTW не совместимые технологии.
Может мне просто не повезло, но по не известным мне причинам LTW сканировало весь classpath и при вызове классов скомпилированных при помощи СTW падало с ошибкой:
java.lang.Exception: java.lang.NoSuchMethodError: com.aop.example.log.AspectA.aspectOf()Lcom/aop/example/log/AspectA;
Проблема сразу же пропадает после отключения LTW.
ИТОГО
Что я для себя вынес и хотел бы добавить:
- Для всех public методов верхнего уровня (EAR/APP, WEB уровень) можно использовать Spring AOP.
- Для всего WEB уровня не public и методов не верхнего уровня можно использовать LTW (если СTW не используется).
- Для всего APP уровня не public и методов не верхнего уровня можно использовать CTW (если LTW не используется).
- В теге
<weaver>
файла app.context нужно указывать как сами “weav” классы так и aspect-ы. - CTW и LTW не совместимые технологии.
В конечном итоге для приложения, имеющего WEB и APP уровни, получаем проект с Spring AOP + CTW технологиями.
От красивой конфигурации в одном .xml файле не осталось и следа. Из-за специфики проекта, объяснять все это заказчику я не решился и оставил эту затею для следующего проекта.
Проект
Скачать проект-пример можно из github-a. Проект на maven-e.
Класс Executor является spring-bean-ом, получив его экземпляр, выполните метод execute().
Если вы все правильно завели, то в результате должны получить следующие сообщения:
--methodA-- method Aspect Before for methodCTW --methodCTW-- method Aspect After for method A --webMethodA-- method Web Aspect Before for MethodCTW --webMethodForCTW-- method Web Aspect After web method A
Литература
Список литературы:
Cсылки на статьи из Хабра:
ссылка на оригинал статьи http://habrahabr.ru/post/186940/
Добавить комментарий