码迷,mamicode.com
首页 > 其他好文 > 详细

日志系统实战(一)-AOP静态注入

时间:2014-08-24 14:06:52      阅读:262      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   os   io   strong   for   ar   div   

 

背景

近期在写日志系统,需要在运行时在函数内注入日志记录,并附带函数信息。这时候就想到用Aop的方式了。

技术分析

AOP分动态注入和静态注入。

动态注入方式

1:Remoting的ContextAttribute上下文(性能差)。

2:动态代理(反射),大多AOP框架都用这种方式。

3:MVC的filter,也是反射。

     第一种:性能太差不考虑。第二种:为了记日志,生产环境都用动态代理,性能损耗不小,不推荐。第三种:只有UI层能用。其他层和第二种一样。

静态注入方式 (本文重点)。

1:基于IL注入,损耗可以忽略不计。PostSharp采用的这种方式。

技术实现:

1:声明Attribute

public class WeaveSign:Attribute
{
}
public class WeaveAction : Attribute
{
        
}
public class Log : WeaveAction
{
        public static void OnActionBefore(MethodBase mbBase)
        {
           //mbBase 要注入方法的所有信息
            var t = mbBase.GetParameters();
        LogManager.Record();
       }
}        

  

 

2:标记需要注入的方法

[Log]
public static string GetUserName(int userId)
{
        return "Vidar";
}

 

3:IL注入(关键点),采用Mono.Cecil

 private static void Weave(IEnumerable<Assembly> assemblyList)
        {
            //assemblyList要注入的程序集列表。
            foreach (var item in assemblyList)
            {
                var filepath = item.CodeBase.Substring(8, item.CodeBase.Length - 8);
                //读取程序集
                var assembly = AssemblyDefinition.ReadAssembly(filepath);

                //获取WeaveSign类型的类型注入标记
                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)
                    {
                        //获取WeaveAction类型的方法注入标记
                        var attrs =
                            method.CustomAttributes.Where(y => y.AttributeType.Resolve().BaseType.Name == "WeaveAction");
                        foreach (var attr in attrs)
                        {
                            //还原类型
                            var resolve = attr.AttributeType.Resolve();
                            //获取IL容器
                            var ilProcessor = method.Body.GetILProcessor();
                            var firstInstruction = ilProcessor.Body.Instructions.First();
                            //找到标记类型的OnActionBefore方法。
                            var onActionBefore = resolve.GetMethods().Single(n => n.Name == "OnActionBefore");
                            //创建System.Reflection.MethodBase.GetCurrentMethod()方法引用
                            var mfReference=assembly.MainModule.Import(typeof (System.Reflection.MethodBase).GetMethod("GetCurrentMethod"));

                            //注入到IL(调用GetCurrentMethod,入栈)
                            ilProcessor.InsertBefore(firstInstruction,
                                ilProcessor.Create(OpCodes.Call,mfReference));
                            //创建调用(call)标记类型的方法OnActionBefore
                            ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, onActionBefore));
                        }
                    }
                }
                if (types.Any())
                {
                    //写入程序集
                    assembly.Write(filepath);
                }
            }
        }

3:编译成功后。反编译看到如下代码。

 

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";
}

 

补充:

本文侧重的是核心实现思路。

 

日志系统实战(一)-AOP静态注入

标签:des   style   blog   os   io   strong   for   ar   div   

原文地址:http://www.cnblogs.com/mushroom/p/3932698.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!