Unity容器的简单AOP与DI的应用Demo

作者: 网络编程  发布:2019-07-18

转发请注明出处:

《asp.net-mvc框架揭秘》一书中,有个示例,是使用unity容器来注入自定义的控制器工厂。代码示例可以自己去下载源码,在这里我就不说了。IOC容器的本质是解耦的实例化接口类,而如何做到解耦就是通过第三方容器来实例化,在这里是unity容器,而不是在项目中实例化接口类。实例化的方法无非就是反射,Emit,表达式树,委托等四个方法。Unity容器的IOC使用主要是三个个方法:Register,Resolver,Dispose。前者注册接口和接口类,后者将接口类的实例化转移到第三方容器中实现。而这里的Dispose却是有点文章了。如果单单是控制台的应用项目,就不必多说,如果是在mvc框架中的话,我们的接口类的资源释放应该放在什么地方合适呢?微软unity开发小组给我们做了很好的解释,原文:
我们将Unity容器里面资源的释放与控制器的资源释放绑定在一起。如何用代码来表示?我们在基于Unity的控制器工厂中的GetControllerInstance中解析controllerType对象,而不是解析某个接口:
(IController)this.UnityContainer.Resolve(controllerType);
尽管Unity容器是IOC框架,我们还是可以使用unity来做AOP,可以参考的官方资料:(5 - Interception using Unity).aspx)
我们主要是通过集成ICallHandler接口来实现AOP,这个接口是unity给我们提供的,这个接口主要就是一个Invoke方法。继承自ICallHandler接口的类(TCalHandler),当通过接口(TIOCInterface)开始调用类(TIOCImple)中的方法时,就会开始调用类(TCalHandler)的Invoke方法。
在Invoke中,如果调用getNext()方法就会调用IOCImple标注了属性的方法。如果你的C#基础比较扎实,你对C#中的一个重要知识点-特性(attribute)应该就会有印象以及一定的了解。asp.net-mvc框架中的过滤器就是基于attribute实现的。那么在这里也是,我们需要调用unity给我们提供的一个特性attribute-HandlerAttribute,在这里我们调用我们基于ICallHandler的类。
DI是为了解耦的实例化接口,而AOP是横向的注入一些逻辑,我们可以在AOP里面实现DI,unity中的AOP模块默认会给我们实现DI,一旦我们实现了AOP,就相当于实现了DI。我会挑一些代码片段来解释。代码来自<<asp.net-mvc框架揭秘>>的第14章S1401源码。首先我们实现自己自定义的控制器工厂:

整个Demo是基于Controller-Service-Repository架构设计的,每一层之间是通过接口来实现解耦与调用的,参照了《ASP.NETMVC5框架揭秘》一书最后的网站示例架构,使用Unity容器作为DI容器以及实现AOP。

图片 1图片 2

首先Repository文件夹里面的代码文件:

public class UnityControllerFactory : DefaultControllerFactory
{
    public IUnityContainer UnityContainer { get; private set; }

    public UnityControllerFactory(IUnityContainer unityContainer)
    {
        this.UnityContainer = unityContainer;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (null == controllerType)
        {
            return null;
        }
        return (IController)this.UnityContainer.Resolve(controllerType);
    }
}

见百度网盘链接

View Code

整个Repository相当于三层架构里面的DAL数据访问层,它的作用就是调用数据库,封装了最基本的增删改查,当然你可以选择ADO.NET或是EntityFramework来做数据库驱动。

之前说过的unity的DI对象 Resolve是在这里完成的。

其次就是Services文件夹里面的代码文件:
见百度网盘链接

我们定义了一个接口ITimeProvider和接口实现类DefaultTimeProvider,然后我们基于ICallHandler接口实现一个类:

整个Services文件主要的功能就是调用下一层的Repository文件夹的相关类。我们在这里就是使用DI中的构造函数注入了,使用接口来实现解耦,这就需要用到unity容器了。这个层次是为上一层的控制器层服务的。

图片 3图片 4

接下来就是Controller层了,这一层调用下一层Services也是基于接口,使用DI构造函数注入实现了解耦。
见百度网盘链接

public class CachingCallHandler : ICallHandler
{
    public int Order { get ; set ; }

    public TimeSpan ExpirationTime { get; private set; }

    public static TimeSpan DefaultExpirationTime { get; private set; }

    public static Func<MethodBase, object[], string> CacheKeyGenerator { get; private set; }

