• ASP.NET MVC进阶之路:深入理解Controller激活机制并使用Ioc容器创建对象


      本文标题说是"深入理解Controller"其实有点“标题党”的味道了。本篇只会探讨"Controller"的激活机制,也就是如何创建Controller的并调用的。本篇不是讲解Controller底层相关知识,不过后期博文会对其进行介绍。

    0X1 DefaultControllerFactory

      DefaultControllerFactory对象可以说是Controller创建激活的一个重要类,在默认情况下(这里的默认情况指没有向MVC指定负责创建Controller的类)DefaultControllerFactory负责Controller的创建于激活。本文讨论的重点将会放在Controller的创建。

      我们先看DefaultControllerFactory类的几个方法

      public virtual IController CreateController(RequestContext requestContext, string controllerName);
            public virtual void ReleaseController(IController controller);
            protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType);
            protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType);
            protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName);
    View Code

       我们可以看到CreateController方法,它主要的作用是“ 使用指定的请求上下文来创建指定的控制器”,也就是说其实是通过这个方法进行创建控制器的操作。我相信很多人看到这里的第一想法就是“虚方法重写不久完了吗“?别急,我们来看看CreateController的源代码:

    public virtual IController CreateController(RequestContext requestContext, string controllerName)
    {
        if (requestContext == null)
        {
            throw new ArgumentNullException("requestContext");
        }
        if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
        {
            throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
        }
        Type controllerType = this.GetControllerType(requestContext, controllerName);
        return this.GetControllerInstance(requestContext, controllerType);
    }
    CreateController源代码

      我们可以看到CreateController方法在对参数进行验证后会调用其实例本身的GetControllerInstance方法,由这个方法最后来创建Controller的实例。

    0X2 第一种实现方式

      我们新建一个MVC应用,并新建一个名为NinjectController的类派生于DefaultControllerFactory,重写DefaultControllerFactory的GetControllerInstance方法,代码如下:

     1         private static IKernel _kernerl;
     2         public static IKernel Kernerl
     3         {
     4             get
     5             {
     6                 if (_kernerl == null)
     7                     _kernerl = new StandardKernel();
     8                 return _kernerl;
     9             }
    10         }
    11 
    12         static NinjectController()
    13         {
    14             //把Home绑定到Ninject里面
    15             Kernerl.Bind<HomeController>().To<HomeController>();
    16         }
    17 
    18 
    19         protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    20         {
    21             if (Kernerl == null)
    22                 throw new NotImplementedException("Kinject未绑定任何数据");
    23             if (controllerType == null || Kernerl.Get(controllerType) == null)
    24                 return null;
    25             return (IController)Kernerl.Get(controllerType);
    26         }
    第一种实现方法

      这种方法虽然可以实现,但是在开发中并不可取,我们再来看第二种方式。

    0X3 第二种实现方式

      我们通过查看GetControllerInstance的源代码可以发现它最终调用了实例中ControllerActivator的Create方法进行创建Controller对象,ControllerActivator类型是一个IControllerActivator接口,那么这个ControllerActivator是在哪赋值的呢?我们看下DefaultControllerFactory的构造函数

    public DefaultControllerFactory() : this(null, null, null)
    {
    }
    public DefaultControllerFactory(IControllerActivator controllerActivator) : this(controllerActivator, null, null) { }

       DefaultControllerFactory有一个内部调用的方法,其源代码如下:

    internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
    {
        if (controllerActivator != null)
        {
            this._controllerActivator = controllerActivator;
            return;
        }
        IResolver<IControllerActivator> arg_44_1 = activatorResolver;
        if (activatorResolver == null)
        {
            arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory constructor");
        }
        this._activatorResolver = arg_44_1;
    }
    View Code

      通过阅读源码我们可以看到当IControllerActivator等于Null的时候,则会通过  DefaultControllerFactory.DefaultControllerActivator方法来创建IControllerActivator对象,我们继续阅读DefaultControllerActivator的源代码

     1 public IController Create(RequestContext requestContext, Type controllerType)
     2     {
     3         IController result;
     4         try
     5         {
     6             result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
     7         }
     8         catch (Exception innerException)
     9         {
    10             throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[]
    11             {
    12                 controllerType
    13             }), innerException);
    14         }
    15         return result;
    16     }

      IControllerActivator的Create方法在创建IController对象的时候会从this._resoverThunk的GetServer方法中获取,this._resoverThunk是IControllerActivator的局部变量,该变量存储一个返回值为IDependencyResolver的委托,其定义如下:

    private Func<IDependencyResolver> _resolverThunk;

      而对其赋值则来则与IControllerActivator的构造方法,其源代码如下:

    public DefaultControllerActivator() : this(null)
        {
        }
    
        public DefaultControllerActivator(IDependencyResolver resolver)
        {
            if (resolver == null)
            {
                this._resolverThunk = (() => DependencyResolver.Current);
                return;
            }
            this._resolverThunk = (() => resolver);
        }

      从以上代码可以看出,this._resoverThunk实则是通过DependencyResolver.Current进行赋值的,而创建IController对象则是通过 this._resolverThunk().GetService(controllerType)来获取的,通俗来讲,默认的Conteroller实则是通过IDependencyResolver接口的GetService方法进行创建的,相信看到这里的朋友心中已经有了答案。没错,我们第二种做法就是通过自定义IDependencyResolver来创建我们的Contorller。

      相信很多朋友会问,IDependencyResolver接口在MVC中起到什么作用。IDependencyResolver定义如下:

    public interface IDependencyResolver
        {
           
            object GetService(Type serviceType);
            IEnumerable<object> GetServices(Type serviceType);
        }

      一个获取单个类型的方法,一个获取多个对象的方法。有一个名为DependencyResolver的,它本身并不实现IDependencyResolver接口,所以它和IDependencyResolver没有任何关系。我们来看下DependencyResolver的定义。

      从上述代码我们可以清楚知道默认情况下实际上是调用了DependencyResolver的Current返回了当前对象,然后进行了对象的创建,那我们要做的只要改变Current的值就可以了,但是,我们有没有办法呢?答案是肯定的,DependencyResolver定义了一个SetResolver的方法,我们看下源码:

    public void InnerSetResolver(object commonServiceLocator)
    {
        if (commonServiceLocator == null)
        {
            throw new ArgumentNullException("commonServiceLocator");
        }
        Type type = commonServiceLocator.GetType();
        MethodInfo method = type.GetMethod("GetInstance", new Type[]
        {
            typeof(Type)
        });
        MethodInfo method2 = type.GetMethod("GetAllInstances", new Type[]
        {
            typeof(Type)
        });
        if (method == null || method.ReturnType != typeof(object) || method2 == null || method2.ReturnType != typeof(IEnumerable<object>))
        {
            throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DependencyResolver_DoesNotImplementICommonServiceLocator, new object[]
            {
                type.FullName
            }), "commonServiceLocator");
        }
        Func<Type, object> getService = (Func<Type, object>)Delegate.CreateDelegate(typeof(Func<Type, object>), commonServiceLocator, method);
        Func<Type, IEnumerable<object>> getServices = (Func<Type, IEnumerable<object>>)Delegate.CreateDelegate(typeof(Func<Type, IEnumerable<object>>), commonServiceLocator, method2);
        this.InnerSetResolver(new DependencyResolver.DelegateBasedDependencyResolver(getService, getServices));
    }
    View Code

      该方法最终会将指定的对象复制给Current,我们新建一个类名为NinjectDependencyResolver并实现IDependencyResolver接口。我们在类中定一个Ninject的字段,并封装一个用来往Ninject中添加对象的方法,其代码如下:

    public class NinjectDependencyResolver : IDependencyResolver
        {
            private IKernel Kerner;
    
            NinjectDependencyResolver()
            {
                Kerner = new StandardKernel();
            }
    
            public void Registet<To, Form>() where Form : To
            {
                Kerner.Bind<To>().To<Form>();
            }
            public object GetService(Type serviceType)
            {
                return Kerner.TryGet(serviceType);
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                return Kerner.GetAll(serviceType);
            }
        }
    View Code

      如需使用我们自定义的NinjectDependencyResolver,我们需要在Global.asax中对其进行注册:

    protected void Application_Start()
            {
                //其他操作
                NinjectDependencyResolver ninjectDependencyResolver = new NinjectDependencyResolver();
                ninjectDependencyResolver.Registet<Controller, Controllers.HomeController>();//这里的Controller应该是具体类型,这里这样写只是为了演示
                DependencyResolver.SetResolver(ninjectDependencyResolver);
            }

      再次运行我们的程序,则会看到效果。

  • 相关阅读:
    dubbo+zookeeper报错:com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method
    CoreException: Could not get the value for parameter compilerId for plugin execution default-compile: PluginResolutionException: Plugin org.apache.maven.plugins:maven-compiler-plugin:3.1
    Spring容器加载配置文件
    Java基础(一)字符串之如何删除指定的字符
    Java基础(二)数组之如何排序数组并插入元素
    Java基础(一)字符串之如何比较字符串
    Spring的事务管理
    Java使用split()截取字符串
    JavaWeb过滤器实现页面跳转至登录页面
    jQuery获取session中的值
  • 原文地址:https://www.cnblogs.com/dazhuangtage/p/5679805.html
Copyright © 2020-2023  润新知