使用Unity解耦你的系统—PART4——Unity&PIAB
在前面几篇有关Unity学习的文章中,我对Unity的一些常用功能进行介绍,包括:Unity的基本知识、管理对象之间的关系、生命周期、依赖注入等,今天则是要介绍Unity的另外一个重要功能——拦截(Interception)。
以下是本文所要介绍的内容:
1、Unity与PIAB的关系。
2、使用Unity来接管PIAB的功能实现。
一、Unity与PIAB的关系
Unity中的Interception可以通过Unity的Container或独立的API(Intercept)来实现,这些具体的实现没有包含在 Unity本身的类库中,而独立放在Unity.Interception这个类库中,想深入了解的朋友可以查看下这个类的具体源代码。今天介绍的则是 Unity与企业库内置的PolicyInjection模块的使用。
在我看来,PolicyInjection就像是Unity.Interception模块的一个通用封装,在PIAB中包含了各种常用的 MatchRules(匹配规则)与CallHandler(调用处理程序),其中各种CallHandler都是实现 Unity.InterceptionExtension.ICallHandler,而ICallHandler其定义与 Unity.InterceptionExtension.IInterceptionBehavior类似的。所以由于PIAB中已经内置好了各种常用 的功能,我们只需按照我们的需求调用既可(可参看:内置Call Handler介绍和自定义Matching Rule),如果需要有特殊的逻辑也只需自己定义具体的CallHandler既可(可参看:建立自定义Call Handler实现用户操作日志记录),当然这些都是建立在PIAB的基础上的,如果你不想通过PIAB来实现AOP拦截处理的话,你就可以直接实现Unity中的IInterceptionBehavior来进行具体的业务处理,这个在后面的文章中会介绍到。
其实查看过PIAB模块源码的朋友就可以发现,其实PIAB本质上都是依赖于Unity.Interception模块,其本身没有具体的实现,其具体实 现都包含在Unity.Interception.PolicyInjection下(包括MatchRules与CallHandler),有兴趣的朋 友可以查看下。由于PIAB依赖于Unity.Interception,所以我们完全可以通过Unity来接管原有PIAB的所有功能(包括各种配置信 息)。
二、使用Unity来接管PIAB的功能实现
在建立自定义Call Handler实现用户操作日志记录一文中我已经实现好了一个自定义的CallHandler(如不了解可以先查看下PIAB相关的文章),不过表示层的具体调用还是通过PIAB的PolicyInjection.Create来获取具体对象,而现在我就要通过Unity来接管原来PIAB的这些对象创建,这样极大程度的统一了对象创建与依赖管理。
我这边还是通过代码及配置2种方式来实现Unity接管PIAB的功能,首先是代码的形式,代码配置相对来说比较繁琐,见如下代码:
private void Login1() { try { IUnityContainer container = new UnityContainer().AddNewExtension<Interception>(); container.Configure<Interception>() //为IStudentManage设置拦截器为TransparentProxyInterceptor .SetDefaultInterceptorFor<IStudentManage>(new TransparentProxyInterceptor()) .AddPolicy("UserLog") //增加MemberNameMatchingRule,使用InjectionConstructor初始化MemberNameMatchingRule的构造函数 .AddMatchingRule<MemberNameMatchingRule> (new InjectionConstructor("Login")) //增加CallHandler,使用InjectionConstructor来初始化CallHandler的构造函数 .AddCallHandler<UserLogCallHandler> ("UserLogCallHandler", new ContainerControlledLifetimeManager(), new InjectionConstructor("登录成功", "")); //注册对象关系 container.RegisterType<IStudentManage, StudentManage>(); IStudentManage studentBll = container.Resolve<IStudentManage>(); bool isAdmin2 = false; if (studentBll.Login(txtUid.Text.Trim(), txtPwd.Text.Trim(), out isAdmin2)) { if (string.IsNullOrEmpty(Request.QueryString["returnUrl"]) == false) { Response.Redirect(Request.QueryString["returnUrl"]); } else { Response.Redirect("~/Default.aspx"); } } else { ltMsg.Text = "用户名或密码不正确,请重试!"; } } catch (Exception ex) { throw; } }
在底层不变的情况下,表示层如果想通过Unity来实现需要以上的代码配置。虽然配置较为繁琐,但是Unity很好的接管了PIAB的功能同时很大程度的解决了代码之间的耦合关系,其中有3个注意点:
1、创建Unity的container容器时需要增加Interception扩展:.AddNewExtension<Interception>();
2、在增加策略是需要为需要拦截的接口增加拦截器:SetDefaultInterceptorFor<IStudentManage>(new TransparentProxyInterceptor())
3、如果MatchRule或CallHandler有相应的构造函数需要通过InjectionConstructor类来初始化构造函数。
接下来是配置文件的代码,如下代码:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> <!--新增配置节扩展,用于下面的<interception>配置节--> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" /> <alias alias="IStudentManage" type="EntLibStudy.IBLL.IStudentManage,EntLibStudy.IBLL"/> <alias alias="StudentManage" type="EntLibStudy.BLL.StudentManage,EntLibStudy.BLL"/> <container> <!--为容器增加Interception扩展,如不增加下面interception配置会报错--> <extension type="Interception"/> <interception> <!--增加一个名为UserLog的拦截策略,用于记录日志--> <policy name="UserLog"> <!--新增MemberNameMatchingRule匹配规则,同时需要同过配置初始化匹配规则构造函数 具体可查看Unity.InterceptionExtension.PolicyInjection.MatchRule下具体类--> <matchingRule name="Member Name Matching Rule" type="MemberNameMatchingRule"> <constructor> <param name="nameToMatch" value="Login"/> </constructor> </matchingRule> <!--增加调用处理程序,这边指定的是我自定义的UserLogCallHandler--> <!--同样也需要初始化构造函数--> <callHandler name="UserLogCallHandler" type="EntLibStudy.Helper.EntLibExtension.PolicyInjectionExtension.UserLogCallHandler, EntLibStudy.Helper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <constructor> <param name="message" type="System.String" value="登录成功!"> </param> <param name="parameterName" type="System.String" value=" "> </param> </constructor> </callHandler> </policy> </interception> <!--注册对象关系,需要注意的是需要为这个注册增加TransparentProxyInterceptor的拦截器--> <register type="IStudentManage" mapTo="StudentManage"> <interceptor isDefaultForType="true" type="TransparentProxyInterceptor"/> </register> </container> </unity>
读取配置程序代码如下:
private void Login2() { IUnityContainer container = new UnityContainer() .LoadConfiguration("Log"); IStudentManage studentBll = container.Resolve<IStudentManage>(); bool isAdmin2 = false; if (studentBll.Login(txtUid.Text.Trim(), txtPwd.Text.Trim(), out isAdmin2)) { if (string.IsNullOrEmpty(Request.QueryString["returnUrl"]) == false) { Response.Redirect(Request.QueryString["returnUrl"]); } else { Response.Redirect("~/Default.aspx"); } } else { ltMsg.Text = "用户名或密码不正确,请重试!"; } }
可以看到配置文件代码相对来说也不少,但是对于项目使用来说还是偏向于通过配置来解决代码的耦合问题,而且Unity的XSD提供了很好的智能提 示,保证了书写配置文件效率及正确性,而且通过将这种具体的关联移到配置文件中也可以大大的减少程序代码的书写,程序代码只需要一句简单初始化+读取配置 节既可完成。
配置方式实现的注意点和代码实现类似,我已经写在上面的配置文件中了,其效果和代码配置是一样的。
以上就是本文的所有内容了,仅仅介绍了如何通过Unity来接管PIAB的工作,我这边举的例子是依赖于我原先自己创建的CallHandler,内置的CallHandler配置也是类似的,这边就不再详细介绍了,想了解的朋友还是去查看Unity的官方文档。
有关Unity的拦截技术点可以查看官方文档:Unity Interception Techniques
源代码下载:点我下载