在软件业,AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。[百度百科 - AOP]
传统的OOP编程,每个类专注于实现各自的功能和职责,进行相对独立的封装,以继承的方式来实现功能结构的扩展。而AOP,则是尽可能将通用的功能逻辑,从不相关的类中分离,单独实现,如果这个逻辑发生变化,则不需要去修改所有使用这个逻辑的类,只需要修改这个通用的行为。
用OOP的方式,实现大多数业务功能逻辑的结构,都是很合适的,但是当一些功能需要横穿所有功能模块时,会导致耦合剧增,并不合适;而AOP则如他的名字一样,可以很好的解决横向切面的问题。AOP是一种只关注切面,专注提取通用的功能,而不关心具体业务逻辑的编程方法。
按照AOP的思路,通用的功能要能横向的影响所有功能模块,比如所有的模块功能调用时都需要进行访问权限验证,比如很多新接口都需要加调试日志,如果对这些模块和接口一一作出修改,代价很大。C# 的 Attribute(类似Java的注解),可以对函数,类,属性做出标注,我们可以很简单的对某个方法加上一个 [Log],来表示这个函数在执行时需要记录日志。此时我们需要对这个函数的参数,返回值进行拦截,才能进行验证、日志记录等操作。那么,具体如何实现呢。
ContextAttribute
, IContributeObjectSink
来获取类的上下文环境,这是通过 Attribute 拦截参数和获取返回值的前提。 [AttributeUsage(AttributeTargets.Class)]
public sealed class AOPContextAttribute : ContextAttribute, IContributeObjectSink
{
public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
{
return new AOPHandler(nextSink);
}
public AOPContextAttribute() : base("AOPContext")
{
}
}
ContextBoundObject
的类,并标注 [AOPContext]
特性,两者配合,使得这个类下的方法可以被成功拦截。需要支持AOP的类,继承这个类即可。 [AOPContext]
public class AOPContext : ContextBoundObject
{
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class AOPMethodAttribute : Attribute
{
}
public class AOPHandler : IMessageSink
{
///
/// 下一个接收器
///
private readonly IMessageSink _nextSink;
public AOPHandler(IMessageSink nextSink)
{
_nextSink = nextSink;
}
public IMessageSink NextSink
{
get { return _nextSink; }
}
///
/// 同步处理方法
///
///
///
public IMessage SyncProcessMessage(IMessage msg)
{
IMessage message = null;
var callMessage = msg as IMethodCallMessage;
if (callMessage != null)
{
var attr = ReflectionUtil.GetAttribute(callMessage.MethodBase as MethodInfo);
if (attr != null)
{
PreProceed(msg);
message = _nextSink.SyncProcessMessage(msg);
PostProceed(message);
}
}
else
{
message = _nextSink.SyncProcessMessage(msg);
}
return message;
}
///
/// 异步处理方法
///
///
///
///
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
return null;
}
///
/// 方法执行前
///
///
///
public void PreProceed(IMessage msg)
{
var message = msg as IMethodMessage;
// 获取到的参数列表 object[]
var paramss = message.Args;
}
///
/// 方法执行后
///
///
///
public void PostProceed(IMessage msg)
{
var message = msg as IMethodReturnMessage;
// 获取到的返回值
var param = message.ReturnValue;
}
}
通过以上的代码,我们已经可以做到,自定义一个功能模块类,然后让其继承 AOPContext
类,并在类中某些方法上标注 [AOPMethod]
,就可以在改方法执行前,从 AOPHandler
中获取到这个方法的参数,并在方法之行结束后,获取到这个方法的返回值。
这只是一段测试代码,应用到具体业务,肯定需要编写很多诸如 AuthCheckAttribute
AuthCheckHandler
来实现权限验证,LogAttribute
LogHandler
来实现日志记录。每实现一个通用方法,不仅需要实现对应的逻辑,还需要额外添加很多类,比较麻烦。
尝试做出如下改进,使用特性标注出拦截到消息后的处理类和函数,并使用反射来执行处理函数。
[AttributeUsage(AttributeTargets.Method)]
public sealed class AOPBeforeAttribute : Attribute
{
public string FullClassName;
public string StaticMethodName;
public AOPBeforeAttribute(string fullClassName, string staticMethodName)
{
FullClassName = fullClassName;
StaticMethodName = staticMethodName;
}
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class AOPAfterAttribute : Attribute
{
public string FullClassName;
public string StaticMethodName;
public AOPAfterAttribute(string fullClassName, string staticMethodName)
{
FullClassName = fullClassName;
StaticMethodName = staticMethodName;
}
}
public class AOPHandler : IMessageSink
{
///
/// 下一个接收器
///
private readonly IMessageSink _nextSink;
public AOPHandler(IMessageSink nextSink)
{
_nextSink = nextSink;
}
public IMessageSink NextSink
{
get { return _nextSink; }
}
///
/// 同步处理方法
///
///
///
public IMessage SyncProcessMessage(IMessage msg)
{
IMessage message = null;
var callMessage = msg as IMethodCallMessage;
if (callMessage != null)
{
// Before
var before = ReflectionUtil.GetAttribute(callMessage.MethodBase as MethodInfo);
if (before != null)
{
PreProceed(msg, before);
}
// Invoke
message = _nextSink.SyncProcessMessage(msg);
// After
var after = ReflectionUtil.GetAttribute(callMessage.MethodBase as MethodInfo);
if (after != null)
{
PostProceed(message, after);
}
}
else
{
message = _nextSink.SyncProcessMessage(msg);
}
return message;
}
///
/// 异步处理方法
///
///
///
///
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
return null;
}
///
/// 方法执行前
///
///
///
public void PreProceed(IMessage msg, AOPBeforeAttribute before)
{
var message = msg as IMethodMessage;
var type = Assembly.GetCallingAssembly().GetType(before.FullClassName);
var param = message.Args;
type.InvokeMember(before.StaticMethodName, BindingFlags.InvokeMethod, null, null, param);
}
///
/// 方法执行后
///
///
///
public void PostProceed(IMessage msg, AOPAfterAttribute after)
{
var message = msg as IMethodReturnMessage;
var type = Assembly.GetCallingAssembly().GetType(after.FullClassName);
var param = message.ReturnValue;
type.InvokeMember(after.StaticMethodName, BindingFlags.InvokeMethod, null, null, new []{ param });
}
}
此处将拦截函数 Before
After
和功能函数放在了同一个类中,实际使用时可以放在同一主调程序集的任意命名空间和类中。
public class AopTestClass : AOPContext
{
[AOPBefore("AopTestClass", "Before")]
[AOPAfter("AopTestClass", "After")]
public int TestMethod1(int a, int b)
{
Debug.Log("Process Test 1 :" + a + "\t" + b);
return a + b;
}
public static void Before(ref int a, ref int b)
{
Debug.Log("Start :" + a + "\t" + b);
a = 200;
b = 400;
}
public static void After(int result)
{
Debug.Log("End :" + result);
}
}
1.通过这种方法实现AOP的类不能是静态类,但被标注AOP拦截的方法可以是静态方法。
2.反射执行处理函数,有一定性能开销,可以通过缓存函数来减少获取函数的消耗,但执行的损耗无法避免。
AOPHandler
中的 T ReflectionUtil.GetAttribute
接口的具体实现如下:
public static T GetAttribute(MethodInfo method) where T : Attribute
{
var attrs = method.GetCustomAttributes(typeof (T), false);
if (attrs.Length != 0)
{
var attribute = attrs[0] as T;
if (attribute != null)
{
return attribute;
}
}
return null;
}