找回密码
 立即注册
首页 业界区 业界 AOP in Asp.net MVC

AOP in Asp.net MVC

崔竹 2025-5-29 20:26:05
 
不同的观点,开拓迥然不同的世界.

Another point of view can sometimes open up a whole new world.

                                                                                                ---- HSBC

本文内容包括:

  • SoC
  • AOP
  • AOP in Asp.net
  • AOP in Asp.net MVC
 
SoC

Separation of concerns (SoC) is the process of breaking a computer program into distinct features that overlap in functionality as little as possible.
源文档  
上面是维基百科中对SoC的定义,在不同的场景中Soc有着不同的含义

  • Soc是一个过程:SoC是一个将功能点分解以尽量减小功能交叉的过程.
  • SoC是软件开发的一个基本原则
  • SoC是一个指标:关注点的分离度
 
       SoC的实践体现在不同的编程模型(Programming paradigm)中,编程模型帮助开发者提高关注点分离度.过程化编程模型把关注点分解成多个过程.在面向对象编程中,关注点抽象为对象/类.面向方面编程模型中,将关注点分解为多个"方面".
1.png
 

        这里有一个非常有意思的思考,我们回顾一下上面三种编程模型的发展历程:面向过程编程模型是人们面对复杂问题的一种自然思路:功能分解.需求的不断变化,功能分解已经不足以应对变化,于是面向对象编程模型的最核心的方法论是抽象.通过在不同视角进行抽象,抽象之后概念视角和规约视角能够保证相对的稳定,具体实现的变化被隔离开.说到这里我们不厌其烦的再次回首面向对象设计原则:SRP原则和ISP原则.单一职责和接口隔离本质上就是在强调对变化的隔离.再具体一点,MVC设计模式也是在强调一种分离,只不过它的是把内容和展示做了分离.
 
AOP

Aspect-oriented programming (AOP) is a programming paradigm that increases modularity by enabling improved separation of concerns. This entails breaking down a program into distinct parts (so-called concerns, cohesive areas of functionality). All programming paradigms support some level of grouping and encapsulation of concerns into separate, independent entities by providing abstractions (e.g. procedures, modules, classes, methods) that can be used to implement, abstract and compose these concerns. But some concerns defy these forms of implementation and are called crosscutting concerns because they "cut across" multiple abstractions in a program.
源文档  
上面的文字同样来自维基百科,我们来做细致解读:
     OOP引入封装、继承和多态性等机制来建立一种对象层次结构.当我们需要为分散的对象引入公共行为的时候,00P则显得无能为力。一个典型的例子:日志功能。日志代码往往水平地散布在所有对象层次中.而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码如权限检查、异常处理也是如此。这种散布在各处的无关的代码被称为横切(cross—cutting)代码,在OOP设计中.它导致了大量代码的重复,而不利于各个模块的重用。本质上核心功能和权限检查之类的功能处于不同的维度,但是在实现的时候所有的代码都被平铺在同一个维度上了.简单看一下下面的示意图:
2.png
  
      OOP解决横切代码的乏力,AOP将散落各处的却又重复的代码组织在一起,在OOP工作的基础上进一步完成模块化.AOP解决这一问题的机制被称为"横切技术";横切技术实现有两种方式:一是采用动态代理技术,利用截取消息的方式.对该消息进行装饰,以取代原有对象行为的执行.二是采用静态织入的方式,引入特
定的语法创建“方面”.从而使得编译器可以在编译期间织入有关“方面”的代码通常将静态横切称为introduce.
 
