Назад

Во-первых, чтобы сделать наш перехватчик совместимым со Spring.NET, нам нужно реализовать спринговский интерфейс IMethodInterceptor (вместо аналогичного интерфейса, который был описан выше):

public class RetryInterceptor : IMethodInterceptor // Spring.NET's interceptor interface!
{
 public object Invoke(IMethodInvocation invocation)
 {
   int retries = 0;
   while (true)
   {
     try
     {
       return invocation.Proceed();
     }
     catch (Exception ex)
     {
       retries++;
       if (retries >= 3)
       {
         throw; // retry threshold exceeded, giving up
       }
       Thread.Sleep(1000); // wait a second
     }
   }
 }
}

Заметьте, что вместо передачи делегата нашему перехватчику мы передаем объект, реализующий интерфейс IMethodInvocation и вызывем его метод Proceed(), чтобы предать контроль следующему перехватчику в цепочке или реальному методу. В отличие от простого делегата, который мы использовали в нашей реализации, интерфейс IMethodInvocation позволяет получить дополнительную информацию о текущем вызове метода:

public interface IMethodInvocation
{
 /// <summary>
 /// Возвращает экземпляр proxy для метода, который должен быть вызван
 /// </summary>
 object Proxy { get; }
 /// <summary>
 /// Возвращает ссылку на целевой метод для метода, который должен быть вызван
 /// </summary>
 object Target { get; }
 /// <summary>
 /// Возвращает экземпляр MethodInfo для метода, который должен быть вызван
 /// </summary>
 MethodInfo Method { get; }
 /// <summary>
 /// Возвращает аргументы для метода, который должен быть вызван 
 /// </summary>
 object[] Arguments { get; }
 /// <summary>
 /// Передает контроль следующему перехватчику или реальному методу
 /// </summary>
 object Proceed();
}

Теперь, когда мы подготовили наши перехватчики к бою, мы можем использовать класс ProxyFactory для автоматической генерации прокси. Заметьте, что в последующем примере метод для добавления перехватчиков назван AddAdvice. Advice (с англ. совет) - это общепринятый термин для действия, которое должно быть добавлено до или после вызова метода реального объекта. Для более детального ознакомления с терминологией AOP обращайтесь к Spring.NET AOP reference. Вот код, позволяющий Spring.NET генерировать прокси за вас:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.Target = new CalculatorWebService(); // определение целевого сервиса
proxyFactory.AddAdvice( new RetryInterceptor() ); // добавление перехватчика

ICalculator calc = (ICalculator) proxyFactory.GetProxy(); // создание экземпляра прокси

Вызов GetProxy() генерирует экземпляр прокси, устанавливает ему целевой сервис и добавляет список перехватчиков. В результате та же самая структура, что и была показана ранее. Граф вызовов в цепочке перехватчиков так же узнаваем:

alt text

Различие в том, что нам не нужно писать ни единой строчки кода для нашего proxy. И это значит, что мы можем с легкостью использовать наш RetryInterceptor для любого класса в нашем приложении!

Хотим большего!

Чумовое путешествие! Мы начали с разбиения поведения на отдельные классы путем введения шаблона "Декоратор", реализации своего собственного класса proxy с гибким механизмом перехватывания вызова целевого метода и наконец увидели, как спринговский класс ProxyFactory может сделать это все за нас.

Осталось ли что-нибудь в нашем списке хотелок? Представьте, что в нашем приложении есть добольно большое количество сервисов, для которых мы хотели бы задействовать операцию повтора. Сейчас мы вынуждены для каждого такого класса выполнять строки по созданию прокси, установки целевого сервиса и списка перехватчиков. Как на счет возможности указать на список объектов и предоставить Spring.NET самому обернуть каждый объект этого списка в прокси с предоставленным заранее списком перехватчиков?

Как вы уже могли догадаться, спринговский AOP framework включает в себя возможность, называемую AutoProxing, которая позволяет с легкостью применять перехватчики к множеству объектов. Оставайтесь на нашей волне, чтобы узнать, как работает эта возможность в следующей статье из этой серии!

Как и ранее вы можете скачать пример кода к данной статье.