AOP 面向切面编程

AOP 面向切面编程

OOP 面向对象编程的关系

AOPAspect-Oriented Programming,面向方面编程),可以说是 OOP(Object-Oriented Programing ,面向对象编程)的补充和完善。

OOP 引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP 则显得无能为力。也就是说, OOP 允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在 OOP 设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP 技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。

所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP 代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

使用“横切”技术,AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如 Avanade 公司的高级方案构架师 Adam Magee 所说,AOP 的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

AOP 技术的实现

主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。然而殊途同归,实现 AOP 的技术特性却是相同的。

装饰器模式实现静态代理

AOP 在方法的前后增加自定义的方法。详见:Decorator.Show() 方法,静态装饰器实现,并不推荐。

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
public static class Decorator
{
public static void Show()
{
User user = new User() { Name = "John", Password = "12345678" };
IUserProcessor processor = new UserProcessor();
Console.WriteLine("******使用普通方法完成注册******");
processor.RegistUser(user);
Console.WriteLine("******使用装饰器模式完成注册******");
processor = new UserProcessorDecorator(processor);
processor.RegistUser(user);
}

public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
public interface IUserProcessor
{
void RegistUser(User user);
}
public class UserProcessor : IUserProcessor
{
public void RegistUser(User user)
{
Console.WriteLine($"成功注册用户:{user.Name} 密码:{user.Password}");
}
}
/// <summary>
/// 装饰器模式去提供一个AOP功能
/// </summary>
public class UserProcessorDecorator : IUserProcessor
{
private IUserProcessor UserProcessor { get; set; }
public UserProcessorDecorator(IUserProcessor processor)
{
UserProcessor = processor;
}
public void RegistUser(User user)
{
PreProceed(user);
try
{
this.UserProcessor.RegistUser(user);
}
catch (Exception)
{
throw;
}
PostProced(user);
}

private void PreProceed(User user)
{
Console.WriteLine($"注册用户:{user.Name} 密码:{user.Password} 之前");
}

private void PostProced(User user)
{
Console.WriteLine($"注册用户:{user.Name} 密码:{user.Password} 之后");
}
}
}

使用 .Net Remoting/RealProxy 实现动态代理

详见 Proxy.Show() 方法。

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
89
90
91
public static class Proxy
{
public static void Show()
{
User user = new User() { Name = "John", Password = "12345678" };
IUserProcessor processor = new UserProcessor();
Console.WriteLine("******使用普通方法完成注册******");
processor.RegistUser(user);
Console.WriteLine("******使用动态代理完成注册******");
UserProcessor userProcessor = TransparentProxy.Create<UserProcessor>();
userProcessor.RegistUser(user);
}

public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
public interface IUserProcessor
{
void RegistUser(User user);
}
public class UserProcessor : MarshalByRefObject, IUserProcessor
{
public void RegistUser(User user)
{
Console.WriteLine($"成功注册用户:{user.Name} 密码:{user.Password}");
}
}

public class MyRealProxy<T> : RealProxy
{
private T tTarget;
public MyRealProxy(T target) : base(typeof(T))
{
this.tTarget = target;
}
public override IMessage Invoke(IMessage msg)
{
PreProceed(msg);
IMethodCallMessage callMessage = (IMethodCallMessage)msg;
object returnValue = callMessage.MethodBase.Invoke(this.tTarget, callMessage.Args);
PostProced(msg);
return new ReturnMessage(returnValue, new object[0], 0, null, callMessage);
}

private void PreProceed(IMessage msg)
{
IMethodCallMessage callMessage = (IMethodCallMessage)msg;
StringBuilder sbInfo = new StringBuilder();
if (callMessage.Args.Length > 0)
{
PropertyInfo[] props = callMessage.Args[0].GetType().GetProperties();
for (int i = 0; i < props.Length; i++)
{
sbInfo.Append($"{props[i].Name}:{props[i].GetValue(callMessage.Args[0], null)} ");
}
}
Console.WriteLine($"方法执行前:{sbInfo.ToString().TrimEnd()}");
}

private void PostProced(IMessage msg)
{
IMethodCallMessage callMessage = (IMethodCallMessage)msg;
StringBuilder sbInfo = new StringBuilder();
if (callMessage.Args.Length > 0)
{
PropertyInfo[] props = callMessage.Args[0].GetType().GetProperties();
for (int i = 0; i < props.Length; i++)
{
sbInfo.Append($"{props[i].Name}:{props[i].GetValue(callMessage.Args[0], null)} ");
}
}
Console.WriteLine($"方法执行后:{sbInfo.ToString().TrimEnd()}");
}
}

/// <summary>
/// TransparentProxy
/// </summary>
public static class TransparentProxy
{
public static T Create<T>()
{
T instance = Activator.CreateInstance<T>();
MyRealProxy<T> proxy = new MyRealProxy<T>(instance);
T transparentProxy = (T)proxy.GetTransparentProxy();
return transparentProxy;
}
}
}

使用 Castle/DynamicProxy 实现动态代理

