攔截注入--AOP的核心話題。AOP--Aspect Oriented Programming面向方面編程。對我來說這一直是個神密存在,它如何將毫無關聯的類關聯起來加上統一的行為?看看Unity框架是如何實現的。
- Unity攔截注入可以在運行時有效捕獲對象方法調用,並給對象附加上額外的功能。很適用於改變某單個實例的行為,而不是整個類的行為,這如同裝飾模式。裝飾模式是什么?
namespace ConsoleUnityDemo.Decrotor { public interface IWindow { void Show(); } public class ConcreteWindow : IWindow { public void Show() { Console.WriteLine("Show Window"); } } public class Program { static void Main(string[] args) { IWindow model = new ConcreteWindow(); ShowWindow(model); } static void ShowWindow(IWindow model) { model.Show(); } } }
以上代碼中Main中調用了IWindow的Show方法。IWindow的實例可能做為別的組件的一個契約,一般情況下不允許修改,但業務要又要求加上新功能,這種情況下用Decorate Pattern改修改代碼。在沒有改變ConcreteWindow類的情況下,給ConcreteWindow實例的Show()方法添加了一個新行為。以下代碼DecoratorWindow即為ConcreateWindow的實例的Show方法添加了一個新行為。
namespace ConsoleUnityDemo.Decorator { public interface IWindow { void Show(); } public class ConcreteWindow : IWindow { public void Show() { Console.WriteLine("Show Window"); } } public class DecoratorWindow : IWindow { IWindow instance; public DecoratorWindow(IWindow model) { instance = model; } public void Show() { instance.Show(); Console.WriteLine("Add a behavior"); } } public class Program { static void Main(string[] args) { IWindow model = new ConcreteWindow(); IWindow decoratorWindow = new DecoratorWindow(model); ShowWindow(decoratorWindow); } static void ShowWindow(IWindow model) { model.Show(); } } }
Unity中的Type Interception
類型截取不是代理服務器使用派生的類。實例截取的工程通過創建目標對象的代理。類型攔截、 更密切地類似於面向方面編程 (AOP) 技術常見的基於 Java 的系統。類型攔截避免了通過動態地從原始類派生新類並插入對的行為構成了管道的調用使用一個代理對象的性能損耗。下面的示意圖顯示類型截取的基本過程
Demo
1. MyClassForProxyInterception是將要被攔截的一個類,這個類必需從MarshalByRefObject類繼承。
public class MyClassForProxyInterception : MarshalByRefObject
{
public string Width { get; set; }
public void Method()
{
Console.WriteLine("invoke:MyClassForProxyInterception.Method");
}
}
2. 自定義一個攔截捕獲行為MyInterceptionBehavior,這個類必需從Microsoft.Practices.Unity.InterceptionExtension.IInterceptionBehavior接口繼承。在Invoke中添加自定義方法。
public class MyInterceptionBehavior : IInterceptionBehavior
{
public IEnumerable<Type> GetRequiredInterfaces()
{
return Type.EmptyTypes;
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
Console.WriteLine("The method of target object has intercepted!");
return getNext()(input, getNext);
}
public bool WillExecute
{
get { return true; }
}
}
3. 配置
在register節點中向MyClassForProxyInterception類添加攔截對向interceptor,interceptor類型為TransparentProxyInterceptor,再用MyInterceptionBehavior來處理攔截行為。
Unity默認提供了3種interceptor,分別為:VirtualMethodInterceptor、interfaceInterceptor、TransparentProxyInterceptor,其中TransparentProxyInterceptor會捕獲對象的所有行為,當然不包括private和protected的方法我屬性
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="unityInterception" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unityInterception>
<alias alias="MyClass" type="ConsoleUnityDemo.MyClassForProxyInterception, ConsoleUnityDemo" />
<alias alias="MyInterceptionBehavior" type="ConsoleUnityDemo.MyInterceptionBehavior, ConsoleUnityDemo" />
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
<container name="MyInterception">
<extension type="Interception" />
<register type="MyClass">
<!-- Other children, like constructor or property -->
<interceptor type="TransparentProxyInterceptor" />
<interceptionBehavior name="MyBehavior" type="MyInterceptionBehavior" />
<policyInjection />
</register>
</container>
</unityInterception>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
</appSettings>
</configuration>
最后驗證代碼
public class InterceptionDemo
{
#region Interception Injection
static void Main(string[] args)
{
UnityConfigurationSection unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unityInterception");
IUnityContainer container = new UnityContainer();
unitySection.Configure(container, "MyInterception");
var obj = container.Resolve<MyClassForProxyInterception>();
obj.Method();
obj.Width = "100px";
Console.ReadKey();
}
#endregion
}
運行代碼,MyInterceptionBehavior被調用2次,一次是Method方法調用,一次是Width屬性被修改。
完整代碼
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Practices.Unity.InterceptionExtension; using Microsoft.Practices.Unity; using Microsoft.Practices.Unity.Configuration; using System.Configuration; using System.Reflection; using System.ComponentModel; namespace ConsoleUnityDemo { public class MyClassForProxyInterception : MarshalByRefObject { public string Width { get; set; } public void Method() { Console.WriteLine("invoke:MyClassForProxyInterception.Method"); } } public class MyInterceptionBehavior : IInterceptionBehavior { public IEnumerable<Type> GetRequiredInterfaces() { return new[] { typeof(INotifyPropertyChanged) }; } public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { Console.WriteLine("The method of target object has intercepted!"); return getNext()(input, getNext); } public bool WillExecute { get { return true; } } } public class InterceptionDemo { #region Interception Injection static void Main(string[] args) { UnityConfigurationSection unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unityInterception"); IUnityContainer container = new UnityContainer(); unitySection.Configure(container, "MyInterception"); var obj = container.Resolve<MyClassForProxyInterception>(); obj.Method(); obj.Width = "100px"; Console.ReadKey(); } #endregion } }