AOP in Asp.net

      AOP真的离我们很远么?举两个现实中Asp.net的例子:

  • 我们要在用户访问若干页面的时候检查是否具有对该页面的权限
  • 我们要为页面做内容的繁简体转换
     上面的两个问题你打算怎么解决?在每一个页面的Page_Load里面都做一下权限检查么?然后在页面事件的最后做一下繁简体转换?
   是的,我们的解决方法是做了HTTPModule.通过对用户请求的拦截,做权限检查.在页面输出到客户端之前获取控制权完成页面繁简体转换.我们发现这样做的好处是:1.对页面核心代码没有任何修改 2.控制灵活:一旦不需要做权限检查了,只需要注释掉Web.config对应的配置节就可以了.之所以能够获得这样的便利,本质上是因为我们做到了权限检查代码的模块化.我们将权限检查的功能和核心业务成功的分离开,而权限检查功能的切入方式就是通过消息拦截.
    我们把HTTPMoudle理解为一种动态横切,Web应用程序生命周期中的特定事件就是Join point.HTTPModule中对特定事件的绑定就是PointCut,执行逻辑是Advice,一个HTTPModule代表一个Aspect.也许有学院派的朋友会不同意我的看法.我个人的看法是AOP是一种思想,这种思想还没有一个严格的定义,AOP需要更多的实践可能性来丰富,它不拘泥于AspectJ或者Aspect#.
    在享受HTTPMoudle便利的同时,我们也发现了这种方案的特点:应用程序生命周期中所暴露的事件就是可利用的Join Point,由于在应用程序级实施横切,横切功能的粒度是应用程序级的.我们思考的方向自然走到这里:Asp.net是不是可以有一个更细粒度的横切实践?嗯哼,Asp.net MVC出现了.
 
AOP in Asp.net MVC

 上回书我们已经讨论过了Asp.netMVC生命周期,我们截取请求处理的其中一个片段,我们从ActionInvoker.InvokeAction方法开始讨论.基于上面对于AOP的思考,当我们再次审视下图的时候,呈现在我们面前的已经是一个个Join Point.下图中核心的代码执行逻辑是Action Execution-->ActionResult Execution -->View.Render.在这些核心功能的执行前后会成为我们的切入点,类似于应用程序事件的Pre和Post一样,我们看到了OnActionExecuting ,OnActionExecuted,OnResultExecuting,OnResultExecuted.
3.png
 
    OutputCache是一个典型的方面应用,我们通过OutputCacheAttribute这样一个系统的默认实现来做具体分析:
 
4.gif
5.gif
OutputCacheAttribute
  1
6.gif
7.gif
/**//* ****************************************************************************
  2
8.gif
 *
  3
9.gif
 * Copyright (c) Microsoft Corporation. All rights reserved.
  4
10.gif
 *
  5
11.gif
 * This software is subject to the Microsoft Public License (Ms-PL). 
  6
12.gif
 * A copy of the license can be found in the license.htm file included 
  7
13.gif
 * in this distribution.
  8
14.gif
 *
  9
15.gif
 * You must not remove this notice, or any other, from this software.
 10
16.gif
 *
 11
17.gif
 * ***************************************************************************/
 12
18.gif

 13
19.gif
20.gif
namespace System.Web.Mvc 
21.png
{
 14
22.gif
    using System;
 15
23.gif
    using System.Diagnostics.CodeAnalysis;
 16
24.gif
    using System.Web;
 17
25.gif
    using System.Web.UI;
 18
26.gif

 19
27.gif
    [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
 20
28.gif
        Justification = "Unsealed so that subclassed types can set properties in the default constructor.")]
 21
29.gif
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
 22
30.gif
31.gif
    public class OutputCacheAttribute : ActionFilterAttribute 
