AOP有幾種實現方式?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"0. 前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"副標題:"},{"type":"text","text":"無價值人生記錄.0:浪費 1000% 時間去做一個用來節省 1% 時間的“輪子玩具”(中:AOP回顧)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上接:"},{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/5d29fbc6edc2bbdae68a5880c","title":null},"content":[{"type":"text","text":"https://xie.infoq.cn/article/5d29fbc6edc2bbdae68a5880c"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面說的是我爲什麼想做這個aop。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來說說aop是啥,怎麼搞它。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"1. 回顧 AOP 是什麼?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"維基百科解釋如下:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"面向切面的程序設計"},{"type":"text","text":"(Aspect-oriented programming,AOP,又譯作"},{"type":"text","marks":[{"type":"strong"}],"text":"面向方面的程序設計"},{"type":"text","text":"、"},{"type":"text","marks":[{"type":"strong"}],"text":"剖面導向程序設計"},{"type":"text","text":")是"},{"type":"link","attrs":{"href":"https://zh.wikipedia.org/wiki/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6","title":"計算機科學"},"content":[{"type":"text","text":"計算機科學"}]},{"type":"text","text":"中的一種"},{"type":"link","attrs":{"href":"https://zh.wikipedia.org/wiki/%E7%BC%96%E7%A8%8B%E8%8C%83%E5%9E%8B","title":"編程思想"},"content":[{"type":"text","text":"程序設計思想"}]},{"type":"text","text":",旨在將"},{"type":"text","marks":[{"type":"strong"}],"text":"橫切關注點"},{"type":"text","text":"與業務主體進行進一步分離,以提高程序代碼的模塊化程度。通過在現有代碼基礎上增加額外的"},{"type":"text","marks":[{"type":"strong"}],"text":"通知"},{"type":"text","text":"(Advice)機制,能夠對被聲明爲“"},{"type":"text","marks":[{"type":"strong"}],"text":"切點"},{"type":"text","text":"(Pointcut)”的代碼塊進行統一管理與裝飾,如“對所有方法名以‘set*’開頭的方法添加後臺日誌”。該思想使得開發人員能夠將與代碼核心業務邏輯關係不那麼密切的功能(如日誌功能)添加至程序中,同時又不降低業務代碼的可讀性。面向切面的程序設計思想也是面向切面軟件開發的基礎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"面向切面的程序設計將代碼邏輯切分爲不同的模塊(即"},{"type":"text","marks":[{"type":"strong"}],"text":"關注點"},{"type":"text","text":"(Concern),一段特定的邏輯功能)。幾乎所有的編程思想都涉及代碼功能的分類,將各個關注點封裝成獨立的抽象模塊(如函數、過程、模塊、類以及方法等),後者又可供進一步實現、封裝和重寫。部分關注點“橫切”程序代碼中的數個模塊,即在多個模塊中都有出現,它們即被稱作“"},{"type":"text","marks":[{"type":"strong"}],"text":"橫切關注點"},{"type":"text","text":"(Cross-cutting concerns, Horizontal concerns)”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"日誌功能即是橫切關注點的一個典型案例,因爲日誌功能往往橫跨系統中的每個業務模塊,即“橫切”所有有日誌需求的類及方法體。而對於一個信用卡應用程序來說,存款、取款、帳單管理是它的核心關注點,日誌和持久化將成爲橫切整個對象結構的橫切關注點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參見: "},{"type":"link","attrs":{"href":"https://zh.wikipedia.org/wiki/%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%9A%84%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1","title":null},"content":[{"type":"text","text":"https://zh.wikipedia.org/wiki/%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%9A%84%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單來說,就是功能上我們要加其他感覺和原本功能無關的邏輯,比如性能日誌,代碼混在一起,看着不爽,影響我們理解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉個例子, 如下代碼我們要多花幾眼時間才能看明白:"}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":" public int doAMethod(int n)\n {\n int sum = 0;\n for (int i = 1; i <= n; i++)\n {\n if (n % i == 0)\n {\n sum += 1;\n }\n }\n if (sum == 2)\n {\n return sum;\n }\n else\n {\n return -1;\n }\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後我們需要記錄一系列日誌,就會變成這樣子:"}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":" public int doAMethod(int n,Logger logger, HttpContext c, .....)\n {\n log.LogInfo($\" n is {n}.\");\n log.LogInfo($\" who call {c.RequestUrl}.\");\n log.LogInfo($\" QueryString {c.QueryString}.\");\n log.LogInfo($\" Ip {c.Ip}.\");\n log.LogInfo($\" start {Datetime.Now}.\");\n int sum = 0;\n for (int i = 1; i <= n; i++)\n {\n if (n % i == 0)\n {\n sum += 1;\n }\n }\n if (sum == 2)\n {\n return sum;\n }\n else\n {\n return -1;\n }\n log.LogInfo($\" end {Datetime.Now}.\");\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一下子這個方法就複雜多了,至少調用它還得找一堆貌似和方法無關的參數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AOP 的想法就是把上述方法拆分開, 讓log之類的方法不在我們眼中:"}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":" public int doAMethod(int n)\n {\n int sum = 0;\n for (int i = 1; i <= n; i++)\n {\n if (n % i == 0)\n {\n sum += 1;\n }\n }\n if (sum == 2)\n {\n return sum;\n }\n else\n {\n return -1;\n }\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AOP 讓看着只調用的 doAMethod 方法實際爲:"}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":" public int doAMethodWithAOP(int n,Logger logger, HttpContext c, .....)\n {\n log.LogInfo($\" n is {n}.\");\n log.LogInfo($\" who call {c.RequestUrl}.\");\n log.LogInfo($\" QueryString {c.QueryString}.\");\n log.LogInfo($\" Ip {c.Ip}.\");\n log.LogInfo($\" start {Datetime.Now}.\");\n return doAMethod(n);\n log.LogInfo($\" end {Datetime.Now}.\");\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以AOP 實際就是幹這個事情,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無論語言,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無論實現,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實只要幹這個事不就是AOP嗎?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"2. 類似AOP想法的實現方式分類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"達到AOP要做的這種事情有很多種方法,下面來做個簡單分類,不一定很全面哦"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 按照方式"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.1 元編程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很多語言都有內置類似這樣一些“增強代碼”的功能,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般來說,從安全性和編譯問題等角度考慮,大多數元編程都"},{"type":"text","marks":[{"type":"strong"}],"text":"只允許新增代碼,不允許修改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種都是編譯器必須有才能做到。("},{"type":"text","marks":[{"type":"italic"},{"type":"del"}],"text":"沒有的,你也可以自己寫個編譯器,只要你做的到)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然元編程的概念不僅僅可以用來做類似AOP的事情,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還可以做各種你想做的事情,"},{"type":"text","marks":[{"type":"italic"},{"type":"del"}],"text":"(只要在限制範圍內能做的)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下的例子就是生成一些新的方法。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"宏"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如 Rust / C++ 等等都具有這樣的功能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如 Rust 的文檔:"},{"type":"link","attrs":{"href":"https://doc.rust-lang.org/stable/book/ch19-06-macros.html","title":null},"content":[{"type":"text","text":"https://doc.rust-lang.org/stable/book/ch19-06-macros.html"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"rust"},"content":[{"type":"text","text":"use hello_macro::HelloMacro;\nuse hello_macro_derive::HelloMacro;\n\n#[derive(HelloMacro)]\nstruct Pancakes;\n\nfn main() {\n Pancakes::hello_macro();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"宏實現"}]},{"type":"codeblock","attrs":{"lang":"rust"},"content":[{"type":"text","text":"extern crate proc_macro;\n\nuse crate::proc_macro::TokenStream;\nuse quote::quote;\nuse syn;\n\n#[proc_macro_derive(HelloMacro)]\npub fn hello_macro_derive(input: TokenStream) -> TokenStream {\n let ast = syn::parse(input).unwrap();\n impl_hello_macro(&ast)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"csharp 的 Source Generators"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新的實驗特性,還在設計修改變化中"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"官方文檔: "},{"type":"link","attrs":{"href":"https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.md","title":null},"content":[{"type":"text","text":"https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.md"}]}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":"public partial class ExampleViewModel\n{\n [AutoNotify]\n private string _text = \"private field text\";\n\n [AutoNotify(PropertyName = \"Count\")]\n private int _amount = 5;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生成器實現"}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":"using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\nusing Microsoft.CodeAnalysis.Text;\n\nnamespace Analyzer1\n{\n [Generator]\n public class AutoNotifyGenerator : ISourceGenerator\n {\n private const string attributeText = @\"\nusing System;\nnamespace AutoNotify\n{\n [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]\n sealed class AutoNotifyAttribute : Attribute\n {\n public AutoNotifyAttribute()\n {\n }\n public string PropertyName { get; set; }\n }\n}\n\";\n\n public void Initialize(InitializationContext context)\n {\n // Register a syntax receiver that will be created for each generation pass\n context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());\n }\n\n public void Execute(SourceGeneratorContext context)\n {\n // add the attribute text\n context.AddSource(\"AutoNotifyAttribute\", SourceText.From(attributeText, Encoding.UTF8));\n\n // retreive the populated receiver \n if (!(context.SyntaxReceiver is SyntaxReceiver receiver))\n return;\n\n // we're going to create a new compilation that contains the attribute.\n // TODO: we should allow source generators to provide source during initialize, so that this step isn't required.\n CSharpParseOptions options = (context.Compilation as CSharpCompilation).SyntaxTrees[0].Options as CSharpParseOptions;\n Compilation compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(attributeText, Encoding.UTF8), options));\n\n // get the newly bound attribute, and INotifyPropertyChanged\n INamedTypeSymbol attributeSymbol = compilation.GetTypeByMetadataName(\"AutoNotify.AutoNotifyAttribute\");\n INamedTypeSymbol notifySymbol = compilation.GetTypeByMetadataName(\"System.ComponentModel.INotifyPropertyChanged\");\n\n // loop over the candidate fields, and keep the ones that are actually annotated\n List fieldSymbols = new List();\n foreach (FieldDeclarationSyntax field in receiver.CandidateFields)\n {\n SemanticModel model = compilation.GetSemanticModel(field.SyntaxTree);\n foreach (VariableDeclaratorSyntax variable in field.Declaration.Variables)\n {\n // Get the symbol being decleared by the field, and keep it if its annotated\n IFieldSymbol fieldSymbol = model.GetDeclaredSymbol(variable) as IFieldSymbol;\n if (fieldSymbol.GetAttributes().Any(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default)))\n {\n fieldSymbols.Add(fieldSymbol);\n }\n }\n }\n\n // group the fields by class, and generate the source\n foreach (IGrouping group in fieldSymbols.GroupBy(f => f.ContainingType))\n {\n string classSource = ProcessClass(group.Key, group.ToList(), attributeSymbol, notifySymbol, context);\n context.AddSource($\"{group.Key.Name}_autoNotify.cs\", SourceText.From(classSource, Encoding.UTF8));\n }\n }\n\n private string ProcessClass(INamedTypeSymbol classSymbol, List fields, ISymbol attributeSymbol, ISymbol notifySymbol, SourceGeneratorContext context)\n {\n if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))\n {\n return null; //TODO: issue a diagnostic that it must be top level\n }\n\n string namespaceName = classSymbol.ContainingNamespace.ToDisplayString();\n\n // begin building the generated source\n StringBuilder source = new StringBuilder($@\"\nnamespace {namespaceName}\n{{\n public partial class {classSymbol.Name} : {notifySymbol.ToDisplayString()}\n {{\n\");\n\n // if the class doesn't implement INotifyPropertyChanged already, add it\n if (!classSymbol.Interfaces.Contains(notifySymbol))\n {\n source.Append(\"public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;\");\n }\n\n // create properties for each field \n foreach (IFieldSymbol fieldSymbol in fields)\n {\n ProcessField(source, fieldSymbol, attributeSymbol);\n }\n\n source.Append(\"} }\");\n return source.ToString();\n }\n\n private void ProcessField(StringBuilder source, IFieldSymbol fieldSymbol, ISymbol attributeSymbol)\n {\n // get the name and type of the field\n string fieldName = fieldSymbol.Name;\n ITypeSymbol fieldType = fieldSymbol.Type;\n\n // get the AutoNotify attribute from the field, and any associated data\n AttributeData attributeData = fieldSymbol.GetAttributes().Single(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default));\n TypedConstant overridenNameOpt = attributeData.NamedArguments.SingleOrDefault(kvp => kvp.Key == \"PropertyName\").Value;\n\n string propertyName = chooseName(fieldName, overridenNameOpt);\n if (propertyName.Length == 0 || propertyName == fieldName)\n {\n //TODO: issue a diagnostic that we can't process this field\n return;\n }\n\n source.Append($@\"\npublic {fieldType} {propertyName} \n{{\n get \n {{\n return this.{fieldName};\n }}\n\n set\n {{\n this.{fieldName} = value;\n this.PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(nameof({propertyName})));\n }}\n}}\n\n\");\n\n string chooseName(string fieldName, TypedConstant overridenNameOpt)\n {\n if (!overridenNameOpt.IsNull)\n {\n return overridenNameOpt.Value.ToString();\n }\n\n fieldName = fieldName.TrimStart('_');\n if (fieldName.Length == 0)\n return string.Empty;\n\n if (fieldName.Length == 1)\n return fieldName.ToUpper();\n\n return fieldName.Substring(0, 1).ToUpper() + fieldName.Substring(1);\n }\n\n }\n\n /// \n /// Created on demand before each generation pass\n /// \n class SyntaxReceiver : ISyntaxReceiver\n {\n public List CandidateFields { get; } = new List();\n\n /// \n /// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation\n /// \n public void OnVisitSyntaxNode(SyntaxNode syntaxNode)\n {\n // any field with at least one attribute is a candidate for property generation\n if (syntaxNode is FieldDeclarationSyntax fieldDeclarationSyntax\n && fieldDeclarationSyntax.AttributeLists.Count > 0)\n {\n CandidateFields.Add(fieldDeclarationSyntax);\n }\n }\n }\n }\n}"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.2 修改代碼"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"代碼文件修改"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般來說,很少有這樣實現的,代碼文件都改了,我們碼農還怎麼寫bug呀。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"中間語言修改"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有很多語言編譯的結果並不是直接的機器碼,而是優化後的一個接近底層的中間層語言,方便擴展支持不同cpu,不同機器架構。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如 dotnet 的 IL "}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":".class private auto ansi ''\n{\n} // end of class \n\n.class public auto ansi beforefieldinit C\n extends [mscorlib]System.Object\n{\n // Fields\n .field private initonly int32 'k__BackingField'\n .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (\n 01 00 00 00\n )\n\n // Methods\n .method public hidebysig specialname rtspecialname \n instance void .ctor () cil managed \n {\n // Method begins at RVA 0x2050\n // Code size 21 (0x15)\n .maxstack 8\n\n IL_0000: ldarg.0\n IL_0001: ldc.i4.5\n IL_0002: stfld int32 C::'k__BackingField'\n IL_0007: ldarg.0\n IL_0008: call instance void [mscorlib]System.Object::.ctor()\n IL_000d: ldarg.0\n IL_000e: ldc.i4.4\n IL_000f: stfld int32 C::'k__BackingField'\n IL_0014: ret\n } // end of method C::.ctor\n\n .method public hidebysig specialname \n instance int32 get_x () cil managed \n {\n .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (\n 01 00 00 00\n )\n // Method begins at RVA 0x2066\n // Code size 7 (0x7)\n .maxstack 8\n\n IL_0000: ldarg.0\n IL_0001: ldfld int32 C::'k__BackingField'\n IL_0006: ret\n } // end of method C::get_x\n\n // Properties\n .property instance int32 x()\n {\n .get instance int32 C::get_x()\n }\n\n} // end of class C\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如 java 的字節碼 (反編譯的結果)"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Classfile /E:/JavaCode/TestProj/out/production/TestProj/com/rhythm7/Main.class\n Last modified 2018-4-7; size 362 bytes\n MD5 checksum 4aed8540b098992663b7ba08c65312de\n Compiled from \"Main.java\"\npublic class com.rhythm7.Main\n minor version: 0\n major version: 52\n flags: ACC_PUBLIC, ACC_SUPER\nConstant pool:\n #1 = Methodref #4.#18 // java/lang/Object.\"\":()V\n #2 = Fieldref #3.#19 // com/rhythm7/Main.m:I\n #3 = Class #20 // com/rhythm7/Main\n #4 = Class #21 // java/lang/Object\n #5 = Utf8 m\n #6 = Utf8 I\n #7 = Utf8 \n #8 = Utf8 ()V\n #9 = Utf8 Code\n #10 = Utf8 LineNumberTable\n #11 = Utf8 LocalVariableTable\n #12 = Utf8 this\n #13 = Utf8 Lcom/rhythm7/Main;\n #14 = Utf8 inc\n #15 = Utf8 ()I\n #16 = Utf8 SourceFile\n #17 = Utf8 Main.java\n #18 = NameAndType #7:#8 // \"\":()V\n #19 = NameAndType #5:#6 // m:I\n #20 = Utf8 com/rhythm7/Main\n #21 = Utf8 java/lang/Object\n{\n private int m;\n descriptor: I\n flags: ACC_PRIVATE\n\n public com.rhythm7.Main();\n descriptor: ()V\n flags: ACC_PUBLIC\n Code:\n stack=1, locals=1, args_size=1\n 0: aload_0\n 1: invokespecial #1 // Method java/lang/Object.\"\":()V\n 4: return\n LineNumberTable:\n line 3: 0\n LocalVariableTable:\n Start Length Slot Name Signature\n 0 5 0 this Lcom/rhythm7/Main;\n\n public int inc();\n descriptor: ()I\n flags: ACC_PUBLIC\n Code:\n stack=2, locals=1, args_size=1\n 0: aload_0\n 1: getfield #2 // Field m:I\n 4: iconst_1\n 5: iadd\n 6: ireturn\n LineNumberTable:\n line 8: 0\n LocalVariableTable:\n Start Length Slot Name Signature\n 0 7 0 this Lcom/rhythm7/Main;\n}\nSourceFile: \"Main.java\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它們也是編程語言的一種,也是可以寫的,所以我們可以用來把別人方法體改了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然怎麼改,怎麼改得各種各樣方法都兼容,做的人簡直👍"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"生成代理代碼"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不修改原來的代碼文件,新增代理代碼實現"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不修改編譯好的IL 或 字節碼等,往裏面添加IL或字節碼等形式代理代碼"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.3 利用編譯器或者運行時的功能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般來說,也是利用編譯器自身提供得擴展功能做擴展"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"java的 AspectJ 好像就可以利用了ajc編譯器做事情"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.4 利用運行時功能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"理論上 dotnet 也可以實現CLR Profiling API 在JIT編譯時修改method body。實現真正無任何限制的運行時靜態AOP (不過貌似得用C++才能做CLR Profiling API,文檔少,兼容貌似也挺難做的)"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 按照編織時機"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.1 編譯前"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改掉別人的代碼文件"},{"type":"text","marks":[{"type":"italic"},{"type":"del"}],"text":"(找死)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生成新的代碼,讓編譯器編譯進去,運行時想辦法用新的代碼"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.2 編譯時"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"元編程"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"做個編譯器"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.3 編譯後靜態編織一次"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據編譯好的東西(dotnet的dll或者其他語言的東西)利用反射,解析等技術生成代理實現,然後塞進去"}]}]}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.4 運行時"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"嚴格來說,運行時也是編譯後"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過不是再編織一次,而是每次運行都編織"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"並且沒有什麼 前中後了,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"都是程序啓動後,在具體類執行之前,把這個類編織了"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如java 的 類加載器:在目標類被裝載到JVM時,通過一個特殊的類加載器,對目標類的字節碼重新“增強。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"具有aop功能的各類 IOC 容器在生成實例前創建代理實例"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實也可以在註冊IOC容器時替換爲代理類型"}]}]}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"3. 代理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏單獨再說一下代理是什麼,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"畢竟很多AOP框架或者其他框架都有利用代理的思想,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼都要這樣玩呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很簡單,代理就是幫你做相同事情,並且可以比你做的更多,還一點兒都不動到你原來的代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如如下 真實的class 和代理class 看起來一模一樣"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3d/3dc1cede74ab1f4aeaf29cdb2c0bc613.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但兩者的真實的代碼可能是這樣子的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RealClass:"}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":"public class RealClass\n{\n public virtual int Add(int i, int j)\n {\n return i + j;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ProxyClass:"}]},{"type":"codeblock","attrs":{"lang":"csharp"},"content":[{"type":"text","text":"public class ProxyClass : RealClass\n{\n public override int Add(int i, int j)\n {\n int r = 0;\n i += 7;\n j -= 7;\n r = base.Add(i, j);\n r += 55;\n return r;\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我們調用的時候會是這樣"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d3/d3daa39e6b7a52fe41b6f457658cf8e7.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下一篇介紹一下 Roslyn 的 source generator 怎麼做Aop"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章