0. 前言
上接:AOP有几种实现形式
接下来说说怎么做AOP的demo,先用csharp 说下动静编织和动态编织,有工夫再说点java的对应内容。
第一篇先说Roslyn 怎么做个JIT的AOP demo。
为啥这样讲呢?
理论是因为Roslyn 曾经蕴含了JIT的全副局部,那我也就不用说任何JIT的实现了。(真爽)
所以本篇理论说的是以下这些内容:
怎么引入Roslyn做JIT编译代码
代理模式的AOP是什么样
为什么不举荐在生产环境不做优化就这样玩?
1. JIT编译代码
Roslyn 是.NET的编译器,感兴趣的能够参见文档 https://docs.microsoft.com/en…
实际上Roslyn曾经做的非常简单了,几行代码引入进来就能够编译csharp代码了。
不信咱们就手把手写一个
1.1 引入Roslyn包
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.6.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
</ItemGroup>
1.2 代码转化为语法树
public SyntaxTree ParseToSyntaxTree(string code)
{
var parseOptions = new CSharpParseOptions(LanguageVersion.Latest, preprocessorSymbols: new[] { "RELEASE" });
// 有许多其余配置项,最简略这些就能够了
return CSharpSyntaxTree.ParseText(code, parseOptions);
}
1.3 筹备编译器实例
public CSharpCompilation BuildCompilation(SyntaxTree syntaxTree)
{
var compilationOptions = new CSharpCompilationOptions(
concurrentBuild: true,
metadataImportOptions: MetadataImportOptions.All,
outputKind: OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
allowUnsafe: true,
platform: Platform.AnyCpu,
checkOverflow: false,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default);
// 有许多其余配置项,最简略这些就能够了
var references = AppDomain.CurrentDomain.GetAssemblies()
.Where(i => !i.IsDynamic && !string.IsNullOrWhiteSpace(i.Location))
.Distinct()
.Select(i => MetadataReference.CreateFromFile(i.Location));
// 获取编译时所需用到的dll, 这里咱们间接简略一点 copy 以后执行环境的
return CSharpCompilation.Create("code.cs", new SyntaxTree[] { syntaxTree }, references, compilationOptions);
}
1.4 编译到内存中
public Assembly ComplieToAssembly(CSharpCompilation compilation)
{
using (var stream = new MemoryStream())
{
var restult = compilation.Emit(stream);
if (restult.Success)
{
stream.Seek(0, SeekOrigin.Begin);
return AssemblyLoadContext.Default.LoadFromStream(stream);
}
else
{
throw new Exception(restult.Diagnostics.Select(i => i.ToString()).DefaultIfEmpty().Aggregate((i, j) => i + j));
}
}
}
1.5 测试一下
static void TestJIT()
{
var code = @"
public class HiJ
{
public void Test(string v)
{
System.Console.WriteLine($""Hi, {v}!"");
}
}";
var jit = new Jit();
var syntaxTree = jit.ParseToSyntaxTree(code);
var compilation = jit.BuildCompilation(syntaxTree);
var assembly = jit.ComplieToAssembly(compilation);
// test
foreach (var item in assembly.GetTypes())
{
Console.WriteLine(item.FullName);
item.GetMethod("Test").Invoke(Activator.CreateInstance(item), new object[] { "joker" });
}
}
运行后果:
HiJ
Hi, joker!
就这么简略,你就能够JIT了,想干什么都能够了。
2. 用代理形式实现AOP
2.1 回顾代理是什么
这里独自再说一下代理是什么,
毕竟很多AOP框架或者其余框架都有利用代理的思维,
为什么都要这样玩呢?
很简略,代理就是帮你做雷同事件,并且能够比你做的更多,还一点儿都不动到你原来的代码。
比方如下 实在的class 和代理class 看起来截然不同
但两者的实在的代码可能是这样子的
RealClass:
public class RealClass
{
public virtual int Add(int i, int j)
{
return i + j;
}
}
ProxyClass:
public class ProxyClass : RealClass
{
public override int Add(int i, int j)
{
int r = 0;
i += 7;
j -= 7;
r = base.Add(i, j);
r += 55;
return r;
}
}
所以咱们调用的时候会是这样
2.2 做一个Proxy代码生成器
那么咱们来做一个下面例子中能生成截然不同的ProxyClass 代码生成器
首先,咱们都晓得 csharp 再运行中能够反射获取元数据(反编译出代码也能够做,就是咱们杀鸡用牛刀呢?)
咱们晓得了元数据,就能够拼字符串拼出咱们想要的代码(对,你没看错,咱们拼字符串就够了)
废话不说, show you code
public class ProxyGenerator
{
public string GenerateProxyCode(Type type, Action<StringBuilder, MethodBase> beforeCall, Action<StringBuilder, MethodBase> afterCall)
{
var sb = new StringBuilder();
sb.Append($"{(type.IsPublic ? "public" : "")} class {type.Name}Proxy : {type.Name} {{ ");
foreach (var method in type.GetTypeInfo().DeclaredMethods)
{
GenerateProxyMethod(beforeCall, afterCall, sb, method);
}
sb.Append(" }");
return sb.ToString();
}
private static void GenerateProxyMethod(Action<StringBuilder, MethodBase> beforeCall, Action<StringBuilder, MethodBase> afterCall, StringBuilder sb, MethodInfo method)
{
var ps = method.GetParameters().Select(p => $"{p.ParameterType.FullName} {p.Name}");
sb.Append($"{(method.IsPublic ? "public" : "")} override {method.ReturnType.FullName} {method.Name}({string.Join(",", ps)}) {{");
sb.Append($"{method.ReturnType.FullName} r = default;");
beforeCall(sb, method);
sb.Append($"r = base.{method.Name}({string.Join(",", method.GetParameters().Select(p => p.Name))});");
afterCall(sb, method);
sb.Append("return r; }");
}
}
测试一下
public static class TestProxyGenerator
{
public static void Test()
{
var generator = new ProxyGenerator();
var code = generator.GenerateProxyCode(typeof(RealClass), (sb, method) => { }, (sb, method) => { sb.Append("r++;"); });
Console.WriteLine(code);
}
}
后果:
public class RealClassProxy : RealClass { public override System.Int32 Add(System.Int32 i,System.Int32 j) {System.Int32 r = default;r = base.Add(i,j);r++;return r; } }
2.3 联合在一起测试一下
public static class TestProxyJit
{
public static RealClass GenerateRealClassProxy()
{
var generator = new ProxyGenerator();
var code = generator.GenerateProxyCode(typeof(RealClass), (sb, method) => { }, (sb, method) => { sb.Append("r++;"); });
var jit = new Jit();
var syntaxTree = jit.ParseToSyntaxTree(code);
var compilation = jit.BuildCompilation(syntaxTree);
var assembly = jit.ComplieToAssembly(compilation);
return Activator.CreateInstance(assembly.GetTypes().First()) as RealClass;
}
public static void Test()
{
RealClass proxy = GenerateRealClassProxy();
var i = 5;
var j = 10;
Console.WriteLine($"{i} + {j} = {(i + j)}, but proxy is {proxy.Add(i, j)}");
}
}
后果为:
5 + 10 = 15, but proxy is 16
是的,咱们写了这么多代码就是为了让 15 变成 16 ,让他人不晓得 多了个 r++; ,这就是AOP的意义
残缺的demo 放在 https://github.com/fs7744/Aop…
2.4 再欠缺欠缺就能够了。。。(也就再写个几年)
你只须要欠缺如下:
- 反对 void 办法
- 反对 async await 办法
- 反对抽象类
- 反对接口
- 反对构造方法
- 反对属性
- 反对索引器
- 反对 in out ref
- 反对泛型
- 反对嵌套类
- 反对剩下的各种各样状况
嗯,置信你本人,你能够的
3. 不举荐在生产环境不通过优化就这样玩,为什么?
3.1 两幅图
手写的proxy :
jit 编译proxy:
随着须要编译的Proxy class 增多, cpu 和 内存都会一样增多
所以要应用呢,最好用一些优化过的计划,状况会好的多,比方 https://github.com/dotnetcore/Natasha
3.2 你信不信得过调用你api的对方
嗯,这是一个信任度的问题,所以不要调用 Roslyn sdk 的输出裸露不做校验,黑客的骚操作是大家永远想不完的
只有对方可信,Roslyn sdk api 他人是不会调用的哦
然而咱们都晓得前人们都通知咱们有个准则:不要置信任何Input。
所以大家的猫奴才会不会跟着他人跑掉就看大家信念和伎俩了。
发表回复