【Autofac笔记】DynamicProxy

介绍

DynamicProxy(以下称为动态代理)起作用主要是为我们的类生成一个代理类,这个代理类可以在我们调用原本类的方法之前,调用拦截器以实现AOP。那么动态代理是怎么实现的呢,这里简单一下提一下,这里主要是用了emit技术动态生成IL,相当于在内存中用IL给我们编写了一个Class。

安装Package

Autofac
Autofac.Extras.DynamicProxy

如何使用

首先我们需要定义一个拦截器:

public interface ICat
{
    void Eat();
}
public class Cat:ICat
{
    public void Eat()
    {
        Console.WriteLine("猫在吃东西");
    }
}
public class CatInterceptor:IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine("猫吃东西之前");
        invocation.Proceed();
        Console.WriteLine("猫吃东西之后");
    }
}

然后在Autofac容器中注册我们的拦截器和类型:

static void Main(string[] args)
{
    var builder = new ContainerBuilder();

    builder.RegisterType<CatInterceptor>();//注册拦截器
    builder.RegisterType<Cat>().As<ICat>().InterceptedBy(typeof(CatInterceptor)).EnableInterfaceInterceptors();//注册Cat并为其添加拦截器

    var container = builder.Build();
    var cat = container.Resolve<ICat>();
    cat.Eat();

    Console.Read();
}

输出:

猫吃东西之前
猫在吃东西
猫吃东西之后

我们还可以通过Attribute的方式绑定到我们的具体类型,而不需要在注册到容器的时候动态指定。

[Intercept(typeof(CatInterceptor))]
public class Cat:ICat
{
    public void Eat()
    {
        Console.WriteLine("猫在吃东西");
    }
}

注册的代码可改为:

builder.RegisterType<Cat>().As<ICat>().EnableInterfaceInterceptors();

IInvocation对象

  • invocation.Arguments: 方法执行被拦截时的方法参数数组
  • invocation.GenericArguments: 被拦截方法的泛型参数类型数组,如果没有就是null
  • invocation.InvocationTarget: 获取当前执行的目标对象,例如:如果是class代理就是YourClassProxy,接口代理就是实现接口的对象实例,如果没有则为null,也就是当使用xxxWithoutTarget的时候
  • invocation.Method?:获取代理对象的方法信息,例如:如果是class代理的时候就是YourClass.YourMethod对象的MethodInfo且这个时候invocation.Method == invocation.MethodInvocationTarget;如果是interface代理就是接口对象的方法信息,例如:ICall.Call 这个方法的MethodInfo信息且这个时候invocation.Method != invocation.MethodInvocationTarget,因为invocation.MethodInvocationTarget是接口对应实现的目标对象的方法信息,也就是例如:MyCall.Call 方法对应上面的 ICall 接口来说,当然也可以使用 WithoutTarget方式,这样就会导致 invocation.MethodInvocationTarget==null的情况
  • invocation.MethodInvocationTarget: 指向真正的目标对象的方法信息MethodInfo,大致可以根据第四点给出了说明
  • invocation.Proxy : 获取拦截时的代理对象
  • invocation.ReturnValue: 获取或者设置代理方法的返回值
  • invocation.TargetType: 获取真实目标对象的类型
  • invocation.GetArgumentValue(int index): 通过index获取参数值
  • invocation.GetConcreteMethod(): 同理第四点
  • invocation.GetConcreteMethodInvocationTarget(): 同理第五点
  • invocation.Proceed(): 调用下一个拦截器直到目标方法
  • invocation.SetArgumentValue(int index, object value): 设置更改参数值通过下标

案例

缓存

public class MethodCacheInterceptor : IInterceptor
{
    private readonly IMemoryCache _memoryCache;
    private readonly ILogger<MethodCacheInterceptor> _logger;

    public MethodCacheInterceptor(IMemoryCache memoryCache,ILogger<MethodCacheInterceptor> logger)
    {
        this._memoryCache = memoryCache;
        this._logger = logger;
    }
    public void Intercept(IInvocation invocation)
    {
        var key = invocation.Method.Name+"_"+string.Join('_', invocation.Arguments
            .Where(arg=>arg.GetType()!=typeof(CancellationToken))
            .Select(arg => $"{arg.GetType().Name}({arg.ToString()})"));
        object? returnValue = _memoryCache.Get(key);
        if (returnValue == null)
        {
            invocation.Proceed();
            if (invocation.ReturnValue!=null)
            {
                _logger.LogInformation($"设置缓存,key:{key},value:{invocation.ReturnValue}");
                _memoryCache.Set(key, invocation.ReturnValue,DateTimeOffset.UtcNow.AddMinutes(10));
            }
        }
        else
        {
            //_logger.LogInformation($"读取缓存,key:{key},value:{returnValue}");
            invocation.ReturnValue = returnValue;
        }
    }
}

HTTPCacheManager:用于存储当前请求缓存,最终存储到HttpContext.Items中
当前http请求中,如果被代理的方法被调用过,下次将不会再被调用

记录DAL调用日志

public class DALInterceptor : IInterceptor
    {
        /// <summary>
        /// Account_AdminDAL 日志对象
        /// </summary>
        private ILogger<DALInterceptor> Logger { get; }

        public DALInterceptor(ILogger<DALInterceptor> logger)
        {
            this.Logger = logger;
        }

        public void Intercept(IInvocation invocation)
        {
            string methodName = invocation.TargetType.FullName + "." + invocation.Method.Name;
            var args = string.Join("; ", invocation.Arguments.Select(arg =>
            {
                string result = null;
                if (arg == null)
                {
                    result = string.Empty;
                }
                else if (arg is string)
                {//字符串类型
                    result = arg as string;
                }
                else if (arg is IEnumerable<KeyValuePair<string, object>>)
                {//字典类型<string, object>
                    result = string.Join(", ", (arg as IEnumerable<KeyValuePair<string, object>>).Select(pair => pair.Key + "=" + pair.Value?.ToString()));
                }
                else if (arg is IEnumerable)
                {//集合类型
                    result = string.Join(", ", (arg as IEnumerable).Cast<object>().Select(item => item?.ToString()));
                }
                else
                {//其它类型
                    result = arg.ToString();
                }
                return result;
            }).ToArray());
            string[] logInfos = new string[2];
            logInfos[0] = string.Format("calling method=>\"{1}\" {0} \t\t arguments=>[{2}]"
                                          , Environment.NewLine
                                          , methodName
                                          , args);
            Stopwatch stopwatch = Stopwatch.StartNew();
            try
            {
                invocation.Proceed();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                stopwatch.Stop();
                string useTime = stopwatch.Elapsed.ToString(@"s\.fff");
                object returnValue = invocation.ReturnValue;
                if (returnValue is ICollection)
                {
                    returnValue = "ICollection.Count=" + (returnValue as ICollection).Count;
                }
                logInfos[1] = string.Format("\tcalled  method=>\"{1}\",use=>{2} seconds {0}\t\t\tresult=>{3}"
                                                , Environment.NewLine
                                                , methodName
                                                , useTime
                                                , returnValue);
                this.Logger?.LogInformation(string.Join(Environment.NewLine, logInfos));
            }
        }
    }

参考:https://www.cnblogs.com/stulzq/p/8547839.html

posted @ 2019-12-26 08:58  .Neterr  阅读(1152)  评论(0编辑  收藏  举报