MVC的DependencyResolver组件

一、前言

  DependencyResolver是MVC中一个重要的组件,从名字可以看出,它负责依赖对象的解析,可以说它是MVC框架内部使用的一个IOC容器。MVC内部很多对象的创建都是通过它完成的,或许我们平时没有直接用到它,但是如果你在使用unity、autofac,或者在看一些开源项目时,总会看到它的身影。接下来就让我们看一下这个组件是如何工作的。

二、通过Controller的激活理解DependencyResolver的工作过程

  这里先插一个题外话,经常会有面试问:asp.net 几个核心对象是什么?一般人都会回答:Server、Request、Response、Session、Cookie这些。但我的回答会是HttpApplication、HttpHandler和HttpModule,这才是管道模型中的核心类型,整个asp.net的处理流程和可扩展性也都是建立在这几个对象上的。

  回到主题,asp.net请求都是交给HttpHandler处理的,对于MVC来说,是交给一个MvcHandler,它负责激活Controller,如果你不知道为什么,请看这里。在这里我们直接定位到MvcHandler的PR方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
{
    IController controller;
    IControllerFactory factory;
    ProcessRequestInit(httpContext, out controller, out factory);
 
    //其它操作
    //调用 controller.Execute方法
}
 
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
    HttpContext currentContext = HttpContext.Current;
 
    //从路由获取controller名称
    string controllerName = RequestContext.RouteData.GetRequiredString("controller");
 
    //通过ControllerBuilder获取ControllerFactory,默认就是DefaultControllerFactory
    factory = ControllerBuilder.GetControllerFactory();
 
    //通过ControllerFactory获取Controller对象
    controller = factory.CreateController(RequestContext, controllerName);
}

  ControllerFactory故名思议就是用于创建Controller的,我们也可以自己实现IControllerFactory,参与Controller的激活过程,具体是在全局调用ControllerBuilder.Current.SetControllerFactory方法。我们这里主要关注的是Controller的激活过程,实际上它们的创建过程是相似的。默认使用的ControllerFactory是DefaultControllerFactory。DefaultControllerFactory的CreateController方法如下:  

1
2
3
4
5
6
7
8
9
10
11
12
13
public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
    //获取Controller类型
    Type controllerType = GetControllerType(requestContext, controllerName);
 
    IController controller = GetControllerInstance(requestContext, controllerType);
    return controller;
}
 
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    return ControllerActivator.Create(requestContext, controllerType);
}

  可以看到,它通过一个ControllerActivator来创建IController对象,默认使用的是DefaultControllerActivator。与ControllerFactory类似,我们可以实现IControllerActivator,参与Controller的激活过程,具体是将ControllerActivator作为DefaultConrtollerFactory构造函数参数,然后再在全局调用ControllerBuilder.Current.SetControllerFactory方法。可以看到MVC的Controller激活过程是很灵活的,它提供多种方式让我们自定义激活过程。DefaultControllerActivator定义如下:

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
26
27
28
29
30
31
32
private class DefaultControllerActivator : IControllerActivator
{
    private Func<IDependencyResolver> _resolverThunk;
 
    public DefaultControllerActivator()
        this(null)
    {
    }
 
    public DefaultControllerActivator(IDependencyResolver resolver)
    {
        if (resolver == null)
        {
            _resolverThunk = () => DependencyResolver.Current;
        }
        else
        {
            _resolverThunk = () => resolver;
        }
    }
 
    public IController Create(RequestContext requestContext, Type controllerType)
    {
        try
        {
            return (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
        }
        catch (Exception ex)
        {
        }
    }
}

  这里的_resolverThunk是一个用于获取IDepencyResolver对象的委托,实际获得的是DependencyResolver.Current。我们也可以自己实现IDependencyResolver,参与Controller的激活过程,具体是在全局调用DependencyResolver的静态方法SetResolver方法。需要注意的是这里的DependencyResolver类型(这里是类型,而其它地方提到的DependencyResolver都是组件的意思)并没有实现IDependencyResolver接口,我觉得将它命名为DependencyResolverContainer会更合适一些。IDepdencyResolver接口的定义如下:

1
2
3
4
5
public interface IDependencyResolver
{
    object GetService(Type serviceType);
    IEnumerable<object> GetServices(Type serviceType);
}

  默认DependencyResolver.Current使用的是DefaultDependencyResolver类型,这里又和ControllerFactory和ControllerActivator的设计一样了,如果我们自定义,那么就使用,否则就使用默认的。DefaultDependencyResolver定义如下:

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
private class DefaultDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        if (serviceType.IsInterface || serviceType.IsAbstract)
        {
            return null;
        }
 
