本文转载自微信公众号「UP技术控」,作者conan5566。转载本文请联系UP技术控公众号。
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点……是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
动态代理是实现AOP的一种方式,即在开发过程中我们不需要处理切面中(日志等)的工作,而是在运行时,通过动态代理来自动完成。Castle DynamicProxy是一个实现动态代理的框架,被很多优秀的项目用来实现AOP编程,EF Core、Autofac等。
为业务类添加AOP拦截器
- /// <summary>
- /// 为业务类添加AOP拦截器。
- /// </summary>
- public class InterceptorAttribute:PlutoStudio.Aop.InterceptorAttribute
- {
- /// <summary>
- /// 拦截方法的执行,如果当前方法有拦截处理器,则执行处理器。
- /// </summary>
- /// <param name="invocation">被拦截的调用目标对象</param>
- public override void Intercept(IInvocation invocation)
- {
- var method = invocation.Method;
- var processors = method.GetCustomAttributes(typeof(IInterceptorProcessor),true).Cast<IInterceptorProcessor>().ToList();
- processors.ForEach(p => PlutoStudio.MefContainer.Container.ComposeParts(p));
- if (processors.Count>0)
- {
- processors.ForEach(p => p.PreProcess(invocation));
- try
- {
- invocation.Proceed();
- processors.ForEach(p => p.PostProcess(invocation, null));
- }
- catch (Exception ex)
- {
- processors.ForEach(p => p.PostProcess(invocation, ex));
- throw;
- }
- }
- else
- {
- invocation.Proceed();
- }
- }
- }
- /// <summary>
- /// 拦截器处理器接口。
- /// </summary>
- public interface IInterceptorProcessor
- {
- /// <summary>
- /// 拦截器处理方法,在目标方法执行前执行。
- /// </summary>
- /// <param name="invocation">拦截的目标对象</param>
- void PreProcess(IInvocation invocation);
- /// <summary>
- /// 拦截器处理方法,在目标方法执行后执行。
- /// </summary>
- /// <param name="invocation">拦截的目标对象</param>
- /// <param name="ex">目标方法的异常</param>
- void PostProcess(IInvocation invocation,Exception ex);
- }
日志处理器
可以将目标方法的信息保存到日志系统中
- /// <summary>
- /// 日志处理器,可以将目标方法的信息保存到日志系统中。
- /// </summary>
- [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
- public class LogAttribute : Attribute, IInterceptorProcessor
- {
- [Import(AllowDefault = true)]
- public ILog Log { get; set; }
- /// <summary>
- /// 在目标方法执行完成后执行,这里会记录目标方法的相关信息并写入日志系统。
- /// </summary>
- /// <param name="invocation"></param>
- /// <param name="ex"></param>
- public void PostProcess(IInvocation invocation, Exception ex)
- {
- if (Log != null)
- {
- var @class = invocation.TargetType.FullName;
- var method = invocation.Method.Name;
- var parameterNames = invocation.Method.GetParameters().Select(p => p.Name).ToList();
- var args = invocation.Arguments;
- var parameters = new Dictionary<string, object>();
- for (int i = 0; i < parameterNames.Count; i++)
- {
- parameters.Add(parameterNames[i], args[i]);
- }
- var returnValue = invocation.ReturnValue;
- var stackTrace = new StackTrace(true);
- var stacks = stackTrace.GetFrames();
- var stack = stacks.SkipWhile(i => i.GetMethod().Name != invocation.Method.Name).Select(GetStack);
- var log = new TraceLog
- {
- Class = @class,
- Method = method,
- Parameter = parameters,
- ReturnValue = returnValue,
- Strack = stack,
- Exception = ex,
- };
- Log.Custom(log);
- }
- }
- private object GetStack(StackFrame frame)
- {
- var method = frame.GetMethod();
- var type = method.ReflectedType;
- if (type.FullName.StartsWith("Castle.Proxies"))
- {
- type = type.BaseType;
- }
- return new
- {
- Method = method.Name,
- Type = type.FullName,
- File = frame.GetFileName(),
- Line = frame.GetFileLineNumber(),
- };
- }
- public void PreProcess(IInvocation invocation)
- {
- }
- }
- /// <summary>
- /// 系统跟踪日志,由<see cref="LogAttribute"/>生成。
- /// </summary>
- public class TraceLog
- {
- /// <summary>
- /// 当前日志记录的目标类。
- /// </summary>
- public string Class { get; internal set; }
- /// <summary>
- /// 当前日志跟踪到的异常。
- /// </summary>
- public Exception Exception { get; internal set; }
- /// <summary>
- /// 当前日志记录的目标方法。
- /// </summary>
- public string Method { get; internal set; }
- /// <summary>
- /// 当前日志记录的目标方法的参数。
- /// </summary>
- public Dictionary<string, object> Parameter { get; internal set; }
- /// <summary>
- /// 当前日志记录的目标方法的返回值。
- /// </summary>
- public object ReturnValue { get; internal set; }
- /// <summary>
- /// 当前日志记录的目标方法的调用栈。
- /// </summary>
- public IEnumerable<object> Strack { get; internal set; }
- }