【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
: 被拦截方法的泛型参数类型数组,如果没有就是nullinvocation.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));
}
}
}