        try
        {
            //如果Controller Type创建Controller实例对象
            return Activator.CreateInstance(serviceType);
        }
        catch
        {
            return null;
        }
    }
 
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return Enumerable.Empty<object>();
    }
}

  可以看到,MVC会将Controller对象的创建通过DependencyResolver完成。将对象的创建通过DependencyResolver完成的好处是可以降低对象间的耦合度;另外,通过实现IDependencyResolver接口,我们可以完全控制对象的创建过程,例如将对象的依赖关系转移到配置文件中等等。

  通过上面我们还知道了有三种默认类型:DefaultControllerFactory、DefaultControllerActivator和DefaultDependencyResolver,分别对应三个接口:IControllerFactory、IControllerActivator、IDependencyResolver。它们的设计是类似的,都是提供给外部一个接口,如果外部自己实现了这个过程,那么就使用,否则用默认的。实际上这也是我们参与Controller激活过程的三种做法。

三、实现IDependencyResolver接口

  接下来通过一个例子证明上面的过程。我们要实现的需求是通过实现IDependencyResolver接口,实现Controller构造函数注入服务。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HomeController : Controller
{           
    private IUserService _service;
    public HomeController(IUserService service)
    {
        _service = service;
    }
 
    public ActionResult Index()
    {
        return Content(_service.GetUserName());
    }
}

  HomeController只依赖于IUserService接口,不依赖于具体对象。

  接下来我们实现IDependencyResolver接口,依赖注入的实现方式有很多种,这里我们使用Unity。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class UnityDependencyResolver : IDependencyResolver
{
    public object GetService(Type serviceType)
    {
        if(serviceType == null)
        {
            throw new ArgumentNullException("serviceType");
        }           
        return (serviceType.IsClass && !serviceType.IsAbstract)
            || Ioc.IsRegistered(serviceType) ? Ioc.GetService(serviceType) : null;
    }
 
    public IEnumerable<object> GetServices(Type serviceType)
    {
        if (serviceType == null)
        {
            throw new ArgumentNullException("serviceType");
        }
        return (serviceType.IsClass && !serviceType.IsAbstract)
            || Ioc.IsRegistered(serviceType) ? Ioc.GetServices(serviceType) : null;
    }
}

  这里需要判断 (serviceType.IsClass && !serviceType.IsAbstract) || Ioc.IsRegistered(serviceType) 原因是我们前面说过的,MVC内部很多对象都是通过DependencyResolver组件创建的,如上面的IConrtollerFactoy,所以这里我们只负责对已注册的类型或类(非抽象类)进行解析。

  Ioc类在这里很简单,如下:

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 class Ioc
{
    private static IUnityContainer _container = new UnityContainer();
 
    public static void RegisterType<TFrom,TTo>()
        where TTo : TFrom
    {           
        _container.RegisterType<TFrom, TTo>();
    }
 
    public static object GetService(Type type)
    {                       
        return _container.Resolve(type);
    }
 
    public static IEnumerable<object> GetServices(Type type)
    {
        return _container.ResolveAll(type);
    }
 
    public static bool IsRegistered(Type type)
    {
        return _container.IsRegistered(type);
    }
}

  接着,在Application_Start方法中,注册Service和设置IocDependencyResolver:

1
2
Ioc.RegisterType<IUserService, UserService>();
DependencyResolver.SetResolver(new IocDependencyResolver());

  运行就可以看到HomeController构造函数的IUserService就是UserService类型了。

四、总结

  实际上,上面的例子我们也可以用实现IControllerFactory或者IControllerActivator达到同样的目的,但使用IDependencyResolver会更简单一点,而且大部分的IOC框架都已经提供了这样的功能。例如上面UnityDependencyResolver根本不用自己定义,Unity for MVC 已经有这么一个类型了,直接使用即可。如果使用Autofac的话可以是:DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

最新文章

  1. js cookie
  2. guava学习--SettableFuture
  3. ACCP7.0-S2-复习自测-15测试分析
  4. SQL循环索引
  5. ASP.NET Web API之消息[拦截]处理
  6. Ubuntu安装JDK与配置环境变量
  7. centos 64bit 安装与升级 chrome的方法
  8. Android Support V7 包中 ActionBar的使用
  9. DataSet与DataTable的区别
  10. win8下nodejs安装配置记录
  11. ChartControl一个小Demo
  12. 并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析
  13. 【Linux】Set CentOS no GUI default
  14. css中的视距perspective和视差效果
  15. (转载)java list排序
  16. 关于文件的INode与Java中的文件操作接口
  17. python开发_thread_线程基础
  18. json格式在ruby和rails中的注意事项
  19. Qt 模拟鼠标点击(QApplication::sendEvent(ui-&gt;pushbutton, &amp;event0);)
  20. Oracle锁处理、解锁方法

热门文章

  1. Eclipse背景和匹配出现单词的一些设置
  2. QT5静态编译教程,主要针对vs2012(渡世白玉)
  3. HP-UX查看版本
  4. 一个跨平台的 C++ 内存泄漏检测器
  5. 简体中文 — ANSI Common Lisp 中文版
  6. Cocos2dx 3.0 过渡篇(三十)灰机还是3D好(Sprite3D)
  7. JAVA泛型之&lt;? extends T&gt;:(通配符上限)和&lt;? super T&gt;(通配符下限)
  8. hdu Crazy Circuits
  9. Android 实现自己定义多级树控件和全选与反选的效果
  10. 开源 java CMS - FreeCMS2.3 留言管理