32.png
{
 23
33.gif

 24
34.gif
        private OutputCacheParameters _cacheSettings = new OutputCacheParameters();
 25
35.gif

 26
36.gif
37.gif
        public string CacheProfile 
38.png
{
 27
39.gif
40.gif
            get 
41.png
{
 28
42.gif
                return _cacheSettings.CacheProfile ?? String.Empty;
 29
43.gif
            }
 30
44.gif
45.gif
            set 
46.png
{
 31
47.gif
                _cacheSettings.CacheProfile = value;
 32
48.gif
            }
 33
49.gif
        }
 34
50.gif

 35
51.gif
52.gif
        internal OutputCacheParameters CacheSettings 
53.png
{
 36
54.gif
55.gif
            get 
56.png
{
 37
57.gif
                return _cacheSettings;
 38
58.gif
            }
 39
59.gif
        }
 40
60.gif

 41
61.gif
62.gif
        public int Duration 
63.png
{
 42
64.gif
65.gif
            get 
66.png
{
 43
67.gif
                return _cacheSettings.Duration;
 44
68.gif
            }
 45
69.gif
70.gif
            set 
71.png
{
 46
72.gif
                _cacheSettings.Duration = value;
 47
73.gif
            }
 48
74.gif
        }
 49
75.gif

 50
76.gif
77.gif
        public OutputCacheLocation Location 
78.png
{
 51
79.gif
80.gif
            get 
81.png
{
 52
82.gif
                return _cacheSettings.Location;
 53
83.gif
            }
 54
84.gif
85.gif
            set 
86.png
{
 55
87.gif
                _cacheSettings.Location = value;
 56
88.gif
            }
 57
89.gif
        }
 58
90.gif

 59
91.gif
92.gif
        public bool NoStore 
93.png
{
 60
94.gif
95.gif
            get 
96.png
{
 61
97.gif
                return _cacheSettings.NoStore;
 62
98.gif
            }
 63
99.gif
100.gif
            set 
101.png
{
 64
102.gif
                _cacheSettings.NoStore = value;
 65
103.gif
            }
 66
104.gif
        }
 67
105.gif

 68
106.gif
107.gif
        public string SqlDependency 
108.png
{
 69
109.gif
110.gif
            get 
111.png
{
 70
112.gif
                return _cacheSettings.SqlDependency ?? String.Empty;
 71
113.gif
            }
 72
114.gif
115.gif
            set 
116.png
{
 73
117.gif
                _cacheSettings.SqlDependency = value;
 74
118.gif
            }
 75
119.gif
        }
 76
120.gif

 77
121.gif
122.gif
        public string VaryByContentEncoding 
123.png
{
 78
124.gif
125.gif
            get 
126.png
{
 79
127.gif
                return _cacheSettings.VaryByContentEncoding ?? String.Empty;
 80
128.gif
            }
 81
129.gif
130.gif
            set 
131.png
{
 82
132.gif
                _cacheSettings.VaryByContentEncoding = value;
 83
133.gif
            }
 84
134.gif
        }
 85
135.gif

 86
136.gif
137.gif
        public string VaryByCustom 
138.png
{
 87
139.gif
140.gif
            get 
141.png
{
 88
142.gif
                return _cacheSettings.VaryByCustom ?? String.Empty;
 89
143.gif
            }
 90
144.gif
145.gif
            set 
146.png
{
 91
147.gif
                _cacheSettings.VaryByCustom = value;
 92
148.gif
            }
 93
149.gif
        }
 94
150.gif

 95
151.gif
152.gif
        public string VaryByHeader 
153.png
{
 96
154.gif
155.gif
            get 
156.png
{
 97
157.gif
                return _cacheSettings.VaryByHeader ?? String.Empty;
 98
158.gif
            }
 99
159.gif
160.gif
            set 
161.png
{
100
162.gif
                _cacheSettings.VaryByHeader = value;
101
163.gif
            }
102
164.gif
        }
103
165.gif

104
166.gif
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "aram",
105
167.gif
            Justification = "Matches the @ OutputCache page directive.")]
106
168.gif
169.gif
        public string VaryByParam 
170.png
{
107
171.gif
172.gif
            get 
173.png
{
108
174.gif
                return _cacheSettings.VaryByParam ?? String.Empty;
109
175.gif
            }
110
176.gif
177.gif
            set 
178.png
{
111
179.gif
                _cacheSettings.VaryByParam = value;
112
180.gif
            }
113
181.gif
        }
114
182.gif

115
183.gif
184.gif
        public override void OnResultExecuting(ResultExecutingContext filterContext) 
185.png
{
116
186.gif
187.gif
            if (filterContext == null) 
188.png
{
117
189.gif
                throw new ArgumentNullException("filterContext");
118
190.gif
            }
119
191.gif

120
192.gif
            // we need to call ProcessRequest() since there's no other way to set the Page.Response intrinsic
121
193.gif
            OutputCachedPage page = new OutputCachedPage(_cacheSettings);
122
194.gif
            page.ProcessRequest(HttpContext.Current);
123
195.gif
        }
124
196.gif

125
197.gif
198.gif
        private sealed class OutputCachedPage : Page 