详见 CastleProxy.Show() 方法。(nuget 安装 Castle.DynamicProxyCastle.Core

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
public class CastleProxy
{
public static void Show()
{
User user = new User() { Name = "John", Password = "12345678" };
IUserProcessor processor = new UserProcessor();
Console.WriteLine("******使用普通方法完成注册******");
processor.RegistUser(user);
Console.WriteLine("******使用动态代理完成注册******");
ProxyGenerator generator = new ProxyGenerator();
MyInterceptor interceptor = new MyInterceptor();
processor = generator.CreateClassProxy<UserProcessor>(interceptor);
processor.RegistUser(user);
}

public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
public interface IUserProcessor
{
void RegistUser(User user);
}
public class UserProcessor : MarshalByRefObject, IUserProcessor
{
/// <summary>
/// 必须是虚方法
/// </summary>
/// <param name="user"></param>
public virtual void RegistUser(User user)
{
Console.WriteLine($"成功注册用户:{user.Name} 密码:{user.Password}");
}
}

public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
PreProceed(invocation);
invocation.Proceed();
PostProced(invocation);
}

private void PreProceed(IInvocation invocation)
{
StringBuilder sbInfo = new StringBuilder();
if (invocation.Arguments.Length > 0)
{
PropertyInfo[] props = invocation.Arguments[0].GetType().GetProperties();
for (int i = 0; i < props.Length; i++)
{
sbInfo.Append($"{props[i].Name}:{props[i].GetValue(invocation.Arguments[0], null)} ");
}
}
Console.WriteLine($"方法执行前:{sbInfo.ToString().TrimEnd()}");
}

private void PostProced(IInvocation invocation)
{
StringBuilder sbInfo = new StringBuilder();
if (invocation.Arguments.Length > 0)
{
PropertyInfo[] props = invocation.Arguments[0].GetType().GetProperties();
for (int i = 0; i < props.Length; i++)
{
sbInfo.Append($"{props[i].Name}:{props[i].GetValue(invocation.Arguments[0], null)} ");
}
}
Console.WriteLine($"方法执行后:{sbInfo.ToString().TrimEnd()}");
}
}
}

使用 EntLib/PIBA Unity 实现动态代理

详见 UnityAOP.Show() 方法。(nuget 安装Unity 4.0.1Unity.Interception

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
public class UnityAOP
{
public static void Show()
{
User user = new User() { Name = "John", Password = "12345678" };
IUserProcessor processor = new UserProcessor();
Console.WriteLine("******使用普通方法完成注册******");
processor.RegistUser(user);

Console.WriteLine("******使用动态代理完成注册******");
IUnityContainer container = new UnityContainer();//声明一个容器
container.RegisterType<IUserProcessor, UserProcessor>();//声明UnityContainer
processor = container.Resolve<IUserProcessor>();
processor.RegistUser(user);//调用

//AOP
container.AddNewExtension<Interception>().Configure<Interception>().SetInterceptorFor<IUserProcessor>(new InterfaceInterceptor());
processor = container.Resolve<IUserProcessor>();
processor.RegistUser(user);
}

#region 特性
public class UserHanlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
ICallHandler handler = new UserHandler() { Order = this.Order };
return handler;
}
}

public class LogHandlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
ICallHandler handler = new LogHandler() { Order = this.Order };
return handler;
}
}

public class ExceptionHandlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
ICallHandler handler = new ExceptionHandler() { Order = this.Order };
return handler;
}
}

public class AfterLogHandlerAttributeAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(IUnityContainer container)
{
ICallHandler handler = new AfterLogHandler() { Order = this.Order };
return handler;
}
}
#endregion

#region 特性对应的行为
public class UserHandler : ICallHandler
{
public int Order { get; set; }

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("this is the user handler.");
User user = input.Arguments[0] as User;
Console.WriteLine($"regist user Name: {user.Name} Password: {user.Password}");
if (string.IsNullOrWhiteSpace(user.Name))
{
throw new Exception("user name can't be null, empty or white space.");
}
if (string.IsNullOrWhiteSpace(user.Password) || user.Password.Length < 8 || user.Password.Length > 32)
{
throw new Exception("user password can't be null, empty or white space, and the password's length must between 8-32.");
}
//return getNext.Invoke().Invoke(input, getNext);
return getNext()(input, getNext);
}
}

public class LogHandler : ICallHandler
{
public int Order { get; set; }

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("this is the log handler.");
User user = input.Arguments[0] as User;
Console.WriteLine($"Name: {user.Name} Password: {user.Password}");
return getNext()(input, getNext);
}
}

public class ExceptionHandler : ICallHandler
{
public int Order { get; set; }

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
IMethodReturn methodReturn = getNext()(input, getNext);
Console.WriteLine("this is the exception handler.");
User user = input.Arguments[0] as User;
if (methodReturn.Exception == null)
{
Console.WriteLine($"end regist user. Name: {user.Name} Password: {user.Password} don't have exception.");
}
else
{
Console.WriteLine($"end regist user. Name: {user.Name} Password: {user.Password} have a exception: {methodReturn.Exception.Message}");
}
return methodReturn;
}
}

public class AfterLogHandler : ICallHandler
{
public int Order { get; set; }

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
Console.WriteLine("this is the end log handler.");
User user = input.Arguments[0] as User;
Console.WriteLine($"end regist user. Name: {user.Name} Password: {user.Password}");
return getNext()(input, getNext);
}
}
#endregion

public class User
{
public string Name { get; set; }
public string Password { get; set; }
}

[UserHanler(Order = 1), ExceptionHandler(Order = 3), LogHandler(Order = 2), AfterLogHandlerAttribute(Order = 5)]
public interface IUserProcessor
{
void RegistUser(User user);
void GetUser(User user);
}
public class UserProcessor : IUserProcessor
{
public void GetUser(User user)
{
throw new NotImplementedException();
}

public void RegistUser(User user)
{
Console.WriteLine($"成功注册用户:{user.Name} 密码:{user.Password}");
}
}

/*
* TransparentProxyInterceptor:直接在类的方法上进行标记,但是这个类必须继承MarshalByRefObject,不建议使用。
* VirtualMethod:直接在类的方法上进行标记,但是这个方法必须是虚方法。
* InterfaceInterceptor:在接口的方法上进行标记,这样继承这个接口的类里实现这个接口方法的方法就能被拦截。
*/
}

以上四种方式,前三种理解,第四种需要掌握。