ASP.NET MVC4学习笔记之Controller的激活
一. 高层相关类说明
当路由系统根据请求Url收集路由信息后,下一步就要将路由信息传给Controller激活系统,Controller激活系统负责实现了IController接口的Controller类实例化。它的相关类族体系如下图所示:
MvcHandler实现了IHttpHandler, IHttpAsyncHandler,IRequiresSessionState三个接口,其中IHttpHandler, IHttpAsyncHandler分别是HttpHandler同步与异步的实现,IRequiresSessionState是个标记接口,表示需要Session支持.
IController, IAsyncController, ControllerBase, Controller 是一个继承体系,IController是控制器接口,只定义了一个方法Execute方法表示执行入口,
IAsyncController是控制器的异步执行版本,ControllerBase是控制器基类,为控制器执行做一些初始化和环境准备工作,实现了Execute方法并在其内调用保护的抽像方法ExecuteCore.这个地方应用了Templete Method模式. Controller实现了一堆接口, 为我们编程提供方便, 定义了大量的属性和方法,具体的后面章节专门讲解.
IControllerFactory 表示的是控制器的创建工厂,其中定义了三个方法, CreateController方法创建IController的实例, GetControllerSessionBehavior方法获取Controller的会话行为,我们可以在自定义的Controller的上应用SessionStateAttribute指定会话行为。ReleaseController负责释放使用完的Controller实例。
public interface IControllerFactory
{
IController CreateController(RequestContext requestContext, string controllerName);
SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
void ReleaseController(IController controller);
}
ControllerBuilder 是负责实例化IController和IControllerFactory的接口,封装具体的创建算法。提供了一个静态只读属性Current表示当前的ControllerBuilder对象。
二. MvcHandler中IController与IControllerFactory的创建与执行
1. 主体过程ProcessRequest方法,代码如下所示, 创建的过程委托给私有方法ProcessRequestInit
protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
IController controller;
IControllerFactory factory;
ProcessRequestInit(httpContext, out controller, out factory);
try
{
controller.Execute(RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
}
2. ProcessRequestInit方法的主要代码如下所示,我们可以看到最终的创建工作是交给了ControllerBuilder对象.
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
//其它代码
// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
factory = ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext, controllerName);
if (controller == null)
{
throw new InvalidOperationException(...)
}
}
三.ControllerBuilder解析
1.接口定义如下:
public class ControllerBuilder
{
public ControllerBuilder();
public static ControllerBuilder Current { get; } //
public HashSet<string> DefaultNamespaces { get; } //默认命名空间,用于Controller类型解析过程
public IControllerFactory GetControllerFactory();
public void SetControllerFactory(IControllerFactory controllerFactory); //设置自定义ControllerFactory
public void SetControllerFactory(Type controllerFactoryType); //设置自定义ControllerFactory的类型,
}
ControllerBuilder主要封装了IControllerFactory的创建过程,也许命名叫ControllerFactoryBuilder更合适,从接口可以看出,我们可以传入自定义
实现的IControllerFactory。
2. 内部引用的几个主要类说明:
IResolver<T> 只定义了一个泛型属性,表示获取相关类型的一个实例;
SingleServiceResolver<TService> 顾名思义,表示单一服务类型解析,它实现在了IResolver接口, 在ControllerBuilder内部使用的是SingleServiceResolver<IControllerFactory>;
DefaultControllerFactory 系统提供的默认的Controller创建工厂实现;
3. 内部IControllerFactory创建过程
在ControllerBuilder实例化时,调用默认构造函数,而默认构造函数调用如下的内部构造函数,serviceResolver传值为null,
internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
_serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
() => _factoryThunk(),
new DefaultControllerFactory { ControllerBuilder = this },
"ControllerBuilder.GetControllerFactory");
}
_factoryTunk() 是一个返回IControllerFactory的委托(Func<IControllerFactory>),默认值是() => null; 其主要目的是当传入自定义IControllerFactory时做统一处理,
public void SetControllerFactory(IControllerFactory controllerFactory)
{
_factoryThunk = () => controllerFactory;
}
ControllerBuilder返回IControllerFactory的方法内部实现如下:
public IControllerFactory GetControllerFactory()
{
return _serviceResolver.Current;
}
从上面的代码中我们可以看出Factory的创建过程进一步委托给了SingleServiceResolver对象,现在我们看看SingleServiceResolver究竟是怎么创建对象的
四.SingleServiceResolver<TService> 类型
SingleServiceResolver其实ASP.NET MVC许多基础类型创建所遵询的一个模式. 它的构造函数如下:
public SingleServiceResolver(Func<TService> currentValueThunk, TService defaultValue, string callerMethodName)
{
//省略检查代码
_resolverThunk = () => DependencyResolver.Current; // DependencyResolver.Current表示系统全局的对象解析器
_currentValueFromResolver = new Lazy<TService>(GetValueFromResolver); //从全局的Resolver器中创建对象
_currentValueThunk = currentValueThunk; //当前传入的创建委托
_defaultValue = defaultValue; //默认值
_callerMethodName = callerMethodName;
}
返回实例的代码如下
public TService Current
{
get { return _currentValueFromResolver.Value ?? _currentValueThunk() ?? _defaultValue; }
}
从中我们可以看出SingleServiceResolver解析对象的过程:
1. 首先从尝试从全局的对象解析器(DependencyResolver)中创建对象
2. 否则尝试利用当前的功能委托来创建对象
3. 最后返回对象的默认值
在IControllerFactory创建中,默认情况下第1种和第2种情况都不起作用,所以返回的是DefaultControllerFactory, 现在我们终于得到了IControllerFactory实例,现在来看看它是怎么生成Controller实例的。
五.DefaultControllerFactory解析
DefaultControllerFactory实现了IControllerFactory, 故名思义,它的主要作用就是Controller实例的创建与释放,会话模式的获取。
1. Controller类型的实例化
Controller实例的创建实现在CreateController方法中,主要的代码如下:
public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
//省略其它代码
Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}
可以看到分成了两步走,首先查找确定Controller的类型,接着再利用类型创建其实例。下面来具体的看看相关的方法
1.1 Controller类型的查找
类型的查找实现在GetControllerType方法中,代码如下:
protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
{
// 省略其它代码
//1. 首先检查在定制路由规则时指定的命名空间
object routeNamespacesObj;
Type match;
if (requestContext != null && routeData.DataTokens.TryGetValue(RouteDataTokenKeys.Namespaces, out routeNamespacesObj))
{
IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
if (routeNamespaces != null && routeNamespaces.Any())
{
HashSet<string> namespaceHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
match = GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaceHash);
// the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
if (match != null || false.Equals(routeData.DataTokens[RouteDataTokenKeys.UseNamespaceFallback]))
{
// got a match or the route requested we stop looking
return match;
}
}
}
// 检查默认的命名空间
if (ControllerBuilder.DefaultNamespaces.Count > 0)
{
HashSet<string> namespaceDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
match = GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaceDefaults);
if (match != null)
{
return match;
}
}
//检查所有的命名空间,也就是只要有Controller名唯一匹配的就返回相应的Controller类型
return GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null /* namespaces */);
}
这个方法是查找Controller类型的骨架,查找是由三个层次的命名空间组成,
1.首先从制定路由规则时指定的命名空间中查找,但一般我们指定路由规则时没有指定命名空间,这里有还有一个参数UseNamespaceFallback表示是否查找后备命名空间,这个参数默认为true.
2. 从默认的命名空间ControllerBuilder.DefaultNamespaces中查找
3. 从所有的命名空间中查找,查找唯一能匹配的Controller
在以上查找中,都会调用GetControllerTypeWithinNamespaces方法,现在来看看这个方法的实现
private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)
{
// Once the master list of controllers has been created we can quickly index into it
ControllerTypeCache.EnsureInitialized(BuildManager);
ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
switch (matchingTypes.Count)
{
case 0:
// no matching types
return null;
case 1:
// single matching type
return matchingTypes.First();
default:
// multiple matching types
throw CreateAmbiguousControllerException(route, controllerName, matchingTypes);
}
}
从以上的代码中我们可以看到查找工作又进一步委托给了ControllerTypeCache这个内部类型,这个类型是特意为实现快速查找Controller类型而设计的一个数据结构,在内部它把所有的Controller通过反射把数据组织成如下形式:
controllerAName namespace1, Type
namespace2, Type
controllerBName namespace1, Type
namespace2, Type
...
当我们调用ControllerTypeCache.GetControllerTypes,传入controllerName, namespaces参数时,首先会通过controllerName得到匹配的namespace和Type列表,
再利用传入的namespaces参数与列表中的每个namespace进行比较,匹配则将相应的类型加入返回列表,如果传入的namespaces为null, 则直接将列表中所有的类型都加入返回列表,在GetControllerTypeWithinNamespaces方法中我们检查返回结果,如果只有一个类型,这是我们想要的结果,则直接返回,有一个以上则抛出Ambiguous异常。至此我们确定了Controller的类型,现在来看看Controller的实例化。
1.2 Controller类型的实例化
在前面我们已经看到,Controller类型实例化是实现在GetControllerInstance方法中,代码如下:
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
//省略非关键代码
return ControllerActivator.Create(requestContext, controllerType);
}
直接调用了ControllerActivator来创建实例,ControllerActivator是一个类型为IControllerActivator的属性,IControllerActivator的定义如下:
public interface IControllerActivator
{
IController Create(RequestContext requestContext, Type controllerType);
}
具体来看看ControllerActivator属性的定义,
private IControllerActivator ControllerActivator
{
get
{
if (_controllerActivator != null)
{
return _controllerActivator;
}
_controllerActivator = _activatorResolver.Current;
return _controllerActivator;
}
}
这里_controllerActivator在DefaultControllerFactory构造函数中初始化,代表传入自定义的Controller激活器.具有最高的优先级。
_activatorResolver是IResolver<IControllerActivator>类型, 也是在构造函数中初始化,允许自定义实现IResolver<IControllerActivator>,具有第二高的优先级,
但在默认情况一下,前面两个参数都为null, _activatorResolver被实例化为SingleServiceResolver<IControllerActivator>类型。具体我们来看看DefaultControllerFactory的构造函数:
internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
{
if (controllerActivator != null)
{
_controllerActivator = controllerActivator;
}
else
{
_activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(
() => null,
new DefaultControllerActivator(dependencyResolver),
"DefaultControllerFactory constructor");
}
}
SingleServiceResolver<T>泛型类前面已分析,这里不再赘述,从上面的代码中我们看到,默认情况下Controller实例化最终落在了DefaultControllerActivator的头上,
再来看看该类型的实现:
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)
{
throw new InvalidOperationException(...)
}
}
}
DefaultControllerActivator构造函数允许传一个IDependencyResolver 对象,从上面的代码中我们可以看出,优先使用该对象创建Conroller实例,
如果该对象为null,则尝试使用系统默认的DependencyResolver,如果前面的两者IDependencyResolver创建失败,再使用Activator.CreateInstance实列化.
在默认情况一下,Controller实例是通过DependencyResolver.Current实列化,最终也是调用Activator.CreateInstance实例化的。
2. Controller实例的释放
释放的实现代码很简单,即检查Controller是否实现了IDisposable 接口,如果实现该接口则调用其Dispose()方法。
public virtual void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
3.Controller会话行为的设置
3.1 会话行为获取
controller会话行为的获取是通过反射得到应用在Controller上的SessionStateAttribute,具体的实现在代码在GetControllerSessionBehavior中,
protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return SessionStateBehavior.Default;
}
return _sessionStateCache.GetOrAdd(
controllerType,
type =>
{
var attr = type.GetCustomAttributes(typeof(SessionStateAttribute), inherit: true)
.OfType<SessionStateAttribute>()
.FirstOrDefault();
return (attr != null) ? attr.Behavior : SessionStateBehavior.Default;
});
}
从上面的代码中可以看出,如果没在Controller上的指定特别的Session行为,会返回SessionStateBehavior.Default,表示由IHttpHandler实现的会话标记接口来确定会话行为,MvcHandler标记了IRequiresSessionState,表示Session可读可写.
3.2 会话行为设置
会话行为设置是在MvcRouteHandler中,具本的代码如下:
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
//设置会话行为
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
{
//省略非关键代码
IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
}
六总结
在本小节中,我们在源代码级别分析了ControllerFacotry和Controller实例的创建过程,从中可以看出ASP.NET MVC框架定义很多的扩展点,
下一节我们来看看具体Controller激活相关的扩展应用。