不用Unity库,利用.NET动态代理自己实现AOP

    AOP意为面向切面的程序设计,主要表现为对不同的代码逻辑进行隔离,从而降低不同业务逻辑之间的耦合性,AOP又理解为“横切”,可以在不改变原有实现的情况下,对代码进行拦截和扩展,如果原有设计像一个瓶子,AOP就相当于一个贴纸,是贴在瓶子外面的,而不是打开瓶盖从瓶口把实现放进瓶子里。

    .NET中实现AOP的第三方库有很多,这里不再阐述了,在这里我们主要用到了.NET中的动态代理技术,为了让大家更深入地理解,这里借用一下上一篇文章(不用Unity库,自己实现.NET轻量级依赖注入)中的例子,在上一篇文章中,我们自己实现了一个轻量级的依赖注入,在这里我们准备用.NET动态代理对依赖注入的方法进行拦截和扩展。

    在大部分系统中,日志和权限都是很常见的功能,我们给自己实现的依赖注入加上AOP的功能,使得所有方法自动实现日志记录和权限验证的功能,首先我们来实现一个公共的权限代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// 权限验证代理类
/// </summary>
public class AuthorizeProxy : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        //判断是否授权
        if (HttpContext.Current.User.Identity.IsAuthenticated)
        {
            invocation.Proceed();
        }
        else
        {
            throw new Exception("没有权限!");
        }
    }
}

    这里要特别说明一下,我们并没有使用原生的Emit来实现动态代理,而是使用了微软的Castle开源库,这个地方需要引用Castle.Core.dll,我们的权限代理类继承自IInterceptor接口,并实现接口中的拦截方法,首先判断是否授权,有授权就利用invocation.Proceed()继续执行,没有授权就抛出异常。接下来看一下怎么在我们的依赖注入中使用该代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public sealed class DependencyInjector
{
    /// <summary>
    /// 根据名称和构造函数的参数加载相应的类
    /// </summary>
    /// <typeparam name="T">需要加载的类所实现的接口</typeparam>
    /// <param name="className">类的名称</param>
    /// <param name="args">构造函数的参数(默认为空)</param>
    /// <returns>类的接口</returns>
    public static T GetClass<T>(string className, object[] args = nullwhere T : class
    {
        //获取接口所在的命名空间
        string factoryName = typeof(T).Namespace;
        //通过依赖注入配置文件获取接口实现所在的命名空间
        string dllName = ConfigurationManager.AppSettings[factoryName];
        //获取类的全名
        string fullClassName = dllName + "." + className;
        //根据dll和类名,利用反射加载类
        object classObject = Assembly.Load(dllName).CreateInstance(fullClassName, true, BindingFlags.Default, null, args, nullnull);
        var generator = new ProxyGenerator();
        //使用动态代理覆盖原来的类
        object classProxy = generator.CreateClassProxy(classObject.GetType(),new IInterceptor[] { new AuthorizeProxy() });
        return classProxy as T;
    }
}

    这段代码和上一篇文章不同的是:得到依赖注入的实现以后,并没有直接返回给调用,而是利用Castle库和我们定义的权限代理类,生成了一个动态代理返回给了调用,这时候如果调用了类中的方法,AuthorizeProxy中的权限验证就会自动执行。接下来我们再添加一个日志的代理类,用于记录每个方法调用的耗时:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LogProxy : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        StringBuilder log = new StringBuilder();
        log.AppendLine(invocation.Method.ReflectedType + "." + invocation.Method.Name + "开始执行...");
        DateTime start = DateTime.Now;
        //这里还可以加上其他的日志...
        invocation.Proceed();
        log.AppendLine(invocation.Method.ReflectedType + "." + invocation.Method.Name + "执行结束,耗时:" + (DateTime.Now - start));
        ErrerLogService<NormalLog>.LogWriter(new NormalLog(log.ToString()));
    }
}

    这段代码和权限验证的代码很像,都是实现了IInterceptor接口的拦截方法,在代码的执行前后都加上日志,并在方法执行完毕后,记录执行时间,同时添加这两个代理类,可以这样:

1
generator.CreateClassProxy(classObject.GetType(),new IInterceptor[] { new LogProxy(),new AuthorizeProxy() });

因为CreateClassProxy接收的是一个数组,这里可以把日志和权限的功能同时加进去。通过这种实现方法我们可以看出:

1.在我们没有更改业务代码的情况实现的日志记录和权限验证(解耦)。

2.我们可以方便地对原有功能进行扩展(扩展性)。

3.AOP提高了我们的开发效率(代码复用)。

作者:Lizon 
出处:http://www.cnblogs.com/lizongshen/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

Leave a Reply