199.png
{
126
200.gif
            private OutputCacheParameters _cacheSettings;
127
201.gif

128
202.gif
203.gif
            public OutputCachedPage(OutputCacheParameters cacheSettings) 
204.png
{
129
205.gif
                // Tracing requires Page IDs to be unique.
130
206.gif
                ID = Guid.NewGuid().ToString();
131
207.gif
                _cacheSettings = cacheSettings;
132
208.gif
            }
133
209.gif

134
210.gif
211.gif
            protected override void FrameworkInitialize() 
212.png
{
135
213.gif
                // when you put the  directive on a page, the generated code calls InitOutputCache() from here
136
214.gif
                base.FrameworkInitialize();
137
215.gif
                InitOutputCache(_cacheSettings);
138
216.gif
            }
139
217.gif
        }
140
218.gif

141
219.gif
    }
142
220.gif
}
143
221.gif
 
下面对OutputCacheAttribute的特点做一个归纳:

  •  OuputCache标签是应用在Controller级别上的,由于一个Controller可以对应多个View,所以它的粒度介于应用程序级和页面级之间
  •  OuputCache标签是静态引入到Controller的,在编译时完成OutputCache的引入而非运行时,是一种静态横切的实现
  • OuputCache标签的执行的PointCut是OnResultExecuting
  • OutputCacheAttribute的继承关系是:OutputCacheAttribute -----> ActionFilterAttribute----->  FilterAttribute, IActionFilter, IResultFilter
我们今天的主角出现了,看一下ActionFilterAttribute的代码:
 
 1
222.gif
223.gif
/**//* ****************************************************************************
 2
224.gif
 *
 3
225.gif
 * Copyright (c) Microsoft Corporation. All rights reserved.
 4
226.gif
 *
 5
227.gif
 * This software is subject to the Microsoft Public License (Ms-PL). 
 6
228.gif
 * A copy of the license can be found in the license.htm file included 
 7
229.gif
 * in this distribution.
 8
230.gif
 *
 9
231.gif
 * You must not remove this notice, or any other, from this software.
10
232.gif
 *
11
233.gif
 * ***************************************************************************/
12
234.gif

13
235.gif
236.gif
namespace System.Web.Mvc 
237.png
{
14
238.gif
    using System;
15
239.gif

16
240.gif
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
17
241.gif
242.gif
    public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter 
243.png
{
18
244.gif

19
245.gif
        // The OnXxx() methods are virtual rather than abstract so that a developer need override
20
246.gif
        // only the ones that interest him.
21
247.gif

22
248.gif
249.gif
        public virtual void OnActionExecuting(ActionExecutingContext filterContext) 
250.png
{
23
251.gif
        }
24
252.gif

25
253.gif
254.gif
        public virtual void OnActionExecuted(ActionExecutedContext filterContext) 
255.png
{
26
256.gif
        }
27
257.gif

28
258.gif
259.gif
        public virtual void OnResultExecuting(ResultExecutingContext filterContext) 
260.png
{
29
261.gif
        }
30
262.gif

31
263.gif
264.gif
        public virtual void OnResultExecuted(ResultExecutedContext filterContext) 
265.png
{
32
266.gif
        }
33
267.gif
    }
34
268.gif
}
35
269.gif
 
 
   ActionFilterAttribute定义的实际上定义了四个PoinCut,可以看到这四个pointCut是虚方法,注释里面解释了这样做的缘由:可以按照需要来重写感兴趣的方法,就是编写Advice代码了;Asp.net HTTPModule实现本质上是读HTTP请求的拦截,而在Asp.net框架中通过ActionFilterAttribute定义了PointCut,从而创建了更多扩展的机会.除此之外,Asp.net MVC框架还提供了AuthorizeAttribute,HandleErrorAttribute等等常规"方面"默认实现,我们同样可以进行扩展.
    比较一下Asp.net和Asp.netMVC在AOP实现上的不同:
 
Asp.net
Asp.net MVC
AOP粒度
应用程序级
灵活的粒度控制
AOP实现方法
动态横切
静态横切
       Asp.net MVC继承了Asp.net的优良传统,同时Asp.net MVC框架承诺的更灵活,扩展性更好在这里有一个更好的体现.当我们耐心的做一下比较,我们发现这不是一句空话,而今天我们看到的灵活性和扩展性,也仅仅是一个"方面"而已.
 
嗯哼,全文完.
写东西,越来越慢,越来越慢…
           
 
270.jpeg
 

 
271.png
272.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册