Aoite 系列(02) - 超动感的 Ioc 容器
Aoite 系列(02) - 超动感的 Ioc 容器
Aoite 是一个适于任何 .Net Framework 4.0+ 项目的快速开发整体解决方案。Aoite.Ioc 是一套解决依赖的最佳实践。
说明: Aoite 是一套快速开发整体解决方案。它不是只有 ORM 或者 Ioc 之类的。框架的内容还是算有点庞大。我需要一点一点的将文章和教程编写出来,如果加上将其每一部分和其他框架进行比较更需要花费时间。所以所有的入门篇都会简单的介绍用法,目的是让使用人员快速入门。若是您想要更快的了解这套框架,可以从单元测试入手。
赶紧加入 Aoite GitHub 的大家庭吧!!
1. 快速入门
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则。IoC的概念已经提出了非常多年了。
如果...如果...你对 Ioc 不是很理解的话,我们可以这么理解:

秒懂了?不懂也没关系,反正我也没打算在这里长篇大论的讲解什么是 IoC。网上有许多关控制反转、依赖注入的相关文章。我就不在这里误人子弟了 :)
和其他 Ioc 框架先不做比较。Aoite.Ioc 比较有意思的一点是:提倡的是无配置化模式。
我们还是赶紧通过代码快速了解 Aoite 的 IoC 模块。
interface IWelcome
{
string GetHelloText();
}
class DefaultWelcome : IWelcome
{
public string GetHelloText()
{
return "Hello World!";
}
}
class ChineseWelcome : IWelcome
{
public string GetHelloText()
{
return "你好,世界!";
}
}
private static void Demo1()
{
IocContainer container = new IocContainer();
Console.WriteLine(container.GetService<IWelcome>().GetHelloText());
}
是的,仅仅只有这样的代码,当你调用了 Demo1 方法后,它将会直接输出“Hello World!”文字。
1.1 它是怎么动感的
Aoite.Ioc 模块有一套类型映射的策略。它是这样的一步一步的匹配:
- 判定
IWelcome是否已经手工注册(container.AddService)。 - 判定上级 Ioc 容器是否已手工注册
IWelcome。 - 判定是否禁用了自动解析的功能(
IocContainer.DisabledAutoResolving),成立则直接返回 null 值。 - 判定
IWelcome是否定义了DefaultMappingAttribute特性。 - 判定是否为基类或值类型,成立则直接返回 null 值。
- 尝试触发
IocContainer.MapResolve事件获取映射类型。 - 尝试触发
ObjectFactory.MapResolve静态事件获取映射类型。 - 如果以上条件都找不到映射的类型,将会从当前所有已加载的程序集中满足以下条件的类型(优先级从上至下):
- namespace.DefaultWelcome
- namespace.Welcome
- namespace.FakeWelcome
- namespace.MockWelcome
- 如果以上的条件无法满足,将会返回一个 null 值。
所以为了我们可以将代码改成这样,代替默认的 DefaultWelcome。
private static void Demo1()
{
IocContainer container = new IocContainer();
container.AddService<IWelcome, ChineseWelcome>(); /* 手工注册 */
Console.WriteLine(container.GetService<IWelcome>().GetHelloText());
}
小技巧:通过 IocContainer.MapResolve 事件或 ObjectFactory.MapResolve 静态事件,你可以应用到 WCF、Remoting 等场景。
1.2 单例模式
讲解单例模式之前,我们先来做一个测试:
private static void Demo1()
{
IocContainer container = new IocContainer();
container.AddService<IWelcome, ChineseWelcome>();
Console.WriteLine(container.GetService<IWelcome>() == container.GetService<IWelcome>());
}
它输出的是 False。为什么会这样呢?原因是默认情况下,IocContainer 并不会将类型单例化。因为它无法准确判断你是要创建一个对象,还是每次调用都创建一个新的对象。
所以,如果要单例,你可以尝试以下几种方式:
接口特性,这样的方式会导致获取这个接口的所有类型,都采用单例模式。
[SingletonMapping]
interface IWelcome
{
//......
}
类特性,只有映射到这个类型,才会成为单例模式。
[SingletonMapping]
class ChineseWelcome : IWelcome
{
//......
}
注册约定
container.AddService<IWelcome, ChineseWelcome>(true /* singletonMode */);
1.3 懒加载
有时候,我们需要一个类似 Lazy 的懒加载方式,或者你需要根据不同的后期绑定参数,返回不同的类型。你可以这样折腾:
IocContainer container = new IocContainer();
container.AddService<IWelcome>(lmps =>
{
if(lmps == null || lmps.Length == 0) return new DefaultWelcome();
return new ChineseWelcome();
});
Console.WriteLine(container.GetService<IWelcome>().GetHelloText());
Console.WriteLine(container.GetService<IWelcome>("abc").GetHelloText());
Console.WriteLine(container.GetService<IWelcome>().GetHelloText());
Console.WriteLine(container.GetService<IWelcome>(1).GetHelloText());
这样的话,输出的内容便是:
Hello World!
你好,世界!
Hello World!
你好,世界!
小技巧:通过 InstanceCreatorCallback 委托,可以很灵活的创建对象。
// 摘要:
// 表示实例创建的委托。
//
// 参数:
// lastMappingArguments:
// 后期绑定的参数列表。
//
// 返回结果:
// 返回一个实例。
public delegate object InstanceCreatorCallback(object[] lastMappingArguments);
2. 进阶内容
2.1 Key-Value 的映射
IocContainer 除了支持对类型的支持外,还支持类似配置参数的方式。比如你可以这样玩:
IocContainer container = new IocContainer();
container.AddValue("a", 1);
container.AddValue("b", 2);
Console.WriteLine(container.GetValue("a"));
Console.WriteLine(container.GetValue("b"));
Console.WriteLine(container.GetValue("c") ?? "<NULL>");
这样有什么意义吗?第一个意义是可以依赖倒置某些简单的配置信息。比如数据库连接字符串、Redis 的连接地址之类。除此之外,还有其他意义吗?
答案是:有!
2. 带参数的构造函数
假设我们新增了一种 Welcome 类型:
class CustomWelcome : IWelcome
{
private string _welcomeText;
public CustomWelcome(string welcomeText)
{
this._welcomeText = welcomeText;
}
public string GetHelloText()
{
return "Oh~" + this._welcomeText;
}
}
那么我们该如何映射呢?搜一鸡!还支持多种姿势!
第一种 后期映射
class CustomWelcome : IWelcome
{
//.....
public CustomWelcome([LastMapping]string welcomeText)
//.....
}
指定了 LastMappingAttribute 表示这个参数允许通过后期绑定来赋值。这个特性还可以装载在类或接口上,表示这个类型/接口如果用在构造函数的话,都会被当作后期绑定参数。
IocContainer container = new IocContainer();
container.AddService<IWelcome, CustomWelcome>();
Console.WriteLine(container.GetService<IWelcome>("自定义欢迎语。").GetHelloText());
第二种 预配模式 不需要加上 LastMappingAttribute 特性,直接通过 Key-Value 映射(指定目标类型优先,并且若存在上级容器,将会寻找到上级容器)。
IocContainer container = new IocContainer();
container.AddValue("welcomeText", "这是一种鸟语的欢迎语。");
container.AddService<IWelcome, CustomWelcome>();
Console.WriteLine(container.GetService<IWelcome>().GetHelloText());
第三种 智能模式 适用于类似以下的业务场景:
public class AccountController : Controller
{
public AccountController(IUserRepository userRepository)
//.....
}
userRepository 参数并不需要指定特性 LastMappingAttribute,甚至无需预配 IUserRepository 接口的映射类型。一气呵成,浑然天成。
需要说明的是,映射的优先级也是从第一种到最后一种。
2.3 Key-Value 的针对性映射
显然 2.1 中的方式虽然好用,但有些场景却不适合,比如说不同类型相同参数名称的场景。这个时候,就可以采用以下方法:
IocContainer container = new IocContainer();
container.AddValue<CustomWelcome>("welcomeText", "这是一种鸟语的欢迎语。");
//- 或 container.AddValue<IWelcome>(...);
container.AddService<IWelcome, CustomWelcome>();
Console.WriteLine(container.GetService<IWelcome>().GetHelloText());
2.4 强制性要求手工注册
有些场景,我们不希望通过智能解析映射。而是在确保未注册情况下返回 null 值。这个时候就需要用到 GetFixedService 方法。这个方法不会智能去解析,它只会判断是否已经在注册列表,如果没有,直接返回 null 值。
2.5 更多
- Parent:上级容器
- ServiceTypes:所有服务类型。
- TypeValueNames:所有绑定到类型的值的名称。
- ValueNames:所有值的名称。
- DestroyAll():销毁所有的映射。
- CreateChildLocator():创建基于当前服务容器的子服务容器。
- ContainsXXXX:判断指定的类型或值是否已注册。
- RemoveXXXX:删除指定的类型或值。
3. 结束
关于 Aoite.Ioc 的简单介绍,就到此结束了,如果你喜欢这个框架,不妨点个推荐吧!如果你非常喜欢这个框架,那请顺便到Aoite GitHub Star 一下 :)
Aoite 系列(02) - 超动感的 Ioc 容器的更多相关文章
- Aoite 系列 目录
介绍 本项目从2009年孵化(V->Sofire->Aoite),至今已度过5个年头.一直在优化,一直在重构,一直在商用.有十分完整的单元测试用例.可以放心使用. Aoite on 博客园 ...
- TypeC一个微软开发的超简单.NET依赖注入/IoC容器
控制反转(IoC,Inversion of Control)是由Martin Fowler总结出来的一种设计模式,用来减少代码间的耦合.一般而言,控制反转分为依赖注入(Dependency Injec ...
- Spring IOC 容器源码分析系列文章导读
1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...
- Spring IOC 容器源码分析 - 余下的初始化工作
1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...
- Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- Spring IOC 容器源码分析 - 创建原始 bean 对象
1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
随机推荐
- 使用phar上线你的代码包
在我前一阵子写的一篇文章<新版 SegmentFault 重构之系统架构>中,很多人对其中提到的利用phar上线代码比较感兴趣,我就在这边跟大家分享下我目前的做法. 哪些项目适合phar打 ...
- Ubuntu Server安装R和Rstudio(zz)
Ubuntu Server安装R和Rstudio 发表于 技术天堂 2014-03-15 21:03 字数: 534 阅读量: 205 R是一个在科研领域很常用的工具,经常用R的年轻人或者经常上统计之 ...
- freeCodeCamp:Falsy Bouncer
真假美猴王! 删除数组中的所有假值. 在JavaScript中,假值有false.null.0."".undefined 和 NaN. /*思路 利用布尔值构造filter的测试函 ...
- ckeditor使用
安装: 下载CKEDITOR的文件,解压后复制到工程的WEBROOT目录下就OK! 引用CKEDITOR的JS文件: 新建JSP页面,添加其JS文件<script type="text ...
- Sql日期时间格式转换
sql server2000中使用convert来取得datetime数据类型样式(全) 日期数据格式的处理,两个示例: CONVERT(varchar(16), 时间一, 20) 结果:2007-0 ...
- error while performing database login with the xxx driver
在MyEclipse的安装路径下D:\Program Files\MyEclipse 6.0\eclipse下面找到eclipse.ini文件,用记事本打开 eclipse.ini文件 -showsp ...
- Java打jar包详细教学
如果我们需要将写好并测试OK的公共接口供多个项目使用,我们可以不用拷贝源代码,可以编译后打包成jar文件,这样会很方便许多,修改的话也方便,直接修改源代码打一个新jar包替换即可,下面是打包的详细教程 ...
- js中的定义
执行环境:执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为,每个执行环境都有一个与之关联的 变量对象:环境中定义的所有变量和函数都保存在这个对象中.(在函数中这个变量对象叫活动对象) ...
- svn服务器搭建与使用
- UI基础之UITableViewController相关
1> 设置类型为Group - (instancetype)initWithStyle:(UITableViewStyle)style { return [super initWithStyle ...