服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 浅谈C# AOP的简单实现

浅谈C# AOP的简单实现

2022-02-25 14:24懒得安分 C#

这篇文章主要介绍了浅谈C# AOP的简单实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

前言:为了弄清楚aop,博主也是拼了。这篇打算写写aop,说起aop,其实博主接触这个概念也才几个月,了解后才知道,原来之前自己写的好多代码原理就是基于aop的,比如mvc的过滤器filter,它里面的异常捕捉可以通过filterattribute,iexceptionfilter去处理,这两个对象的处理机制内部原理应该就是aop,只不过之前没有这个概念罢了。

一、aop概念

老规矩,还是先看官方解释:aop(aspect-oriented programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统oop编程的一种补充。oop是关注将需求功能划分为不同的并且相对独立,封装良好的类,并让它们有着属于自己的行为,依靠继承和多态等来定义彼此的关系;aop是希望能够将通用需求功能从不相关的类当中分离出来,能够使得很多类共享一个行为,一旦发生变化,不必修改很多类,而只需要修改这个行为即可。aop是使用切面(aspect)将横切关注点模块化,oop是使用类将状态和行为模块化。在oop的世界中,程序都是通过类和接口组织的,使用它们实现程序的核心业务逻辑是十分合适。但是对于实现横切关注点(跨越应用程序多个模块的功能需求)则十分吃力,比如日志记录,权限验证,异常拦截等。

博主的理解:aop就是将公用功能提取出来,如果以后公用功能的需求发生变化,只需要改动公用的模块的代码即可,多个调用的地方则不需要改动。所谓面向切面,就是只关注通用功能,而不关注业务逻辑。实现方式一般是通过拦截。比如,我们随便一个web项目基本都有的权限验证功能,进入每个页面前都会校验当前登录用户是否有权限查看该界面,我们不可能说在每个页面的初始化方法里面都去写这段验证的代码,这个时候我们的aop就派上用场了,aop的机制是预先定义一组特性,使它具有拦截方法的功能,可以让你在执行方法之前和之后做你想做的业务,而我们使用的时候只需要的对应的方法或者类定义上面加上某一个特性就好了。

二、使用aop的优势

博主觉得它的优势主要表现在:

1、将通用功能从业务逻辑中抽离出来,可以省略大量重复代码,有利于代码的操作和维护。

2、在软件设计时,抽出通用功能(切面),有利于软件设计的模块化,降低软件架构的复杂度。也就是说通用的功能都是一个单独的模块,在项目的主业务里面是看不到这些通用功能的设计代码的。

三、aop的简单应用

为了说明aop的工作原理,博主打算先从一个简单的例子开始,通过静态拦截的方式来了解aop是如何工作的。

1、静态拦截

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class order
  {
    public int id { set; get; }
    public string name { set; get; }
    public int count { set; get; }
    public double price { set; get; }
    public string desc { set; get; }
  }
 
  public interface iorderprocessor
  {
    void submit(order order);
  }
  public class orderprocessor : iorderprocessor
  {
    public void submit(order order)
    {
      console.writeline("提交订单");
    }
  }
 
  public class orderprocessordecorator : iorderprocessor
  {
    public iorderprocessor orderprocessor { get; set; }
    public orderprocessordecorator(iorderprocessor orderprocessor)
    {
      orderprocessor = orderprocessor;
    }
    public void submit(order order)
    {
      preproceed(order);
      orderprocessor.submit(order);
      postproceed(order);
    }
    public void preproceed(order order)
    {
      console.writeline("提交订单前,进行订单数据校验....");
      if (order.price < 0)
      {
        console.writeline("订单总价有误,请重新核对订单。");
      }
    }
 
    public void postproceed(order order)
    {
      console.writeline("提交带单后,进行订单日志记录......");
      console.writeline(datetime.now.tostring("yyyy-mm-dd hh:mm:ss") + "提交订单,订单名称:" + order.name + ",订单价格:" + order.price);
    }
  }

调用代码:

?
1
2
3
4
5
6
7
static void main(string[] args)
    {
      order order = new order() { id = 1, name = "lee", count = 10, price = 100.00, desc = "订单测试" };
      iorderprocessor orderprocessor = new orderprocessordecorator(new orderprocessor());
      orderprocessor.submit(order);
      console.readline();
    }

得到结果:

浅谈C# AOP的简单实现

上面我们模拟订单提交的例子,在提交一个订单前,我们需要做很多的准备工作,比如数据有效性校验等;订单提交完成之后,我们还需要做日志记录等。上面的代码很简单,没有任何复杂的逻辑,从上面的代码可以看出,我们通过静态植入的方式手动在执行方法前和执行方法后让它做一些我们需要的功能。aop的实现原理应该也是如此,只不过它帮助我们做了方法拦截,帮我们省去了大量重复代码,我们要做的仅仅是写好拦截前和拦截后需要处理的逻辑。

2、动态代理

了解了静态拦截的例子,你是否对aop有一个初步的认识了呢。下面我们就来到底aop该如何使用。按照园子里面很多牛人的说法,aop的实现方式大致可以分为两类:动态代理和il 编织两种方式。博主也不打算照本宣科,分别拿demo来说话吧。下面就以两种方式各选一个代表框架来说明。

动态代理方式,博主就以微软企业库(ms enterprise library)里面的piab(policy injection application block)框架来作说明。

首先需要下载以下几个dll,然后添加它们的引用。

浅谈C# AOP的简单实现

然后定义对应的handler

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public class user
  {
    public string name { set; get; }
    public string password { set; get; }
  }
 
  #region 1、定义特性方便使用
  public class loghandlerattribute : handlerattribute
  {
    public string loginfo { set; get; }
    public int order { get; set; }
    public override icallhandler createhandler(iunitycontainer container)
    {
      return new loghandler() { order = this.order, loginfo = this.loginfo };
    }
  }
  #endregion
 
  #region 2、注册对需要的handler拦截请求
  public class loghandler : icallhandler
  {
    public int order { get; set; }
    public string loginfo { set; get; }
 
    //这个方法就是拦截的方法,可以规定在执行方法之前和之后的拦截
    public imethodreturn invoke(imethodinvocation input, getnexthandlerdelegate getnext)
    {
      console.writeline("loginfo内容" + loginfo);
      //0.解析参数
      var arrinputs = input.inputs;
      if (arrinputs.count > 0)
      {
        var ousertest1 = arrinputs[0] as user;
      }
      //1.执行方法之前的拦截
      console.writeline("方法执行前拦截到了");
      //2.执行方法
      var messagereturn = getnext()(input, getnext);
 
      //3.执行方法之后的拦截
      console.writeline("方法执行后拦截到了");
      return messagereturn;
    }
  }
  #endregion
 
  #region 3、用户定义接口和实现
  public interface iuseroperation
  {
    void test(user ouser);
    void test2(user ouser, user ouser2);
  }
 
 
  //这里必须要继承这个类marshalbyrefobject,否则报错
  public class useroperation : marshalbyrefobject, iuseroperation
  {
    private static useroperation ouseropertion = null;
    public useroperation()
    {
      //ouseropertion = policyinjection.create<useroperation>();
    }
 
    //定义单例模式将policyinjection.create<useroperation>()产生的这个对象传出去,这样就避免了在调用处写这些东西
    public static useroperation getinstance()
    {
      if (ouseropertion == null)
        ouseropertion = policyinjection.create<useroperation>();
 
      return ouseropertion;
    }
    //调用属性也会拦截
    public string name { set; get; }
 
    //[loghandler],在方法上面加这个特性,只对此方法拦截
    [loghandler(loginfo = "test的日志为aaaaa")]
    public void test(user ouser)
    {
      console.writeline("test方法执行了");
    }
 
    [loghandler(loginfo = "test2的日志为bbbbb")]
    public void test2(user ouser, user ouser2)
    {
      console.writeline("test2方法执行了");
    }
  }
  #endregion

最后我们来看调用的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void main(string[] args)
    {
      try
      {
        var ousertest1 = new user() { name = "test2222", password = "yxj" };
        var ousertest2 = new user() { name = "test3333", password = "yxj" };
        var ouser = useroperation.getinstance();
        ouser.test(ousertest1);
        ouser.test2(ousertest1,ousertest2);
      }
      catch (exception ex)
      {
        //throw;
      }
    }

得到结果如下:

浅谈C# AOP的简单实现

我们来看执行test()方法和test2()方法时候的顺序。

浅谈C# AOP的简单实现

由于test()和test2()方法上面加了loghander特性,这个特性里面定义了aop的handler,在执行test和test2方法之前和之后都会进入invoke()方法里面。其实这就是aop的意义所在,将切面的通用功能在统一的地方处理,在主要逻辑里面直接用过特性使用即可。

3、il编织

静态织入的方式博主打算使用postsharp来说明,一来这个使用起来简单,二来项目中用过这种方式。

postsharp从2.0版本就开始收费了。为了说明aop的功能,博主下载了一个免费版本的安装包,使用postsharp与其它框架不太一样的是一定要下载安装包安装,只引用类库是不行的,因为上文说过,aop框架需要为编译器或运行时添加扩展。使用步骤如下:

(1)下载postsharp安装包,安装。

(2)在需要使用aop的项目中添加postsharp.dll这个dll的引用。

(3)定义拦截的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[serializable]
  public class testaop : postsharp.aspects.onmethodboundaryaspect
  {
     //发生异常时进入此方法
    public override void onexception(methodexecutionargs args)
    {
      base.onexception(args);
    }
 
     //执行方法前执行此方法
    public override void onentry(methodexecutionargs args)
    {
      base.onentry(args);
    }
 
     //执行方法后执行此方法
    public override void onexit(methodexecutionargs args)
    {
      base.onexit(args);
    }
  }

注意这里的testaop这个类必须要是可序列化的,所以要加上[serializable]特性

(4)在需要拦截功能的地方使用。

在类上面加特性拦截,此类下面的所有的方法都会具有拦截功能。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[testaop]public class impc_tm_plant : ifc_tm_plant
  {
    /// <summary>
    /// 获取或设置服务接口。
    /// </summary>
    private ic_tm_plantservice service { get; set; }
    
    public ilist<dto_tm_plant> find()
    {
      dto_tm_plant otest = null;
      otest.name_c = "test";//异常,会进入onexception方法
        return service.findall();
     }
  }

方法上面加特性拦截,只会拦截此方法。

?
1
2
3
4
5
6
7
[testaop]
    public ilist<dto_tm_plant> find()
    {
      dto_tm_plant otest = null;
      otest.name_c = "test";
      return service.findall();
    }

有没有感觉很简单,很强大,其实这一简单应用,解决我们常见的日志、异常、权限验证等功能简直太小菜一碟了。当然postsharp可能还有许多更加高级的功能,有兴趣可以深究下。

4、mvc里面的filter

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class aopfilterattribute : actionfilterattribute, iexceptionfilter
  {
 
    public void onexception(exceptioncontext filtercontext)
    {
      throw new system.notimplementedexception();
    }
    public override void onactionexecuting(actionexecutingcontext filtercontext)
    {
      
      base.onactionexecuting(filtercontext);
    }
 
    public override void onactionexecuted(actionexecutedcontext filtercontext)
    {
      base.onactionexecuted(filtercontext);
    }
  }

在controller里面使用该特性:

?
1
2
3
4
5
6
7
8
9
10
[aopfilter]
    public jsonresult geteditmodel(string strtype)
    {
      var lstres = new list<list<dragelementprop>>();
      var lstrespage = new list<pageproperty>();
 
       //.........todo
 
      return json(new { lstdataattr = lstres, pageattr = lstrespage, lstjsconnections = lstjsplumblines }, jsonrequestbehavior.allowget);
    }

调试可知,在执行geteditmodel(string strtype)方法之前,会先执行onactionexecuting()方法,geteditmodel(string strtype)之后,又会执行onactionexecuted()方法。这在我们mvc里面权限验证、错误页导向、日志记录等常用功能都可以方便解决。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://www.cnblogs.com/landeanfen/p/4782370.html

延伸 · 阅读

精彩推荐
  • C#三十分钟快速掌握C# 6.0知识点

    三十分钟快速掌握C# 6.0知识点

    这篇文章主要介绍了C# 6.0的相关知识点,文中介绍的非常详细,通过这篇文字可以让大家在三十分钟内快速的掌握C# 6.0,需要的朋友可以参考借鉴,下面来...

    雨夜潇湘8272021-12-28
  • C#C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    这篇文章主要介绍了C#设计模式之Strategy策略模式解决007大破密码危机问题,简单描述了策略模式的定义并结合加密解密算法实例分析了C#策略模式的具体使用...

    GhostRider10972022-01-21
  • C#利用C#实现网络爬虫

    利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...

    C#教程网11852021-11-16
  • C#深入理解C#的数组

    深入理解C#的数组

    本篇文章主要介绍了C#的数组,数组是一种数据结构,详细的介绍了数组的声明和访问等,有兴趣的可以了解一下。...

    佳园9492021-12-10
  • C#C#微信公众号与订阅号接口开发示例代码

    C#微信公众号与订阅号接口开发示例代码

    这篇文章主要介绍了C#微信公众号与订阅号接口开发示例代码,结合实例形式简单分析了C#针对微信接口的调用与处理技巧,需要的朋友可以参考下...

    smartsmile20127762021-11-25
  • C#VS2012 程序打包部署图文详解

    VS2012 程序打包部署图文详解

    VS2012虽然没有集成打包工具,但它为我们提供了下载的端口,需要我们手动安装一个插件InstallShield。网上有很多第三方的打包工具,但为什么偏要使用微软...

    张信秀7712021-12-15
  • C#SQLite在C#中的安装与操作技巧

    SQLite在C#中的安装与操作技巧

    SQLite,是一款轻型的数据库,用于本地的数据储存。其优点有很多,下面通过本文给大家介绍SQLite在C#中的安装与操作技巧,感兴趣的的朋友参考下吧...

    蓝曈魅11162022-01-20
  • C#如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    这篇文章主要给大家介绍了关于如何使用C#将Tensorflow训练的.pb文件用在生产环境的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴...

    bbird201811792022-03-05