我被要求在我的asp.net Web应用程序中实现Castle Dynamic Proxy,我正在查阅从Castle Project和Code Project获取的几篇文章有关在asp.net Web应用程序中使用Castle Dynamic Proxy的文章......
这两篇文章都涉及创建拦截器,但我无法理解为什么要在类中使用拦截器...为什么要拦截本来表现正常的类呢?
我被要求在我的asp.net Web应用程序中实现Castle Dynamic Proxy,我正在查阅从Castle Project和Code Project获取的几篇文章有关在asp.net Web应用程序中使用Castle Dynamic Proxy的文章......
这两篇文章都涉及创建拦截器,但我无法理解为什么要在类中使用拦截器...为什么要拦截本来表现正常的类呢?
假设你的类在某种操作中需要实现三个任务:
假设你的类不知道有关安全性、日志记录或缓存的具体配置方式,那么你需要依赖这些事物的抽象。
有几种方法可以实现这一点。其中一种方法是设置一堆接口并使用构造函数注入:
public class OrderService : IOrderService
{
private readonly IAuthorizationService auth;
private readonly ILogger logger;
private readonly ICache cache;
public OrderService(IAuthorizationService auth, ILogger logger,
ICache cache)
{
if (auth == null)
throw new ArgumentNullException("auth");
if (logger == null)
throw new ArgumentNullException("logger");
if (cache == null)
throw new ArgumentNullException("cache");
this.auth = auth;
this.logger = logger;
this.cache = cache;
}
public Order GetOrder(int orderID)
{
auth.AssertPermission("GetOrder");
logger.LogInfo("GetOrder:{0}", orderID);
string cacheKey = string.Format("GetOrder-{0}", orderID);
if (cache.Contains(cacheKey))
return (Order)cache[cacheKey];
Order order = LookupOrderInDatabase(orderID);
cache[cacheKey] = order;
return order;
}
}
这并不是糟糕的代码,但是请考虑我们引入的问题:
OrderService
类没有三个依赖项中的任何一个都无法工作。如果我们想要使其能够正常工作,就需要在代码中到处添加空值检查。
为了执行相对简单的操作(查找订单),我们编写了大量的额外代码。
所有这些样板代码必须在每个方法中重复,导致非常庞大、丑陋和容易出错的实现。
下面是一个易于维护得多的类:
public class OrderService : IOrderService
{
[Authorize]
[Log]
[Cache("GetOrder-{0}")]
public virtual Order GetOrder(int orderID)
{
return LookupOrderInDatabase(orderID);
}
}
在面向切面编程中,这些属性被称为连接点,它们的完整集合被称为切入点。
我们可以留下"提示"来说明某些附加操作应该对该方法执行,而不是一遍又一遍地编写依赖代码。
当然,这些属性必须在某个时候转化为代码,但您可以通过为OrderService
创建一个代理(请注意,GetOrder
方法已被设置为virtual
,因为需要重写该服务),并拦截GetOrder
方法,将其延迟到主应用程序代码。
编写拦截器可能只需要像这样简单:
public class LoggingInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (Attribute.IsDefined(invocation.Method, typeof(LogAttribute))
{
Console.Writeline("Method called: "+ invocation.Method.Name);
}
invocation.Proceed();
}
}
创建代理的方法如下:
var generator = new ProxyGenerator();
var orderService = (IOrderService)generator.CreateClassProxy(typeof(OrderService),
new LoggingInterceptor());
这不仅是代码更少重复,而且完全消除了实际的依赖关系,因为看看我们所做的-我们甚至还没有授权或缓存系统,但系统仍在运行。 我们只需通过注册另一个拦截器并检查AuthorizeAttribute
或CacheAttribute
,稍后插入授权和缓存逻辑。
希望这解释了“为什么”。
侧边栏:正如Krzysztof Koźmic所评论的那样,使用动态拦截器不是DP的“最佳实践”。 在生产代码中,您不希望拦截器在不必要的方法中运行,因此请改用IInterceptorSelector。
IInterceptorSelector
实现)中。 - Krzysztof KozmicIInterceptorSelector
,但在这里我主要是为了让那些不熟悉DP的人更容易理解这个基本示例而将其排除在外。 - Aaronaught