使用 Castle Windsor 实现 Web API 依赖注入
使用 Castle Windsor 实现 Web API 依赖注入
Web API 的依赖注入
Web API 定义了依赖注入的接口 IDependencyResolver , 定义如下:
public interface IDependencyResolver : IDependencyScope, IDisposable {
IDependencyScope BeginScope();
}
public interface IDependencyScope : IDisposable {
object GetService(Type serviceType);
IEnumerable<object> GetServices(Type serviceType);
}
接口 IDependencyScope 有两个方法:
- GetService 创建指定类型的一个新实例;
- GetServices 创建制定类型的集合;
IDependencyResolver
接口继承自 IDependencyScope
并添加了一个 BeginScope 方法。 每次请求都会创建新的 Controller , 为了管理对象的生命周期, IDependencyResolver 使用了作用域 (Scope) 的概念。
HttpConfiguration 对象的 DependencyResolver 拥有全局作用域, 当 Web API 创建 Controller 时, 会调用 BeginScope 方法, 返回一个类型为 IDependencyScope 的子作用域。
Web API 接下来调用子作用域的 GetService 方法来创建 Controller , 依赖注入容器可以在这里创建 Controller 的实例, 并解决 Controller 的依赖项。 如果 GetService 方法返回 null
, 则 Web API 会使用 Controller 默认的构造函数来创建 Controller 实例。
注意: 如果 GetService 不能创建指定类型的实例, 应当返回 null ; 如果 GetServices 不能创建指定类型的实例, 应当返回空的集合; 遇到未知类型不能抛出异常。
当请求完成时, 调用的 Dispose 方法来销毁子作用域, 如果 Controller 有资源需要销毁, 请在 Controller 的 Dispose 方法中显式销毁资源。
使用 Castle Windsor 实现依赖注入
通过下面的 PowerShell 命令来安装 Windsor:
Install-Package Castle.Windsor
知道了 IDependencyScope 和 IDependencyResolver 的定义以及注意问题, 实现起来就很容易了, 首先来实现 IDependencyScope , 代码如下:
public class WindsorDependencyScope : IDependencyScope {
private ILogger logger = NullLogger.Instance;
public ILogger Logger {
get { return logger; }
set { logger = value; }
}
private IWindsorContainer container;
protected IWindsorContainer Container {
get { return container; }
}
public WindsorDependencyScope(IWindsorContainer container) {
this.container = container;
}
public void Dispose() {
// ChildScope 销毁时把 Container 也销毁;
if (container.Parent != null) {
container.RemoveChildContainer(container);
}
container.Dispose();
}
public object GetService(Type serviceType) {
// 根据 GetService 的约定, 遇到未知类型不能抛出异常
Logger.DebugFormat("GetService of type {0}", serviceType);
object service = null;
try {
service = container.Resolve(serviceType);
}
catch (Exception ex) {
Logger.Error(string.Format("Can not resolve {0}", serviceType), ex);
}
return service;
}
public IEnumerable<object> GetServices(Type serviceType) {
Logger.DebugFormat("Get All Service of type {0}", serviceType);
// Windsor 的 ResolveAll 方法不会抛出异常, 所以可以直接用;
return container.ResolveAll(serviceType).Cast<object>();
}
}
有了 WindsorDependencyScope , 再实现一个 WindsorDependencyResolver 就更容易了, 代码如下:
public class WindsorDependencyResolver : WindsorDependencyScope, IDependencyResolver {
public WindsorDependencyResolver(IWindsorContainer container) : base(container) { }
public IDependencyScope BeginScope() {
// 创建一个新的 WindsorContainer , 并添加为 ChildContainer
var childContainer = new WindsorContainer();
Container.AddChildContainer(childContainer);
// 返回新的 DepedencyScope
return new WindsorDependencyScope(childContainer);
}
}
注册 WindsorDependencyResolver
通过下面的代码将 WindsorDependencyResolver 注册到 HttpConfiguration 就可以使用了:
public void Configuration(IAppBuilder app) {
var config = new HttpConfiguration();
// 创建 WindsorContainer 新实例
var container = new WindsorContainer();
// 向 Container 注册 WindsorDependencyResolver , 这样 WindsorDependencyResolver 自己
// 也可以使用使用依赖项;
container.Register(
Component.For<IWindsorContainer>().Instance(container),
Component.For<IDependencyResolver>().ImplementedBy<WindsorDependencyResolver>()
);
// 通过配置文件注册其它类型
var installer = Castle.Windsor.Installer.Configuration.FromXmlFile("windsor.config");
container.Install(installer);
config.DependencyResolver.Resolve<IDependencyResolver>();
// 向 OWIN 注册 WebAPI
app.UseWebApi(config);
}
向 Windsor 注册 Controller
值得注意的是, Windsor 中注册的类型默认全是单例的, 而 WebAPI 对每次请求都需要创建 Controller 的新实例, 在请求完成之后销毁实例, 所以在 Windsor 注册的 Controller 类型必须显示声明生命周期为 transient 才能正常使用。
如果使用配置文件注册, 则需要在 xml 文件中添加生命周期, 示例代码如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<facilities>
<facility id="logging"
type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging"
loggingApi="log4net" configFile="log.config" />
</facilities>
<components>
<!-- Controller 类型必须显示声明生命周期为 transient 默认为 singleton -->
<component type="WebApi.Controllers.CategoriesController,WebApi" lifestyle="transient"/>
<component type="WebApi.Controllers.ProductsController,WebApi" lifestyle="transient"/>
</components>
</configuration>
如果使用代码注册, 同样必须在代码中指定生命周期, 示例代码如下:
container.Register(
Component.For<CategoryController>().LifestyleTransient(),
Component.For<ProductsController>().LifestyleTransient()
);
如果 Controller 非常多的话, 可以考虑使用自定义的 Installer 。
参考 Dependency Injection in ASP.NET Web API 2
- 如何在Hue中配置HiveServer2的负载均衡
- 如何修改CDH集群的IP地址
- 如何在Kerberos环境的CDH集群部署Livy
- 如何在Kerberos环境下使用Haproxy实现HiveServer2负载均衡
- Hive与Impala的关键字
- 如何通过Livy的RESTful API接口向非Kerberos环境的CDH集群提交作业
- 如何编译Livy并在非Kerberos环境的CDH集群中安装
- Livy,基于Apache Spark的开源REST服务,加入Cloudera Labs
- 如何在RedHat7上使用Bind搭建DNS服务
- 如何在Redhat7.3的CDH5.14中启用Kerberos
- 如何使用SAML配置Cloudera Manager的身份验证
- 如何使用Shibboleth搭建IDP服务并集成OpenLDAP
- 如何获取Hive正在执行或者已结束的的MapReduce作业的SQL语句
- 如何启用Oozie的HA
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法