AOP in action. AspectJ (CTW) + Spring + LTW

Решил внедрить АОП логирование на проект и не внедрил. Как и почему, собственно и хочу поделиться.

Я не буду описывать суть и принципы АОП, а опишу только те проблемы, с которыми я столкнулся, и решения которых заняло много времени.
У меня было в распоряжении 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, я решил использовать именно его. Цена вопроса:
  1. Теперь у нас нет одного .xml файла, куда мы красиво складываем наши pointcut-ы, aspect-ы.
  2. Нужно добавить новый aop.xml, где мы должны указать наши weaver-ы (классы которые непосредственно учувствуют в процессе), aspects-ы.
  3. Pointcut-ы тепер указываются непосредственно над aspect-ами.
    @Before( "execution(*  com.solutions. web.test.WebTestClass.testA())")
        public void testALog() {}
    
  4. Над классами аспектов появляется аннотация @Aspect.
  5. Нужно добавить аргумент при запуске 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>

В теге
 следует указать все классы к которым будут применены аспекты + пакет со всеми аспектами.

Проблема № 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. Цена вопроса:
  1. Больше нет хоть какого-то конфигурационного файла, где мы можем посмотреть все наши aspect-ы и pointcut-ы.
  2. Cкладывая АОП систему логирования в один пакет можно найти все pointcut-ы, но все равно это неудобно и занимает много времени.
  3. Нужно использовать 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.

ИТОГО


Что я для себя вынес и хотел бы добавить:
  1. Для всех public методов верхнего уровня (EAR/APP, WEB уровень) можно использовать Spring AOP.
  2. Для всего WEB уровня не public и методов не верхнего уровня можно использовать LTW (если СTW не используется).
  3. Для всего APP уровня не public и методов не верхнего уровня можно использовать CTW (если LTW не используется).
  4. В теге файла app.context нужно указывать как сами “weav” классы так и aspect-ы.
    CTW и LTW не совместимые технологии.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 8

    0
    А вы смотрели на AOP логирование с точки зрения производительности? Не уверен, что на большом проекте это будет целесообразно. Если у кого-то был опыт внедрения подхода, то интересно было бы посмотреть.
      +1
      От способа weaving зависит. Тут можно почитать небольшое исследование на эту тему.
        0
        Только хотел привести эту же статью. Там конечно недостаточный анализ, но ничего лучшего я не находил и сам не проверял.
          0
          Конечно, понятно, что есть зависимость от weaving, спасибо за статью, почитаю. Просто хотелось посмотреть на реальные примеры внедрения и услышать отзывы хабравчан по этому вопросу.
            +2
            Ну у меня на проекте мы используем аспекты для логгирования. Специально бенчмарков не делали, но проблем с производительностью из-за логов не замечено. LTW, если что.
      0
      использую ejb3 interceptors, удобно, явных тормозов не видно, замеры пока делать нехочется
        0
        Небольшое уточнение про LTW для EAR — насколько я понимаю, в указанной выше ссылке речь идет именно зависимостях, т.е. сторонних jar. К сожалению, очень давно не работал с LTW, но примерно два года назад удавалось завести LTW на WebLogic 10.3, если не ошибаюсь, с aspectj версии 1.6.1 именно в варианте с EAR.

        Присутствие LTW заметно на старте приложения, когда происходит weaving, который может отрабатывать ощутимо долго (в зависимости от количества классов, которые обрабатываются). По поводу быстродействия в рантайме, к сожалению, прокомметнировать не могу, но поддержу комментарий выше — просадки по производительности не замечал.

        Да, и еще одна тонкость — с LTW будут проблемы в случае использования нескольких classloader'ов, конечно, такая ситуация довольно редка, но тем не менее. В свое время я так и не смог подружить LTW с несколькими classloader'ами. Пример кейса — доводилось править проект, в котором в рантайме компилировались Java-классы и подгружались в classloader. Компилируемые классы так и не удалось «обвить» через LTW.
          0
          Спасибо за комментарий!

        Only users with full accounts can post comments. Log in, please.