Unity Ioc 依赖倒置及Untity AOP被动拦截/自动拦截
各位博友金安,首先声明这是一篇转载的博客,原文链接:https://www.cnblogs.com/scottpei/archive/2013/01/08/2851087.html
十年河东,十年河西,莫欺少年穷
学无止境,精益求精
Unity Ioc 介绍:
Unity是微软团队开发的一个轻量级,可扩展的依赖注入容器,为松散耦合应用程序提供了很好的解决方案,支持构造器注入,属性注入,方法注入。同时因为把对象交给容器创建,有没有可能在创建的时候做些手脚和功能呢?答案是肯定的。
目前Unity中提供两个Lifetime Manager类可供我们直接使用,当然你也可以实现自己的Lifetime Manager类。
1. ContainerControlledLifetimeManager
Unity保存一个指向对象实例的引用。通过Unity容器为同一个类型或对象获取对象实例时,每次获取到的都是同一个实例。也就是说实现了对象单例模式。默认情况下,RegisterInstance方法使用该Lifetime Manager。
2. ExternallyControlledLifetimeManager
Unity仅保存一个指向对象实例的弱引用。通过Unity容器为同一个类型或对象获取对象实例时,每次获取到的都是同一个实例。但是由于当对象创建完之后,容器没有对该对象的强引用,所以就可能出现当其他地方没有去强引用它时候,会被GC回收掉。
先看看一个接口和类,下面会用到
public interface IPlayer
{
void Play();
} public class Mp3Player : IPlayer
{
public void Play()
{
Console.WriteLine("Playing Mp3");
}
}
接下来通过在RegisterType和RegisterInstance时指定相应的Lifetime Manager来介绍Lifetime Manager的应用场景。
1. RegisterType
当用RegisterType注册映射关系时,如果没有指定LifetimeManager,默认是使用一个瞬态的Lifetime Manager。即每次通过Unity容器获取对象的实例时都会重新创建一个该实例,也就是说Unity容器不存在一个到该对象的引用。
看一个例子:
IUnityContainer container = new UnityContainer(); container.RegisterType<IPlayer, Mp3Player>(); IPlayer player1 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player1 HashCode: {0}",player1.GetHashCode())); IPlayer player2 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player2 HashCode: {0}",player2.GetHashCode()));
输出结果:

通过输出的player1和player2对象的HashCode值可以看出,player1和player2分别是Mp3Player类的不同实例。
那怎样实现单例模式呢?
要实现单例模式,容器需要保存一个指向对象实例的引用。通过在RegisterType时为它指定相应的Lifetime Manager可以实现单例模式,从上面对ContainerControlledLifetimeManager和ExternallyControlledLifetimeManager的介绍可以知道,这两个Lifetime Manager都可以支持单例模式。
修改上面的代码为:
IUnityContainer container = new UnityContainer(); //这里指定使用ContainerControlledLifetimeManager对象
container.RegisterType<IPlayer, Mp3Player>(new ContainerControlledLifetimeManager()); IPlayer player1 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player1 HashCode: {0}",player1.GetHashCode())); IPlayer player2 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player2 HashCode: {0}",player2.GetHashCode()));
看看输出:

通过输出结果可以看出,player1和player2对象为Mp3Player类的同一实例,指向同一内存地址。
2. RegisterInstance
当用RegisterInstance注册映射关系时,如果没有指定Lifetime Manager,默认是使用ContainerControlledLifetimeManager,即支持单例模式。
看个例子:
IUnityContainer container = new UnityContainer(); IPlayer mp3Player = new Mp3Player();
container.RegisterInstance<IPlayer>(mp3Player); IPlayer player1 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player1 HashCode: {0}", player1.GetHashCode())); IPlayer player2 = container.Resolve<IPlayer>();
Console.WriteLine(string.Format("Player2 HashCode: {0}", player2.GetHashCode()));
看看输出:

