вівторок, 8 квітня 2008 р.

Аспектно-Ориентированное Программирование: Предисловие

про АОП я узнал два года назад, когда я открыл для себя Spring всплыла тема АОП. но тогда у меня не было времени разбиратся с этой концепцией, тем более что она показалась мне довольно сложной и не относящейся к тому, что я тогда делал. коллеги в соседнем проекте вроде бы как использовали аспекты, но я так и не был привлечен в разработку этого проекта. а следовательно АОП осталась очередной технологией в стопке "посмотрю, когда будет время".
интерес мой проснулся спонтанно, Виктор Ронин выложил пост про локализацию ошибок. я большой поклонник использования подробного логирования программы для этих целей. однако, код логов действительно "засоряет" код программы и возможность отделит одно от другого была бы очень кстати. и тут я вспомнил про АОП.
действительно, логирование или трассировка программы -- это одна из классических задач АОП. но часто кажется, что одних средств АОП тут недостаточно и все-равно необходимо использовать логи внутри функционала, а раз так, то зачем писать что-то еще? пусть все логи будут внтури, раз нельзя их полностью отделить. на самом деле это не так. рабочую систему логирования я опишу позже, когда создам и оттестирую таковую :) пока поговорим о практических принципах применения АОП в среде Java + Spring. в документации по Spring очень много написано про АОП, фактически есть два способа реализации аспектов --с помощью Spring AOP и через AspectJ. я пока пользуюсь AspectJ аспектами, но возможно посмотрю и реализацию Spring.
итак, первое что необходимо понимать при аспектном программировании -- мы работаем с вызовами функциональности. т.е. елементарная операция для аспекта -- это вызов. мы можем описать для вызова пред- и пост-условия, обернуть выполнение метода какой-то функциональностью, котролировать метод после выброса исключения или после точки возврата. мы не можем обратиться к функционалу метода напрямую, но любой метод нашего класса есть на 90% набор вызовов других методов, а эти методы в свою очредь доступны через АОП. небольшая иллюстрация в коде, чтобы было еще понятнее:
предположим нам необходимо заполнить некторую коллекцию, пусть реализацию java.util.Map внутри класса:

package org.corwin.samples;

public class AopSample {
    private Map container = new HashMap();

    public void fillMap() {
        container.put("sample1", "something");
        container.put("sample2", "another thing");
    }
}

теперь опишем аспект, который будет контролировать AopSample класс:

package org.corwin.samples.aspects;

public class SampleAspect {
    public void beforeFillMap() {
        System.out.println("Call before Map fill with values");
    }

    public void afterFillMap() {
        System.out.println("Call after Map fill with values");
    }
}
чтобы связать аспект с классом, который он должен контролировать в Spring есть два способа -- аннотации AspectJ и конфигруационный файл Spring. через аннотации у меня, откровенно сказать, пока не получилось, поэтому опишем конфигурационны файл.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
">
    <bean id="sampleClass" class="org.corwin.samples.AopSample"/>
    <bean id="sampleAspect" class="org.corwin.samples.aspects.SampleAspect"/>
    <aop:config>
        <aop:aspect ref="sampleAspect">
            <aop:pointcut id="mapPutCut" expression="execution(* java.util.Map.put(..))"/>
            <aop:before method="beforeFillMap" pointcut-ref="mapPutCut"/>
            <aop:after method="afterFillMap" pointcut-ref="mapPutCut"/>
        </aop:aspect>
    </aop:config>
</beans>
таким образом, после сборки проекта, у нас появится возможность не добавляя напрямую код в SampleClass воводить некую информацию до и после вызова метода put. точно также можно описывать изменение состояний любого клсса, а это очень похоже на задачу, которая ставиться перед системой трассировочного логирования программы.