WebActivator的实现原理详解

文章内容

上篇文章,我们分析如何动态注册HttpModule的实现,本篇我们来分析一下通过上篇代码原理实现的WebActivator类库,WebActivator提供了3种功能,允许我们分别在HttpApplication初始化之前,之后以及ShutDown的时候分别执行指定的代码,示例如下:

[assembly: WebActivator.PreApplicationStartMethod(typeof(A.InitClass1), "PreStart")]
[assembly: WebActivator.PostApplicationStartMethod(typeof(A.InitClass1), "PostStart")]
[assembly: WebActivator.ApplicationShutdownMethod(typeof(A.InitClass1), "ShutDown")]

另外还有一点和系统自带的PreApplicationStartMethodAttribute不同的是,WebActivator的每种特性都可以使用多次,比如:

[assembly: WebActivator.PreApplicationStartMethod(typeof(A.InitClass1), "PreStart")]
[assembly: WebActivator.PreApplicationStartMethod(typeof(A.InitClass2), "PreStart")]
[assembly: WebActivator.PreApplicationStartMethod(typeof(A.InitClass3), "PreStart")]

因为它的源码很少,所以今天我们就来全面分析一下WebActivator的实现原理,首先下载WebActivator的最新1.5源码,源码地址:https://bitbucket.org/davidebbo/webactivator/src

解压代码,我们可以看到WebActivator项目里总共有6个重要的cs文件,以及一个packages.config文件(用于标记本项目引用了Microsoft.Web.Infrastructure.dll类库),下面我们来分析一下每个文件的源码。

3个XXXMethodAttribute属性:

根据上面的用法,我们指导WebActivator提供了3个MethodAttribute,我们先来看看这3个文件都是如何实现的,查阅代码发现3个类(PreApplicationStartMethodAttribute/ PostApplicationStartMethodAttribute/ ApplicationShutdownMethodAttribute)的内容都是一样的,都是继承于BaseActivationMethodAttribute类,然后提供构造函数所需要的Type类型和方法名称, 3个特性类都支持使用多次并且只能用于Assembly,代码如下:

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]

通用的基类BaseActivationMethodAttribute:

using System;
using System.Reflection; namespace WebActivator
{
// Base class of all the activation attributes
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public abstract class BaseActivationMethodAttribute : Attribute
{
private Type _type;
private string _methodName; public BaseActivationMethodAttribute(Type type, string methodName)
{
_type = type;
_methodName = methodName;
} public Type Type { get { return _type; } } public string MethodName { get { return _methodName; } } public int Order { get; set; } public void InvokeMethod()
{
// Get the method
MethodInfo method = Type.GetMethod(
MethodName,
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); if (method == null)
{
throw new ArgumentException(
String.Format("The type {0} doesn't have a static method named {1}",
Type, MethodName));
} // Invoke it
method.Invoke(null, null);
}
}
}

通过代码,我们首先可以看到,除了Type和MethodName以外,还多了一个Order属性,用来标记多次使用同一个Attribute的时候的执行顺序。然后提供了一个InvokeMethod方法,用来执行该类里传入当前Type类的MethodName静态方法。

Assembly扩展方法AssemblyExtensions:

using System.Collections.Generic;
using System.Linq;
using System.Reflection; namespace WebActivator
{
static class AssemblyExtensions
{
// Return all the attributes of a given type from an assembly
public static IEnumerable<T> GetActivationAttributes<T>(this Assembly assembly) where T : BaseActivationMethodAttribute
{
return assembly.GetCustomAttributes(
typeof(T),
inherit: false).OfType<T>();
}
}
}

该扩展方法主要是用于获取某一个程序集Assembly下指定类型的所有Attribute(并且不包括继承的类),也就是查询上述3种特性的Attributes(因为每种都允许声明多次)。

主管理类ActivationManager:

该类主要分为如下几个部分:

1 私有静态函数Assemblies, GetAssemblyFiles主要是获取当前应用程序下的所有DLL程序集,以供其它方法从这个程序集集合里遍历相应的特性声明。

