Во-первых, чтобы сделать наш перехватчик совместимым со 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()
генерирует экземпляр прокси, устанавливает ему целевой сервис и добавляет список перехватчиков. В результате та же самая структура, что и была показана ранее. Граф вызовов в цепочке перехватчиков так же узнаваем:
Различие в том, что нам не нужно писать ни единой строчки кода для нашего proxy. И это значит, что мы можем с легкостью использовать наш RetryInterceptor
для любого класса в нашем приложении!
Хотим большего!
Чумовое путешествие! Мы начали с разбиения поведения на отдельные классы путем введения шаблона "Декоратор", реализации своего собственного класса proxy с гибким механизмом перехватывания вызова целевого метода и наконец увидели, как спринговский класс ProxyFactory
может сделать это все за нас.
Осталось ли что-нибудь в нашем списке хотелок? Представьте, что в нашем приложении есть добольно большое количество сервисов, для которых мы хотели бы задействовать операцию повтора. Сейчас мы вынуждены для каждого такого класса выполнять строки по созданию прокси, установки целевого сервиса и списка перехватчиков. Как на счет возможности указать на список объектов и предоставить Spring.NET самому обернуть каждый объект этого списка в прокси с предоставленным заранее списком перехватчиков?
Как вы уже могли догадаться, спринговский AOP framework включает в себя возможность, называемую AutoProxing, которая позволяет с легкостью применять перехватчики к множеству объектов. Оставайтесь на нашей волне, чтобы узнать, как работает эта возможность в следующей статье из этой серии!
Как и ранее вы можете скачать пример кода к данной статье.