    // 静态构造函数,只调用一次,并且是最先调用的
    static CachingCallHandler()
    {
        DefaultExpirationTime = new TimeSpan(0, 5, 0);
        Guid prefix = Guid.NewGuid();

        CacheKeyGenerator = (method, inputs) =>
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("{0}: ", prefix);
            sb.AppendFormat("{0}: ", method.DeclaringType);
            sb.AppendFormat("{0}: ", method.Name);

            if (inputs != null)
            {
                foreach (var input in inputs)
                {
                    string hashCode = (input == null) ? "" : input.GetHashCode().ToString();
                    sb.AppendFormat("{0}: ", hashCode);
                }
            }
            return sb.ToString().TrimEnd(':');
        };

    }

    public CachingCallHandler(TimeSpan? expirationTime=null)
    {
        this.ExpirationTime = expirationTime.HasValue ? expirationTime.Value : DefaultExpirationTime;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        MethodInfo targetMethod = (MethodInfo)input.MethodBase;

        if(targetMethod.ReturnType == typeof(void))
        {
            return getNext()(input, getNext);
        }

        object[] inputs = new object[input.Inputs.Count];
        input.Inputs.CopyTo(inputs, 0);
        string cacheKey = CacheKeyGenerator(targetMethod, inputs);

        object[] cachedResult = HttpRuntime.Cache.Get(cacheKey) as object[];

        if (null == cachedResult)
        {
            IMethodReturn realReturn = getNext()(input, getNext);
            if(null == realReturn.Exception)
            {
                HttpRuntime.Cache.Insert(cacheKey, new object[] { realReturn.ReturnValue }, null, DateTime.Now.Add(this.ExpirationTime), Cache.NoSlidingExpiration);
            }
            return realReturn;
        }
        return input.CreateMethodReturn(cachedResult[0], new object[] { input.Arguments });

    }
}

准备做好了,接下来就是使用Unity容器来替换MVC框架默认的控制器工厂以及基于Unity的AOP设计。

View Code

首先基于DefaultControllerFactory创建一个UnityControllerFactory,引入unity容器:

Invoke方法的调用是在ITimeProvider对象调用其内的接口时候触发调用的。
接下来基于HandlerAttribute来实现一个类:

public class UnityControllerFactory : DefaultControllerFactory
    {
        public IUnityContainer UnityContainer { get; private set; }

        public UnityControllerFactory()
        {
            /// unity container 的AOP可以完成IOC的功能,在我们使用AOP的时候
            /// 也就完成了依赖项的实例化。
            UnityContainer = new UnityContainer();
            UnityContainer.AddNewExtension<Interception>()
                .RegisterType<IFooRepository, FooRepository>() ///IOC注入实现
                .RegisterType<IBarRepository, BarRepository>() ///IOC注入实现
                .RegisterType<IFooService, FooService>() /// FooService的AOP
                .Configure<Interception>()
                .SetInterceptorFor<IFooService>(new InterfaceInterceptor());

            /// BarSerice的AOP
            UnityContainer.AddNewExtension<Interception>()
                .RegisterType<IBarService, BarSerice>()
                .Configure<Interception>()
                .SetInterceptorFor<IBarService>(new InterfaceInterceptor());
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (null == controllerType)
            {
                return null;
            }
            return (IController)UnityContainer.Resolve(controllerType);
        }
    }

图片 5图片 6

在构造函数里面使用Unity容器引入IOC和AOP,这是特别重要的:

public class CachingCallHandlerAttribute : HandlerAttribute
{
    public TimeSpan? ExpirationTime { get; private set; }

    public CachingCallHandlerAttribute(string expirationTime ="")
    {
        if(!string.IsNullOrEmpty(expirationTime))
        {
            TimeSpan expirationTimeSpan;
            if(!TimeSpan.TryParse(expirationTime, out expirationTimeSpan))
            {
                throw new ArgumentException("输入过期时间(TimeSpan) 不合法");
            }
            this.ExpirationTime = expirationTimeSpan;
        }
    }

    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new CachingCallHandler(this.ExpirationTime) { Order = this.Order };
    }
}
/// unity container 的AOP可以完成IOC的功能,在我们使用AOP的时候
/// 也就完成了依赖项的实例化。
UnityContainer = new UnityContainer();
UnityContainer.AddNewExtension<Interception>()
.RegisterType<IFooRepository, FooRepository>()
.RegisterType<IBarRepository, BarRepository>()
.RegisterType<IFooService, FooService>() /// FooService的AOP
.Configure<Interception>()
.SetInterceptorFor<IFooService>(new InterfaceInterceptor());


/// BarSerice的AOP
UnityContainer.AddNewExtension<Interception>()
.RegisterType<IBarService, BarSerice>()
.Configure<Interception>()
.SetInterceptorFor<IBarService>(new InterfaceInterceptor());

View Code

查看FooSercice类和BarService类,我们在两个方法里面使用了AOP注入,这点是要在Unity构造函数中,用unity容器的创建AOP,AOP的实现是基于IFooService接口与FooService类,IBarService接口和BarService类的。

接下来我们Global.asax中,完成AOP的注入:

本文由金沙澳门官网发布于网络编程,转载请注明出处:Unity容器的简单AOP与DI的应用Demo

关键词: 金沙澳门官网