通过输出结果可以看出,player1和player2对象为Mp3Player类的同一实例,指向同一内存地址。
Unity是微软P&P部门开发的一个轻量级IoC框架,通过Interception机制可以实现基于三种拦截机制的AOP。不过Unity仅仅提供“显式”拦截机制,以致我们为了注册可被拦截的类型会多写很多代码和配置。本篇文章通过UnityContainer的扩展提供了一种“自动”拦截机制。
一、显式拦截
我们通过一个简单的实例演示Unity原生支持的显式拦截机制和我们通过扩展实现的自动拦截机制。我们定了如下一个简单的SimpleCallHandler,在Invoke方法中通过在控制台打印一段文字用以证明应用在某个类型上的CallHandler被执行了。
public class SimpleCallHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("The CallHandler applied to \"{0}\" is invoked.", input.Target.GetType().Name);
return getNext()(input, getNext);
}
public int Order { get; set; }
}
public class SimpleCallHandlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
return new SimpleCallHandler
{
Order = this.Order
};
}
}
然后我们创建了如下所示的一个接口IFoo和三个类Foo、Bar和Baz。其中Foo实现了接口IFoo,而Foo依赖于Bar,Bar依赖于Baz。我们以构造器注入的方式定义Foo和Bar。SimpleCallHandler被同时应用到了Foo、Bar和Baz的DoSth方法上。
public interface IFoo
{
void DoSth();
} public class Foo : IFoo
{
public Bar Bar { get; private set; }
public Foo(Bar bar)
{
this.Bar = bar;
}
[SimpleCallHandler]
public virtual void DoSth()
{
this.Bar.DoSth();
}
}
public class Bar : MarshalByRefObject
{
public Baz Baz { get; private set; }
public Bar(Baz baz)
{
this.Baz = baz;
}
[SimpleCallHandler]
public virtual void DoSth()
{
this.Baz.DoSth();
}
}
public class Baz : MarshalByRefObject
{
[SimpleCallHandler]
public void DoSth()
{
Console.WriteLine("Done...");
}
}
所谓显式拦截就是说:如果某个类型需要被拦截处理,比如将其显式地注册为“可被拦截的类型”,并且需要显式地注册拦截器(决定拦截机制)和拦截行为。对于本实例来说,为了上应用在Foo、Bar和Baz上的CallHandler能够起作用,我们需要通过如下的方式对这三个类型进行显式地拦截注册。
IUnityContainer container = new UnityContainer();
container.AddNewExtension<Interception>()
.RegisterType<IFoo, Foo>(new Interceptor<TransparentProxyInterceptor>(), new .RegisterType<Bar>(new Interceptor<TransparentProxyInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>())
.RegisterType<Baz>(new Interceptor<TransparentProxyInterceptor>(), new InterceptionBehavior<PolicyInjectionBehavior>()); IFoo foo = container.Resolve<IFoo>();
foo.DoSth();
运行结果:
1: The CallHandler applied to "Foo" is invoked.
2: The CallHandler applied to "Bar" is invoked.
3: The CallHandler applied to "Baz" is invoked.
4: Done...
二、自动拦截
如果通过我们自定义的UnityContainer扩展AutoInterception,你就无须对需要被拦截的类型进行显式注册。而相关的代码将会变得简单,运行如下一段代码,你依然会得到同上面一样的结果。
IUnityContainer container = new UnityContainer();
container.AddNewExtension<AutoInterception>()
.AddNewExtension<Interception>()
.RegisterType<IFoo, Foo>(); IFoo foo = container.Resolve<IFoo>();
foo.DoSth();
三、应用不同的拦截机制
在默认的情况下,AutoInterception采用的拦截器为TransparentProxyInterceptor。我们通过通过配置AutoInterception的方式来应用其它两种拦截器,即InterfaceInterceptor和VirtualMethodInterceptor。由于在下面的代码中采用了InterfaceInterceptor,所有只有实现了IFoo接口的Foo对象才会被拦截。
IUnityContainer container = new UnityContainer();
container.AddNewExtension<AutoInterception>()
.AddNewExtension<Interception>()
.Configure<AutoInterception>().RegisterInterceptor(new InterfaceInterceptor())
.RegisterType<IFoo, Foo>(); IFoo foo = container.Resolve<IFoo>();
foo.DoSth();
执行结果:
1: The CallHandler applied to "Foo" is invoked.
2: Done...
如果我们采用VirtualMethodInterceptor的话,只有定义在需方法的Foo和Bar的DoSth方法才会被拦截。
IUnityContainer container = new UnityContainer();
container.AddNewExtension<AutoInterception>()
.AddNewExtension<Interception>()
.Configure<AutoInterception>().RegisterInterceptor(new VirtualMethodInterceptor())
.RegisterType<IFoo, Foo>(); IFoo foo = container.Resolve<IFoo>();
foo.DoSth();
输出结果:
1: The CallHandler applied to "Wrapped_Foo_6c22528df1b64d3886e9955cd8961ca7" is invoked.
2: The CallHandler applied to "Wrapped_Bar_c10e3640a27d469c8872ec4193303897" is invoked.
3: Done...
四、支持配置
AutoInterception不仅仅支持Unity提供的Policy Injection配置,还可以通过配置指定采用的拦截器类型。现在我们将应用在Foo、Bar和Baz上的SimpleCallHandlerAttribute特性全部删除,通过如下的配置将该CallHandler应用到所有的DoSth方法上。这个配置还指定了采用的拦截器类型为VirtualMethodInterceptor。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <configSections>
4: <section name="unity"
5: type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
6: </configSections>
7: <unity>
8: <alias alias="SimpleCallHandler" type="Artech.UnityExtensions.SimpleCallHandler, Artech.UnityExtensions" />
9: <alias alias="IFoo" type="Artech.UnityExtensions.IFoo, Artech.UnityExtensions" />
10: <alias alias="Foo" type="Artech.UnityExtensions.Foo, Artech.UnityExtensions" />
11: <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
12: <sectionExtension type="Artech.UnityExtensions.Configuration.AutoInterceptionConfigurationExtension, Artech.UnityExtensions" />
13: <container>
14: <extension type="AutoInterception"/>
15: <extension type="Interception"/>
16: <register type="IFoo" mapTo="Foo"/>
17: <interception>
18: <policy name="service">
19: <matchingRule name="MemberNameMatchingRule" type="MemberNameMatchingRule">
20: <constructor>
21: <param name="nameToMatch" value="DoSth"/>
22: </constructor>
23: </matchingRule>
24: <callHandler name="SimpleCallHandler" type="SimpleCallHandler"/>
25: </policy>
26: </interception>
27: <autoInterception>
28: <interceptor type="VirtualMethodInterceptor"/>
29: </autoInterception>
30: </container>
31: </unity>
32: </configuration>
我们通过如下的代码,通过加载配置的方式来配置创建的UnityContainer。最终直接通过解析接口IFoo得到Foo对象,并调用其DoSth方法。
1: IUnityContainer container = new UnityContainer();
2: UnityConfigurationSection unitySettings = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
3: unitySettings.Configure(container);
4: IFoo foo = container.Resolve<IFoo>();
5: foo.DoSth();
由于我们采用的是VirtualMethodInterceptor,所有只有Foo和Bar中定义的需方法才能被拦截,这可以通过如下的输出结果得到证实:
1: The CallHandler applied to "Wrapped_Foo_53c9f355fbac4acdaf405b2a92d0bd7a" is invoked.
2: The CallHandler applied to "Wrapped_Bar_8cdbf768e96c434da36ed1f181c2d6cd" is invoked.
3: Done...
虽然AutoInterception实现的逻辑并不复杂,但是对于不了解Unity设计的人来说也不是那么容易理解的。所以我并不打算介绍其内部原理,又兴趣的读者可以从
/Files/scottpei/AutoInterception.rar
下载源代码。
Unity Ioc 依赖倒置及Untity AOP被动拦截/自动拦截的更多相关文章
- [Unity 设计模式]IOC依赖倒置
1.前言 最近在看<游戏开发与设计模式>一书,看到控制反转设计模式,作者说:上层模块不应该依赖于下层模块,上层模块和下层模块都应该依赖于接口,这样能减少耦合.然后附带举了个例子,我觉得特别 ...
- C#软件设计——小话设计模式原则之:依赖倒置原则DIP
前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”, ...
- Spring-DI控制反转和IOC依赖注入
Spring-DI控制反转和IOC依赖注入 DI控制反转实例 IDEAJ自动导入Spring框架 创建UserDao.java接口 public interface UserDao { public ...
- IoC模式(依赖、依赖倒置、依赖注入、控制反转)
1.依赖 依赖就是有联系,有地方使用到它就是有依赖它,一个系统不可能完全避免依赖.如果你的一个类或者模块在项目中没有用到它,恭喜你,可以从项目中剔除它或者排除它了,因为没有一个地方会依赖它.下面看一个 ...
- IOC使用Unity 实现依赖注入
转自:http://www.cnblogs.com/techborther/archive/2012/01/06/2313498.html http://www.cnblogs.com/xishuai ...
- 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。
轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战 ...
- 依赖倒置原则(DIP)、控制反转(IoC)、依赖注入(DI)(C#)
理解: 依赖倒置原则(DIP)主程序要依赖于抽象接口,不要依赖于具体实现.高层模块不应该依赖底层模块,两个都应该以来抽象.抽象不应该依赖细节,细节应该依赖抽象.(具体看我上一篇贴子) 依赖倒置原则是六 ...
- 使用MEF应用IOC(依赖倒置)
MVC实用架构设计(二)--使用MEF应用IOC(依赖倒置) 前言 在<上篇>中,基本的项目结构已经搭建起来了,但是有个问题,层与层之间虽然使用了接口进行隔离,但实例化接口的时候,还引 ...
- 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解
1.概述 所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模 ...
随机推荐
- linux (07) redis详解
一.redis持久化RDB 1.在配置文件中添加参数,开启rdb功能 redis.conf 写入 port 6379 daemonize yes logfile /data/6379/redis.lo ...
- Hello,DTOS!(中)
org 0x7c00 //主引导程序的入口地址为0x7c00(物理地址),类似于用c或c++程序中的main函数. start: //定义标签,标签的含义就是mov ax,cs这条指令的地址. ...
- 201871010109-胡欢欢《面向对象程序设计(java)》第八周学习总结
博文正文开头:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/ ...
- 201871010132-张潇潇-《面向对象程序设计(java)》第七周总结
项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...
- day28 8_7 网络编程之tcp协议
一.socket模块 socket模块就是用来网络搭建的模块,socket也叫套接字. 创建网络连接,需要使用两个模块进行模拟,一个作为server服务器端,一个作为client客户端. 在服务器端, ...
- 每天一道Rust-LeetCode(2019-06-10)
每天一道Rust-LeetCode(2019-06-02) Z 字形变换 坚持每天一道题,刷题学习Rust. 题目描述 https://leetcode-cn.com/problems/simplif ...
- Redis和MySQL数据同步及Redis使用场景
1.同步MySQL数据到Redis (1) 在redis数据库设置缓存时间,当该条数据缓存时间过期之后自动释放,去数据库进行重新查询,但这样的话,我们放在缓存中的数据对数据的一致性要求不是很高才能放入 ...
- jq form表单渲染单选框内容渲染
单选框赋值 单选按钮赋值的主要就在于一个value值和name值对应,但是单选的选中状态按钮是input标签的check(选中)属性 当newattr的属性是1的时候为true,或者当newattr的 ...
- 4.Go-结构体、结构体指针和方法
4.1.结构体 结构体:将一个或多个变量组合到一起形成新的类型,这个类型就是结构体,结构体是值类型 定义结构体和赋值 //Learn_Go/main.go package main import &q ...
- 【day05】css
一.盒模型(BoxModel) 1.width 宽度 2.height 高度 说明: 块元素和有宽高属性的标记(img,input) 能设置宽度和高度,而行元素不能设置宽高 3 ...