Новичку полезно

  • А знаете ли вы что?

    - rudvs - ваш пояс шахида!


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

Теги: Аспектно-Ориентированное, программирование, java
источник

Вступление

Коротко говоря, Аспектно-Ориентированное программирование является относительно новой методологией, которая обеспечивает разделение между собой так называемых «crosscutting concerns» путем введение нового понятия модуляризации – аспект (aspect) , который пересекается с другими модулями. Самое главное, с помощью АОП вы отделяете эти «пересекающиеся отношения» из главных модулей в аспекты. В конечном итоге, происходит так называемый процесс «сплетения сущностей» который в итоге выдает нам готовую систему в виде классов или JAR библиотеки. Примерную схему процесса можно изобразить в виде двух треугольных призм, которые выполняют так называемые «преломления», разделяя и собирая вместе части системы, обеспечивая тем самым их независимость.

Почему это вдруг так выгодно?

Приведу простой и довольно часто встречающийся на практике (в разных своих вариациях) пример. Допустим, есть какой-либо класс BusinessClass c одной из операцией, к примеру, operation (Object). Как это выглядит в большинстве случаев:

public class BusinessObject {
//Некоторые глобальные переменные
//Объект лога
//Другие методы
 
public void operation(Object someData) {
//Проверить на наличие авторизации
//Блокировка для потоковой безопасности
//Записать в лог начало операции
run(someData);
//Записать конец операции
//Разблокировать объект
}
 
}

Знакомо? Думаю большинству из нас приходилось писать или сталкиваться с подобными реализациями. Как решает эту проблему АОП? Решение весьма простое.

Как видно из кода, метод operation(Object) выполняет гораздо больше того, что он должен выполнять. Но так как нам необходима потоковая безопасность, система логов, система авторизации и т.п. нам необходимо выполнять перед началом операции и эти действия. АОП предлагает достаточно элегантное решение – мы разбиваем все эти операции на аспекты: к примеру, LogAspect, ThreadSafetyAspect, AuthorisationAspect и, используя стандартный механизм связывания, компилируем. В итоге получается следующий код:

public class BusinessObject {
//Некоторые глобальные переменные
//Другие методы
 
public void operation(Object someData) {
run(someData);
}
 
}

И больше нет смешения логики в коде – мы видим, что операция выполняет то, что должна выполнять.

Пример: логирование в AspectJ

Так как моей целью не было подробного технического описания тонкостей синтаксиса и прочего, связанного с AspectJ, я приведу просто пример использования в работе с логами.

Допустим, у нас имеются следующие классы:

public class Person {
private String name;
private Role role;
 
public String getName() {
return name;
}
 
public void setName(String name) {
this.name = name;
}
 
public Role getRole() {
return role;
}
 
public void setName(Role role) {
this.role = role;
}
 
}

И, соответственно:

public class Role {
private String title;
 
public String getTitle() {
return title;
}
 
public void setTitle(String title) {
this.title = title;
}
 
}

Теперь, представим, что в лог должны заноситься все операции получения/изменения данных. Еще раз напомню, что это крайне простой пример, и методы могут называться по другому, и выполнять другие виды операций, но они в любом случае также должны будут заноситься в лог. И так, после добавление операций, связанных с логированием, получилось нечто вроде такого:

public class Person {
private String name;
private Role role;
static Logger _logger = Logger.getLogger("trace");
 
public String getName() {
_logger.logp(Level.INFO, "Person", "getName", "Entering");
return name;
}
 
public void setName(String name) {
_logger.logp(Level.INFO, "Person", "setName", "Entering");
this.name = name;
}
 
public Role getRole() {
_logger.logp(Level.INFO, "Person", "getRole", "Entering");
return role;
}
 
public void setName(Role role) {
_logger.logp(Level.INFO, "Person", "setRole", "Entering");
this.role = role;
}
 
}

А также второй класс:

public class Role {
private String title;
static Logger _logger = Logger.getLogger("trace");
 
public String getTitle() {
_logger.logp(Level.INFO, "Role", "getTitle", "Entering");
return title;
}
 
public void setTitle(String title) {
_logger.logp(Level.INFO, "Role", "setTitle", "Entering");
this.title = title;
}
 
}

Теперь все заносится в лог, но код при этом сильно пострадал. Приметно, что операции делают больше , чем они должны делать исходя из их имени. Тут нам на помощь и приходит АОП.

Используя AspectJ, создадим новый файл LogAspect.aj со следующим содержанием:

public aspect TraceAspect {
    private Logger _logger = Logger.getLogger("trace"); //Объявляем объект для работы с логами
 
    pointcut traceMethods()
        : execution(* *.*(..)) && !within(TraceAspect); //Все методы всех функций в системе любых типов
 
    //Выполняется перед каждым запуском методов, определенных ранее в traceMethods
    before() : traceMethods() {
        Signature sig = thisJoinPointStaticPart.getSignature();
        _logger.logp(Level.INFO, sig.getDeclaringType().getName(),
        sig.getName(), "Entering");
    }
 
}

Исключая подобное объяснение особенностей синтаксиса этого файла, можно сказать одно – теперь все логирование системы сосредоточенно в одном месте! Теперь наши классы не будут засорены излишними подробностями. Все необходимые изменения будут происходить во время «сплетения» данного аспекта с необходимыми классами. Для глубокого знакомства со всеми подробностями данной системы рекомендую почитать книгу Manning: AspectJ in Action.

Спасибо за внимание!

Добавить комментарий


Обновить