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 源码的一 ...
随机推荐
- 3dmax渲染插件,生成2.5d瓦片
基于3dmax2013的2.5d渲染插件,demo版,需要的和感兴趣的可以试用,这是百度网盘地址:http://pan.baidu.com/s/1c0mYY7e 插件主要功能,按层级对3dmax场景进 ...
- [OSI]网络7层模型的理解
应用层:HTTP 应用层 表现层:编码 表现层 会话层:端口 会话层 传输层:TCP/UDP 协议 传输层 网络层:IP 标记 +-- 网络层 --+ 网络层 数据链路层:计算机Mac地址标记 | ...
- 危险的“我以为”DDoS&丑陋的现实
有些话题可能终其一生你也可以假装不知道就好了,比如全球气候在变暖,不过有些话题确是不得不面对的冰冷现实,在互联网日益发达的当下,比如你不得不面对的DDoS攻击. DDoS代表了分布式拒绝服务,通过许多 ...
- SQL Server 2008通过LinkServer操作ORACLE
时光荏苒~~ 最近项目有需求需要通过SQL Server2008中的数据自动更新到ORACLE中,其实,一开始肯定会想到触发器,因为可以保证实时性. 方案一: 首先,我很确定的一件事情就是MSSQL中 ...
- 给JavaScript初学者的24条最佳实践
.fluid-width-video-wrapper { width: 100%; position: relative; padding: 0 } .fluid-width-video-wrapp ...
- block反向界面传值
1.在第二个界面的.h文件中申明block @property(nonatomic,copy)void(^myBlock)(NSString * str); 2.在返回第一个界面的点击事件中赋值要传递 ...
- c++ 课堂作业(1)
一.题目 Create a program that asks for the radius of a circle and prints the area of that circle, using ...
- unity 实现物体破碎效果的一些方法
游戏越来越接近现实的感觉,如果有一个真是的 虚拟现实设备,可能我们真的会感觉是在真实世界.场景的逼真是在渲染效果.角色AI.游戏逻辑.物理效果等等一起导致的结果.现在游戏越来越大,除了渲染,物理估计是 ...
- 为什么LTE系统的最小时间单位是Ts?
之前一直在做LTE物理层相关的工作,一直有个疑惑, 在36.211开头的一章定义Ts的大小是1/(15000*2048)s,为什么定义这么一个奇怪的unit time. 最近才反应过来,这跟FFT/I ...
- 编译gtk+程序报错gcc: pkg-config --cflags --libs gtk+-2.0: 没有那个文件或目录
第一次接触gtk+.在网上搜罗良一番,装好相应的库后,编写了第一hello程序.在编译时输入以下命令:gcc -o hello hello.c 'pkg-config --cflags --libs ...