Аспектно-ориентированное программирование: различия между версиями

Содержимое удалено Содержимое добавлено
Строка 18:
Чтобы лучше почувствовать особенности аспектно-ориентированного программирования, рассмотрим [[w:AspectJ]] — аспектно-ориентированное расширение [[w:Java|Java]], предложенное [[w:Xerox PARC|Xerox PARC]]. В качестве примера рассмотрим фрагмент кода, предназначенный для регистрации событий в log-файле, реализованный на AspectJ. Этот пример взят из системы с открытым кодом Cactus, упрощающей тестирование Java-компонентов на стороне сервера. Каркас Cactus разработан для поддержки процесса отладки с помощью трассировки вызовов всех методов. Версия 1.2 Cactus была написана без [[w:AspectJ|AspectJ]]. Поэтому большинство методов выглядели, как показано ниже.
 
<source lang="java">
<pre>public void doGet(JspImplicitObjects theObjects) throws ServletException {
{
logger.entry("doGet(...)");
JspTestController controller = new JspTestController();
controller.handleRequest(theObjects);
logger.exit("doGet");
}
}</pre>
</source>
 
Каждому разработчику, в рамках соглашений по созданию проектов, предлагалось включать log-вызовы в начало и в конец каждого метода. Кроме того, рекомендовалось заносить в log значения параметров каждого метода. Следование этим соглашениям требовало существенных усилий со стороны разработчика. Так в версии Cactus 1.2 содержится около 80 различных регистрационных вызовов, охватывающих 15 классов. В версии 1.3 эти 80 вызовов были заменены одним аспектом, который автоматически регистрирует и параметры, и возвращаемые значения наряду с входами и выходами метода. Упрощенная версия этого аспекта (опущена регистрация параметра и возвращаемого значения) представлена ниже:
 
<source lang="java">
<pre>Publicpublic aspect AutoLog {
{
pointcut publicMethods() : execution(public * org.apache.cactus..*(..));
 
Строка 34 ⟶ 39 :
pointcut loggableCalls() : publicMethods() && ! logObjectCalls();
 
before() : loggableCalls(){
{
Logger.entry(thisJoinPoint.getSignature().toString());
}
 
after() : loggableCalls(){
{
Logger.exit(thisJoinPoint.getSignature().toString());
}
}
}</pre>
</source>
 
Проанализируем этот пример и посмотрим, какие действия осуществляет аспект. Первое, на что нужно обратить внимание — это объявление аспекта. Оно подобно объявлению класса и, так же как класс, определяет тип Java. Кроме того, аспект содержит конструкции ''pointcut'' и ''advice''.
Строка 51 ⟶ 59 :
Теперь, после того, как в аспекте определены точки, нужно использовать конструкцию advice, чтобы выполнить текущую регистрацию. Advice — это фрагмент кода, выполняющийся до, после или в составе точки соединения. Advice определяется для pointcut, что представляет собой нечто наподобие указания «выполнить этот код после каждого вызова метода, который надо регистрировать». В нашем примере первый advice объявлен следующим образом:
 
<source lang="java">
<pre>before() : loggableCalls(){
{
Logger.entry(thisJoinPoint.getSignature().toString());
}
}</pre>
</source>
 
Этот advice использует класс Logger, методы entry и exit которого выглядят следующим образом:
 
<source lang="java">
<pre>public static void entry(String message) {
{
System.out.println("entering method " + message);
}
}</pre>
</source>
 
В приведенном примере классу logger передается String, образованная от thisJoinPoint, специального объекта, разрешающего доступ к контексту времени выполнения, в котором исполняется точка соединения. В данном, используемом Cactus-ом аспекте, advice применяет этот объект для извлечения параметров метода, передающихся в каждый зарегистрированный вызов метода. «След» вызова метода (с применением аспекта) в log-файле выглядит следующим образом:
 
}</pre>
<pre>Entering method: void test.Logging.main(String[])
Entering method: void test.Logging.foo()
exiting method: void test.Logging.foo()
exiting method: void test.Logging.main(String[])
</pre>
 
=== Advice типа around ===
Строка 73 ⟶ 89 :
Следующий advice вызывает (или не вызывает) исполнение метода say из класса Hello в зависимости от генерируемого случайного числа (random):
 
<source lang="java">
<pre>Voidvoid around(): call(public void Hello.say()){
if (Math.random() > .5){
{
if (Math.random() > .5){
{
proceed();//go ahead with the method call
}
else{
{
System.out.println("Fate is not on your side.");
}
}
}</pre>
</source>
 
== Программирование в AspectJ ==