Aoite 系列(02) - 超动感的 Ioc 容器

Aoite 是一个适于任何 .Net Framework 4.0+ 项目的快速开发整体解决方案。Aoite.Ioc 是一套解决依赖的最佳实践。

说明: Aoite 是一套快速开发整体解决方案。它不是只有 ORM 或者 Ioc 之类的。框架的内容还是算有点庞大。我需要一点一点的将文章和教程编写出来,如果加上将其每一部分和其他框架进行比较更需要花费时间。所以所有的入门篇都会简单的介绍用法,目的是让使用人员快速入门。若是您想要更快的了解这套框架,可以从单元测试入手。

【Aoite 系列 目录】

赶紧加入 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 模块有一套类型映射的策略。它是这样的一步一步的匹配:

  1. 判定 IWelcome 是否已经手工注册(container.AddService)。
  2. 判定上级 Ioc 容器是否已手工注册 IWelcome
  3. 判定是否禁用了自动解析的功能(IocContainer.DisabledAutoResolving),成立则直接返回 null 值。
  4. 判定 IWelcome 是否定义了 DefaultMappingAttribute 特性。
  5. 判定是否为基类或值类型,成立则直接返回 null 值。
  6. 尝试触发 IocContainer.MapResolve 事件获取映射类型。
  7. 尝试触发 ObjectFactory.MapResolve 静态事件获取映射类型。
  8. 如果以上条件都找不到映射的类型,将会从当前所有已加载的程序集中满足以下条件的类型(优先级从上至下):
    • namespace.DefaultWelcome
    • namespace.Welcome
    • namespace.FakeWelcome
    • namespace.MockWelcome
  9. 如果以上的条件无法满足,将会返回一个 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 容器的更多相关文章

  1. Aoite 系列 目录

    介绍 本项目从2009年孵化(V->Sofire->Aoite),至今已度过5个年头.一直在优化,一直在重构,一直在商用.有十分完整的单元测试用例.可以放心使用. Aoite on 博客园 ...

  2. TypeC一个微软开发的超简单.NET依赖注入/IoC容器

    控制反转(IoC,Inversion of Control)是由Martin Fowler总结出来的一种设计模式,用来减少代码间的耦合.一般而言,控制反转分为依赖注入(Dependency Injec ...

  3. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  4. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  5. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  6. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  7. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  8. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  9. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

随机推荐

  1. Ubuntu 16 安装odoo10 实录

    安装Ubuntu 16,省略 安装时,默认用户名为 odoo ubuntu 16开始 使用 systemd 管理服务,但是systemd 兼容 sysv init 脚本 下载 odoo源码 从 htt ...

  2. mysql 行变列(多行变成一行/多行合并成一行/多行合并成多列/合并行)

    数据库结构如图: 而我想让同一个人的不同成绩变成此人在这一行不同列上显示出来,此时分为2中展现: 第一种展现如图----[多行变一列](合并后的数据在同一列上): sql如下: select name ...

  3. Eclipse在线集成maven M2eclipse插件

    首先说下版本: Eclipse:3.6 Maven:3.3.1,若不知道如何在本地安装Maven,请参见我的另一篇文章:Window下安装Maven 废话少说,直接讲步骤就好: 1.打开eclipse ...

  4. 《UML大战需求分析》阅读笔记6

    流程分析三剑客之总结 顺序图,活动图相类似表示活动,状态机图表示状态.分析特点: 顺序图: 强调角色之间的交互,信息明确: 从上到下,从左到右,按时间顺序: 不适合表达复杂特殊情况(循环分支,条件分支 ...

  5. Object 转化为String时的一个问题 null->"null"

    近日在工作出了一个较大的问题,导致被客户投诉. 事情大致是,某个功能里新增对用户手机的修改,在平台数据同步过程中,出现了将用户以前的要同步的数据,那时还没有手机号码所以是null,新功能上线后,将手机 ...

  6. 从MyEclipse转战到IntelliJ IDEA的经历

    从MyEclipse转战到IntelliJ IDEA的经历 我一个朋友写了一篇"从Eclipse到Android Studio"博文,于是心潮澎湃我也想一篇,分享自己用这个IDEA ...

  7. ubuntu 安装apache2并配置cgi,搭建mimetex转化公式图片的服务

    一.Apache的安装 在终端输入: sudo apt-get install apache2 二.启动.停止Apache服务 Apache的启动和停止文件是:/etc/init.d/apache2 ...

  8. android手机调试时不能打印Logcat日志信息

    方法: 1.在拨号界面输入:*#*#2846579#*#*  进入测试菜单界面 2.Project Menu–后台设置–LOG设置 3.LOG开关–LOG打开   LOG级别设置–VERBOSE 4. ...

  9. Ubuntu 中 不显示WIFI解决方法

    先用有线接到网络,打开终端,执行以下命令sudo apt-get update sudo apt-get install --reinstall bcmwl-kernel-source 执行成功即可看 ...

  10. hibernate的缓存机制

    hibernate提供两种缓存:一级缓存和二级缓存 一.一级缓存:也就是Session缓存(又称作事务缓存):Hibernate内置的,不能卸除. 在同一个Session里面,第一次调用get()方法 ...