文章内容

上篇文章,我们分析如何动态注册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++ == )
{
RunPostStartMethods();
}
}
} public void Dispose()
{
lock (_lock)
{
// Call the shutdown methods when the last module is disposed
if (--_initializedModuleCount == )
{
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之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。

MVC之前的那点事儿系列(7):WebActivator的实现原理详解的更多相关文章

  1. MVC之前的那点事儿系列(3):HttpRuntime详解分析(下)

    文章内容 话说,经过各种各样复杂的我们不知道的内部处理,非托管代码正式开始调用ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime继承了IISPAIRuntime接口 ...

  2. MVC之前的那点事儿系列(2):HttpRuntime详解分析(上)

    文章内容 从上章文章都知道,asp.net是运行在HttpRuntime里的,但是从CLR如何进入HttpRuntime的,可能大家都不太清晰.本章节就是通过深入分析.Net4的源码来展示其中的重要步 ...

  3. Spring框架系列(6) - Spring IOC实现原理详解之IOC体系结构设计

    在对IoC有了初步的认知后,我们开始对IOC的实现原理进行深入理解.本文将帮助你站在设计者的角度去看IOC最顶层的结构设计.@pdai Spring框架系列(6) - Spring IOC实现原理详解 ...

  4. Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程

    上文,我们看了IOC设计要点和设计结构:紧接着这篇,我们可以看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的. ...

  5. Spring框架系列(8) - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)

    上文,我们看了IOC设计要点和设计结构:以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的:容器中存放的是Bean的定义即Be ...

  6. Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现

    前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...

  7. Spring框架系列(10) - Spring AOP实现原理详解之AOP代理的创建

    上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor).本文在此基 ...

  8. Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现

    我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...

  9. Spring框架系列(12) - Spring AOP实现原理详解之JDK代理实现

    上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分.@pdai Spring框架系列(12) - Spring AOP实现原理详解 ...

随机推荐

  1. C struct结构体内存对齐问题

    在空间看到别人的疑问引起了我的兴趣,刚好是我感兴趣的话题,就写一下.为了别人的疑问,也发表在qq空间里.因为下班比较晚,10点才到家,发表的也晚.其实是个简单的问题.  直接用实例和内存图说明: #i ...

  2. PMO、P3O

    中国的大中型企业最近几年纷纷设立了项目管理办公室PMO,普遍关注如何发挥PMO最大的价值.而P3O指南描述了一个高阶的组织模型和机制,构建组织内部所有关于变革的决策支持与交付的结构及机制,允许永久的和 ...

  3. Atitit. Atiposter 发帖机 新特性 poster new feature v11  .docx

    Atitit. Atiposter 发帖机 新特性 poster new feature v11  .docx 1.1.  版本历史1 2. 1. 未来版本规划2 2.1. V12版本规划2 2.2. ...

  4. Atitit 控制中心快速启动面板quick launcher

    Atitit 控制中心快速启动面板quick launcher contralPanel.bat aaaControlPanel.contrlx /AtiPlatf_auto/src_atibrow/ ...

  5. 更新日志 - fir.im 新版优化上线

    经过这段时间的用户反馈收集和新版本的功能调研,我们对 fir.im Rio 上传下载.应用管理再次做了调整优化.感谢之前内测用户的反馈与建议.目前 fir.im Rio 新版已正式上线,主要优化有以下 ...

  6. 每天一个linux命令(24):Linux文件类型与扩展名

    Linux文件类型和Linux文件的文件名所代表的意义是两个不同的概念.我们通过一般应用程序而创建的比如file.txt.file.tar.gz ,这些文件虽然要用不同的程序来打开,但放在Linux文 ...

  7. java 中获取2个时间段中所包含的周数(股票的周数->从周六到周五)

    Calendar 类中是以周日为开始的第一天的,所以Calendar.DAY_OF_WEEK为1的时候是周日. 在股票中有日K 周K和月K的数据.  在此之中的周K是指交易日中一周的数据,周六到周五为 ...

  8. struts2获取web元素(request、session、application)

    一.Action中获取 第一种方式: 通过ActionContext,这种方式取到的对象是Map类型 import java.util.Map; import com.opensymphony.xwo ...

  9. 【原创】Newlife.XCode的常见功能使用(一)查询与数据初始化

            本博客所有文章分类的总目录:http://www.cnblogs.com/asxinyu/p/4288836.html Newlife XCode组件相关文章目录:http://www ...

  10. Abp公共连接和事务管理方法

    Conection 和事务管理在使用数据库的应用中是一个最重要的概念.当你打开一个连接,开始一个事务,如何来处理这些连接等等. 您也许知道,.NET使用了连接池.所以,创建一个连接实际上是从连接池里得 ...