// 加载所有获取的程序集
private static IEnumerable<Assembly> Assemblies
{
get
{
if (_assemblies == null)
{
// Cache the list of relevant assemblies, since we need it for both Pre and Post
_assemblies = new List<Assembly>();
foreach (var assemblyFile in GetAssemblyFiles())
{
try
{
// Ignore assemblies we can't load. They could be native, etc...
_assemblies.Add(Assembly.LoadFrom(assemblyFile));
}
catch
{
}
}
} return _assemblies;
}
} // 获取程序集文件路径集合
private static IEnumerable<string> GetAssemblyFiles()
{
// When running under ASP.NET, find assemblies in the bin folder.
// Outside of ASP.NET, use whatever folder WebActivator itself is in
string directory = HostingEnvironment.IsHosted
? HttpRuntime.BinDirectory
: Path.GetDirectoryName(typeof(ActivationManager).Assembly.Location);
return Directory.GetFiles(directory, "*.dll");
}

2 获取所有AppCode文件夹下代码编译后的程序集。

// Return all the App_Code assemblies
private static IEnumerable<Assembly> AppCodeAssemblies
{
get
{
// Return an empty list if we;re not hosted or there aren't any
if (!HostingEnvironment.IsHosted || !_hasInited || BuildManager.CodeAssemblies == null)
{
return Enumerable.Empty<Assembly>();
} return BuildManager.CodeAssemblies.OfType<Assembly>();
}
}

3 执行3种特性里所指定的方法

public static void RunPreStartMethods()
{
RunActivationMethods<PreApplicationStartMethodAttribute>();
} public static void RunPostStartMethods()
{
RunActivationMethods<PostApplicationStartMethodAttribute>();
} public static void RunShutdownMethods()
{
RunActivationMethods<ApplicationShutdownMethodAttribute>();
} // Call the relevant activation method from all assemblies
private static void RunActivationMethods<T>() where T : BaseActivationMethodAttribute
{
foreach (var assembly in Assemblies.Concat(AppCodeAssemblies))
{
foreach (BaseActivationMethodAttribute activationAttrib in assembly.GetActivationAttributes<T>().OrderBy(att => att.Order))
{
activationAttrib.InvokeMethod();
}
}
}

从代码可以看出,3个特性执行方法调用的都是同一个泛型方法RunActivationMethods<T>,在这个方法里,主要是从所有的程序集里,通过泛型方法查询所有标记的特性(按Order排序),并且执行每个特性声明里指定的方法。另外从Assemblies.Concat(AppCodeAssemblies)可以发现,所有的程序集还要包括App_Code目录下代码编译的程序集哦。

4 自定义HttpModule

class StartMethodCallingModule : IHttpModule
{
private static object _lock = new object();
private static int _initializedModuleCount; public void Init(HttpApplication context)
{
lock (_lock)
{
// Keep track of the number of modules initialized and
// make sure we only call the post start methods once per app domain
if (_initializedModuleCount++ == 0)
{
RunPostStartMethods();
}
}
} public void Dispose()
{
lock (_lock)
{
// Call the shutdown methods when the last module is disposed
if (--_initializedModuleCount == 0)
{
RunShutdownMethods();
}
}
}
}

该Module主要是用于在 Init的时候执行PostStart类型的方法,并且在Dispose的时候执行Shutdown类型的方法,并且只执行一次。

5.最重要的入口方法

public static void Run()
{
if (!_hasInited)
{
RunPreStartMethods(); // Register our module to handle any Post Start methods. But outside of ASP.NET, just run them now
if (HostingEnvironment.IsHosted)
{
Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(StartMethodCallingModule));
}
else
{
RunPostStartMethods();
} _hasInited = true;
}
}

Run方法看起来很容易理解了,首先执行PreStart类型的方法,然后判断HostingEnvironment是否Host成功,如果成功就动态注册我们上面自定义的HttpModule,以便让该Module在HttpApplication初始化和Dispose的时候分别执行PostStart类型的方法和ShutDown类型的方法,如果没有Host成功,那只执行PostStart类型的方法。

注:由代码实现可以看出,在PreStart类型的方法里,不能使用HttpContext对象进行输入输出,因为该对象在此时还没用创建成功呢。

6.谁调用了入口方法Run()

这个就不用多说了吧,肯定是使用.Net4.0自带的PreApplicationStartMethodAttribute特性,代码如下:

[assembly: PreApplicationStartMethod(typeof(WebActivator.ActivationManager), "Run")]

你可以让这段代码放在WebActivator项目里任何类文件的namespace外部,但为了统一起见,一般都是放在Properties目录下的AssemblyInfo类文件里,WebActivator就是这么做的。

总结,好了,这就是WebActivator的全部源码,实现起来其实很简单,对吧?那以后项目再有类似需求的时候,就大胆使用这个类库吧,另外NInject.MVC也是基于这个类库来实现的。

参考资料:

http://blogs.msdn.com/b/davidebb/archive/2010/10/11/light-up-your-nupacks-with-startup-code-and-webactivator.aspx

https://bitbucket.org/davidebbo/webactivator/src

同步与推荐

本文已同步至目录索引:MVC之前的那点事儿系列

MVC之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。

 
 

WebActivator的实现原理详解的更多相关文章

  1. MVC之前的那点事儿系列(7):WebActivator的实现原理详解

    文章内容 上篇文章,我们分析如何动态注册HttpModule的实现,本篇我们来分析一下通过上篇代码原理实现的WebActivator类库,WebActivator提供了3种功能,允许我们分别在Http ...

  2. I2C 基础原理详解

    今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都 ...

  3. Zigbee组网原理详解

    Zigbee组网原理详解 来源:互联网 作者:佚名2015年08月13日 15:57   [导读] 组建一个完整的zigbee网状网络包括两个步骤:网络初始化.节点加入网络.其中节点加入网络又包括两个 ...

  4. 块级格式化上下文(block formatting context)、浮动和绝对定位的工作原理详解

    CSS的可视化格式模型中具有一个非常重要地位的概念——定位方案.定位方案用以控制元素的布局,在CSS2.1中,有三种定位方案——普通流.浮动和绝对定位: 普通流:元素按照先后位置自上而下布局,inli ...

  5. SSL/TLS 原理详解

    本文大部分整理自网络,相关文章请见文后参考. SSL/TLS作为一种互联网安全加密技术,原理较为复杂,枯燥而无味,我也是试图理解之后重新整理,尽量做到层次清晰.正文开始. 1. SSL/TLS概览 1 ...

  6. 锁之“轻量级锁”原理详解(Lightweight Locking)

    大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意. 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖 ...

  7. [转]js中几种实用的跨域方法原理详解

    转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 // // 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同 ...

  8. 节点地址的函数list_entry()原理详解

    本节中,我们继续讲解,在linux2.4内核下,如果通过一些列函数从路径名找到目标节点. 3.3.1)接下来查看chached_lookup()的代码(namei.c) [path_walk()> ...

  9. Influxdb原理详解

    本文属于<InfluxDB系列教程>文章系列,该系列共包括以下 15 部分: InfluxDB学习之InfluxDB的安装和简介 InfluxDB学习之InfluxDB的基本概念 Infl ...

随机推荐

  1. angularjs从零开始(一)

    简介   AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了.所以我做了一些工作(你也可以觉得是小 ...

  2. JavaEE(2) - Weblogic 服务器执行JNDI绑定和查找

    1. 应用服务器默认添加的系统属性 NetBeans创建java web project(ctxTest) (index.jsp) <%@page import="java.util. ...

  3. C++ Primer 学习笔记_56_ 类和数据抽象 --消息处理演示示例

    拷贝控制 --消息处理演示样例 说明: 有些类为了做一些工作须要对复制进行控制. 为了给出这种样例,我们将概略定义两个类,这两个类可用于邮件处理应用程序.Message类和 Folder类分别表示电子 ...

  4. 《代码的第一行——Android》封面诞生

    <代码的第一行--Android>已经上市近一个月,现在的情况是相当不错的销售,也特别感谢众多朋友的支持. 其实一本好书,假设你想卖.除了给予外力所要求的内容.封面设计是至关重要的,这本书 ...

  5. Ubuntu下一个openldapserver部署步骤

    1:安装zlib 下载zlib-1.2.3.tar.gz(或其它版本号) wget http://down1.chinaunix.net/distfiles/zlib-1.2.3.tar.gz # . ...

  6. Appium:通过wifi连接Android设备

    1.首先用USB连接你的Android设备,然后在终端运行命令,它可以启动设备的5555端口使其在网络上可以连接. adb tcpip 2.现在断开USB连接,然后确保设备和你的电脑连接同一个无线网络 ...

  7. Swift中文手册 -- The Basics

    原文:Swift中文手册 -- The Basics 基础部分 Swift 是 iOS 和 OS X 应用开发的一门新语言.然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 S ...

  8. CSharp设计模式读书笔记(22):策略模式(学习难度:★☆☆☆☆,使用频率:★★★★☆)

    策略模式(Strategy Pattern):定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy). 模式角色与结构: ...

  9. [kmp+dp] hdu 4628 Pieces

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4622 Reincarnation Time Limit: 6000/3000 MS (Java/Ot ...

  10. easyui datagrid shift 多选

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta na ...