Background
Recently, when writing a log system, you need to inject log records into the function at runtime with function information. At this time, I thought of using AOP.
Technical Analysis
AOP is divided into Dynamic Injection and Static injection.
Dynamic Injection Mode
1: remoting contextattribute context (poor performance ).
2: Dynamic proxy (reflection). Most AOP frameworks use this method.
3: MVC filter, also reflected.
First, the performance is too poor to consider. Type 2: Dynamic proxies are used in the production environment to keep logs, which is not recommended because the performance consumption is not small. Third: only the UI layer can be used. The other layer is the same as the second layer.
Static injection method (This article focuses on ).
1: Based on IL injection, the loss is negligible. Postsharp adopts this method.
Technical implementation:
1: declare attribute
Public class weavesign: attribute {} public class weaveaction: attribute {} public class log: weaveaction {public static void onactionbefore (methodbase mbbase) {// all information about the mbbase method to be injected var T = mbbase. getparameters (); logmanager. record ();}}
2: Mark the method to be injected
[Log]public static string GetUserName(int userId){ return "Vidar";}
3: il injection (Key Point), using mono. Cecil
Private Static void weave (ienumerable <Assembly> assemblylist) {// assemblylist the Assembly list to be injected. Foreach (VAR item in assemblylist) {var filepath = item. codebase. substring (8, item. codebase. length-8); // read the Assembly var Assembly = assemblydefinition. readassembly (filepath); // obtain the type injection mark of the weavesign type var types = assembly. mainmodule. types. where (n => N. customattributes. any (y => Y. attributetype. resolve (). name = "weavesign"); foreach (VAR type in types) {foreach (VaR method in type. methods) {// obtain weavea VaR attrs = method. customattributes. where (y => Y. attributetype. resolve (). basetype. name = "weaveaction"); foreach (var attr in attrs) {// Restore Type var resolve = ATTR. attributetype. resolve (); // obtain the VaR ilprocessor = method of the il container. body. getilprocessor (); var firstinstruction = ilprocessor. body. instructions. first (); // locate the onactionbefore method of the tag type. VaR onactionbefore = resolve. getmethods (). single (n => N. name = "onactionbefore"); // create a system. reflection. methodbase. the getcurrentmethod () method references var mfreference = assembly. mainmodule. import (typeof (system. reflection. methodbase ). getmethod ("getcurrentmethod"); // inject to Il (call getcurrentmethod, stack) ilprocessor. insertbefore (firstinstruction, ilprocessor. create (Opcodes. call, mfreference); // creates a call (CALL) marking method onactionbefore ilprocessor. insertbefore (firstinstruction, ilprocessor. create (Opcodes. call, onactionbefore) ;}} if (types. any () {// write Assembly assembly. write (filepath );}}}
3: After compilation is successful. The following code is displayed during decompilation.
Il
.method public hidebysig static string GetUserName(int32 userId) cil managed{ .custom instance void TestLibrary.Log::.ctor() .maxstack 1 .locals init ( [0] string str) L_0000: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetCurrentMethod() L_0005: call void TestLibrary.Log::OnActionBefore(class [mscorlib]System.Reflection.MethodBase) L_000a: nop L_000b: ldstr "Vidar" L_0010: stloc.0 L_0011: br.s L_0013 L_0013: ldloc.0 L_0014: ret }
C #
[Log]public static string GetUserName(int userId){ Log.OnActionBefore(MethodBase.GetCurrentMethod()); return "Vidar";}
Supplement:
This article focuses on the core implementation ideas.
Log System Practice (I)-AOP Static injection