AOP
面向切面编程
与 OOP
面向对象编程的关系
AOP
(Aspect-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()
方法,静态装饰器实现,并不推荐。
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()
方法。
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.DynamicProxy
或 Castle.Core
)
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.1
与 Unity.Interception
)
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:在接口的方法上进行标记,这样继承这个接口的类里实现这个接口方法的方法就能被拦截。
*/
}
以上四种方式,前三种理解,第四种需要掌握。