zoukankan      html  css  js  c++  java
  • 利用IOC和AOP实现一个简单框架(完整)

      实现框架之前,我想先讲一讲为什么我会写这篇文章。

      暑假期间,我在一家软件公司实习。在8月15这天(周一)早上,项目小组组长对我说:“IOC和AOP在项目中经常用到,我希望你能用这两种技术实现一个简单框架”。

      "IOC”、"AOP",我的天,我听都没听过,这是什么。组长说:"我一会儿会发给你一些资料和一个例子,你再查查资料,学习学习,给你两周时间"。好吧,只能这样,框架还是听过,但没做过,对于一些没做过的东西,人总是觉得很神秘、很深奥,不容易做,但当你做过之后才发现,它们只是人们用一些已有的技术手段实现的一些想法,并不太难。

      IOC:(Inversion of Control)  中文意思是“控制反转” 还有一个原理相同的概念是:Dependency Injection(依赖注入)

      AOP:Aspect Oriented Programming-面向方面编程,确切的说应是:面向切面编程。

      上面这两个概念要想详细了解解决什么问题可以查资料,在这就不多说了。

      在查资料和学习的过程中我很痛苦,为什么呢?因为我除了懂得这些原理以外,我还想找个确切的实例,但是找个确切的例子凭的是人品,一般网上说的例子都很零散,有的只给其中的一段代码,你不能看到程序运行的过程;有的只是给出了其中一方面的例子,你也不知道怎样有机的结合起来,你还是不能完全看到整个过程;有的虽然给出了整个实现代码,但还是引用了一些dll文件,你也不能看到这些dll文件中是怎样实现的。有些人说的云里雾里的,不是很懂。于是我经过两周实现一个简单框架之后就想将这个例子共享出来,让后来学习的人能容易入手。

      IOC实际好处就是消除耦合或者降低耦合,减轻以后软件维护的难度,一般主要用到反射。

      可以使用反射动态地创建类型的实例,将类型绑定(其实就是对象引用实例,绑定我刚开始也不懂)到现有对象,然后,可以调用实例的方法等。

      首先我们先做一个通用的可以得到实例的类:Instance.cs

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Xml;
    6 using System.Reflection;
    7
    8 namespace MyIOC.IOC
    9 {
    10 class Instance
    11 {
    12 publicstaticobject GetInstance(String key)
    13 {
    14 String className =null;
    15 object obj =null;
    16 XmlDocument doc =new XmlDocument();
    17
    18 // 加载配置文件
    19 doc.Load("App.config");
    20
    21 XmlNode classNode;
    22
    23 // 得到根节点
    24 XmlNode root = doc.DocumentElement;
    25
    26 classNode = root.SelectSingleNode("descendant::class[key='"+ key +"']");
    27
    28 //得到子节点集
    29 XmlNodeList list = classNode.ChildNodes;
    30
    31 // 得到类名
    32 foreach(XmlNode node in list)
    33 {
    34 // 得到节点名为“class-name”的节点
    35 if (node.Name.Equals("class-name"))
    36 {
    37 className = node.InnerText;
    38 }
    39 }
    40
    41 if (className !=null)
    42 {
    43 // 得到UserPerssion的类型
    44 Type t = Type.GetType(className);
    45
    46 // 得到UserPermission的实例
    47 obj = Activator.CreateInstance(t);
    48 }
    49
    50 return obj;
    51 }
    52 }
    53 }

      配置文件主要写反射所需要的类的信息

      App.config配置文件内容如下:

    View Code
     1 <?xml version="1.0" encoding="utf-8"?>
    2 <configuration xmlns:bk="urn:samples">
    3
    4 <class>
    5 <key>IUserPermission</key>
    6 <class-name>MyIOC.IOC.UserPermission</class-name>
    7 </class>
    8
    9 <class>
    10 <key>IData</key>
    11 <class-name>MyIOC.Data</class-name>
    12 </class>
    13
    14 </configuration>

      为了方便演示,我们反射项目中的一个类,你可以看到和上面配置文件的关系,实际应用中一般是引用外部dll文件,一般这些dll是处理日志记录,性能统计,安全控制,事务处理,异常处理这些与业务逻辑无关的东西,比如我们要验证一个人的权限,我们就不需要满世界的写验证权限的代码。下面给出的就是反射时需要验证权限的接口与实现类

      IUserPermission.cs

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5
    6 namespace MyIOC.IOC
    7 {
    8 interface IUserPermission
    9 {
    10 void HasPermission(String name);
    11 }
    12 }

      UserPermission.cs

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Windows.Forms;
    6
    7 namespace MyIOC.IOC
    8 {
    9 class UserPermission : IUserPermission
    10 {
    11 private String name ="user";
    12
    13 #region IUserPermission Members
    14
    15 publicvoid HasPermission(string name)
    16 {
    17 if (this.name.Equals(name))
    18 {
    19 Console.WriteLine("you have the permission");
    20 }
    21 else
    22 {
    23 Console.WriteLine("you haven't the permission");
    24 }
    25 }
    26
    27 #endregion
    28 }
    29 }

      其实使用接口也是方便日后维护,如果以后我们不用UserPermission这样验证权限,我们就可以在定义一个类UserPermission2去实现接口IUserPermission,在配置文件里用UserPermission2替换UserPermission即可,如果你不用接口,你就要修改每一处使用UserPermission的地方,这样不是很麻烦。

      顺便说一下,这个验证权限与具体的业务逻辑无关,这里也就体现了面向切面编程的思想,假如我们要写数据,我们就要自动验证权限,看有没有写数据的权限,所以程序写到这我们还不能实现自动验证权限,下一步我们就要用拦截器进行拦截,关于拦截器的的原理和使用我建议你看这篇博文

      首先定义一个接收器

      InterceptorContext.cs

      

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Runtime.Remoting.Messaging;
    6 using MyIOC.IOC;
    7
    8 namespace AOP
    9 {
    10
    11 publicclass InterceptorContext : IMessageSink //实现IMessageSink
    12 {
    13 private IMessageSink nextSink; //保存下一个接收器
    14
    15 //在构造器中初始化下一个接收器
    16 public InterceptorContext(IMessageSink next)
    17 {
    18 nextSink = next;
    19 }
    20
    21 //必须实现的IMessageSink接口属性
    22 public IMessageSink NextSink
    23 {
    24 get
    25 {
    26 return nextSink;
    27 }
    28 }
    29
    30 //实现IMessageSink的接口方法,当消息传递的时候,该方法被调用
    31 public IMessage SyncProcessMessage(IMessage msg)
    32 {
    33 //拦截消息,做前处理
    34 beforeProcess(msg);
    35
    36 //传递消息给下一个接收器
    37 IMessage retMsg = nextSink.SyncProcessMessage(msg);
    38
    39 //调用返回时进行拦截,并进行后处理
    40 afterProcess(msg, retMsg);
    41 return retMsg;
    42 }
    43
    44 //IMessageSink接口方法,用于异步处理,我们不实现异步处理,所以简单返回null,
    45 //不管是同步还是异步,这个方法都需要定义
    46 public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
    47 {
    48 returnnull;
    49 }
    50
    51 privatevoid beforeProcess(IMessage msg)
    52 {
    53 //检查是否是方法调用,我们只拦截Data的WriteData方法。
    54 IMethodCallMessage callMes = msg as IMethodCallMessage;
    55
    56 if (callMes ==null)
    57 return;
    58
    59 if (callMes.MethodName =="WriteData")
    60 {
    61 IUserPermission userPermission = (IUserPermission)Instance.GetInstance("IUserPermission");
    62 userPermission.HasPermission("user");
    63
    64 }
    65 }
    66
    67 // 写入日志文件
    68 privatevoid afterProcess(IMessage msg, IMessage retMsg)
    69 {
    70 IMethodCallMessage callMes = msg as IMethodCallMessage;
    71
    72 if (callMes ==null)
    73 return;
    74 Console.WriteLine("Log the operation");
    75 }
    76 }
    77 }

      上面61行就是使用反射得到一个UserPermission的实例。但是在这块没有扩展性,我们只能拦截Data的WriteData()的方法,所以要提高扩展性,我们还是要配置我们的App.config文件,利用反射来确定我们拦截哪些方法,你可以试着实现。上面的写入日志文件也要使用反射来实现,为了演示,我们就这样简单处理了。你可以将它重写,so easy 啦!

      下来我们定义上下文环境的属性,至于为什么要定义这个,请再看上面推荐的博文

      InterceptorProperty.cs

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Runtime.Remoting.Contexts;
    6 using System.Runtime.Remoting.Messaging;
    7
    8 namespace AOP
    9 {
    10 class InterceptorProperty : IContextProperty, IContributeObjectSink
    11 {
    12 public InterceptorProperty()
    13 {
    14 }
    15
    16 //IContributeObjectSink的接口方法,实例化消息接收器
    17 public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next)
    18 {
    19 returnnew InterceptorContext(next);
    20 }
    21
    22 //IContextProperty接口方法,如果该方法返回ture,在新的上下文环境中激活对象
    23 publicbool IsNewContextOK(Context newCtx)
    24 {
    25 returntrue;
    26 }
    27
    28 //IContextProperty接口方法,提供高级使用
    29 publicvoid Freeze(Context newCtx)
    30 {
    31 }
    32
    33 //IContextProperty接口属性
    34 publicstring Name
    35 {
    36 get { return"DataTrace"; }
    37 }
    38
    39 }
    40 }

      接着是ContextAttribute
      InterceptorAttribute.cs

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Runtime.Remoting.Contexts;
    6 using System.Runtime.Remoting.Activation;
    7
    8 namespace AOP
    9 {
    10 [AttributeUsage(AttributeTargets.Class)]
    11 class InterceptorAttribute : ContextAttribute
    12 {
    13 public InterceptorAttribute()
    14 : base("Interceptor")
    15 {
    16 }
    17
    18 //重载ContextAttribute方法,创建一个上下文环境属性
    19 publicoverridevoid GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
    20 {
    21 ctorMsg.ContextProperties.Add(new InterceptorProperty());
    22 }
    23 }
    24 }

      至此,我们利用IOC和AOP实现的框架就完成了,虽然程序不长,但涉及的知识比较多。

      好了,激动人心的时刻到了,我们现在可以使用我们的框架了。

      首先我们设定一个IData接口,还是那句话,使用接口易于维护。

      IData.cs

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5
    6 namespace MyIOC
    7 {
    8 interface IData
    9 {
    10 void WriteData(String data);
    11 }
    12 }

      实现它: 

      Data.cs

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using AOP;
    6
    7 namespace MyIOC
    8 {
    9 [Interceptor]
    10 class Data : ContextBoundObject, IData
    11 {
    12 publicvoid WriteData(String data)
    13 {
    14 Console.WriteLine(data);
    15 }
    16 }
    17 }

      第9行的 [Interceptor] 是一个拦截标志,要不程序怎么知道拦截哪里的方法呢? 在InterceptorAttribute.cs的第14行你也可以看到它。

      最后看看我们是怎么WriteData的

      Program.cs

    View Code
     1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Reflection;
    6 using MyIOC.IOC;
    7
    8 namespace MyIOC
    9 {
    10 class Program
    11 {
    12 staticvoid Main(string[] args)
    13 {
    14 IData data = (IData)Instance.GetInstance("IData");
    15 data.WriteData("you have write some data");
    16 Console.ReadLine();
    17 }
    18 }
    19 }

      在主程序中,我们只写了三句话,其实只是两句,我们利用反射得到Data的实例,然后调用WriteData的方法,当然我们的Data也在App.config需要注册,看看配置文件,是不是这样。  

      看看运行结果:

      you have the permission
      you have write some data
      Log the operation

      OK,我们可以看到我们只利用反射得到Data的实例,然后调用了WriteData的方法,结果在写那句话之前,拦截器拦截到了这个消息,进行了权限验证,然后输出那句话,最后在日志文件中写入的这个操作。
      最后,我希望我这个小例子可以帮助你了解AOP、IOC和框架,当然这只是我浅浅的一些理解,如果有什么错误不足之处,希望大师批评指正,但拒绝人参公鸡。

  • 相关阅读:
    基于RMAN从活动数据库异机克隆(rman duplicate from active DB)
    包含min函数的栈
    栈的链表实现
    HDU 2196 树形DP Computer
    linux之access函数解析
    [置顶] sqlplus 使用笔记
    仿新浪微博登陆邮箱提示效果!
    找出数组中出现奇数次的元素<异或的应用>
    SOA体系结构基础培训教程-规范标准篇
    一个寻找.jar 和.zip文件中class文件的工具
  • 原文地址:https://www.cnblogs.com/dofi/p/2155755.html
Copyright © 2011-2022 走看看