MVC之前的那点事儿系列(7):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++ == )
{
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也是基于这个类库来实现的。
参考资料:
https://bitbucket.org/davidebbo/webactivator/src
同步与推荐
本文已同步至目录索引:MVC之前的那点事儿系列
MVC之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。
MVC之前的那点事儿系列(7):WebActivator的实现原理详解的更多相关文章
- MVC之前的那点事儿系列(3):HttpRuntime详解分析(下)
文章内容 话说,经过各种各样复杂的我们不知道的内部处理,非托管代码正式开始调用ISPAIRuntime的ProcessRequest方法了(ISPAIRuntime继承了IISPAIRuntime接口 ...
- MVC之前的那点事儿系列(2):HttpRuntime详解分析(上)
文章内容 从上章文章都知道,asp.net是运行在HttpRuntime里的,但是从CLR如何进入HttpRuntime的,可能大家都不太清晰.本章节就是通过深入分析.Net4的源码来展示其中的重要步 ...
- Spring框架系列(6) - Spring IOC实现原理详解之IOC体系结构设计
在对IoC有了初步的认知后,我们开始对IOC的实现原理进行深入理解.本文将帮助你站在设计者的角度去看IOC最顶层的结构设计.@pdai Spring框架系列(6) - Spring IOC实现原理详解 ...
- Spring框架系列(7) - Spring IOC实现原理详解之IOC初始化流程
上文,我们看了IOC设计要点和设计结构:紧接着这篇,我们可以看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的. ...
- Spring框架系列(8) - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)
上文,我们看了IOC设计要点和设计结构:以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的:容器中存放的是Bean的定义即Be ...
- Spring框架系列(9) - Spring AOP实现原理详解之AOP切面的实现
前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的.本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所 ...
- Spring框架系列(10) - Spring AOP实现原理详解之AOP代理的创建
上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor).本文在此基 ...
- Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现
我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...
- Spring框架系列(12) - Spring AOP实现原理详解之JDK代理实现
上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分.@pdai Spring框架系列(12) - Spring AOP实现原理详解 ...
随机推荐
- C struct结构体内存对齐问题
在空间看到别人的疑问引起了我的兴趣,刚好是我感兴趣的话题,就写一下.为了别人的疑问,也发表在qq空间里.因为下班比较晚,10点才到家,发表的也晚.其实是个简单的问题. 直接用实例和内存图说明: #i ...
- PMO、P3O
中国的大中型企业最近几年纷纷设立了项目管理办公室PMO,普遍关注如何发挥PMO最大的价值.而P3O指南描述了一个高阶的组织模型和机制,构建组织内部所有关于变革的决策支持与交付的结构及机制,允许永久的和 ...
- 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. ...
- Atitit 控制中心快速启动面板quick launcher
Atitit 控制中心快速启动面板quick launcher contralPanel.bat aaaControlPanel.contrlx /AtiPlatf_auto/src_atibrow/ ...
- 更新日志 - fir.im 新版优化上线
经过这段时间的用户反馈收集和新版本的功能调研,我们对 fir.im Rio 上传下载.应用管理再次做了调整优化.感谢之前内测用户的反馈与建议.目前 fir.im Rio 新版已正式上线,主要优化有以下 ...
- 每天一个linux命令(24):Linux文件类型与扩展名
Linux文件类型和Linux文件的文件名所代表的意义是两个不同的概念.我们通过一般应用程序而创建的比如file.txt.file.tar.gz ,这些文件虽然要用不同的程序来打开,但放在Linux文 ...
- java 中获取2个时间段中所包含的周数(股票的周数->从周六到周五)
Calendar 类中是以周日为开始的第一天的,所以Calendar.DAY_OF_WEEK为1的时候是周日. 在股票中有日K 周K和月K的数据. 在此之中的周K是指交易日中一周的数据,周六到周五为 ...
- struts2获取web元素(request、session、application)
一.Action中获取 第一种方式: 通过ActionContext,这种方式取到的对象是Map类型 import java.util.Map; import com.opensymphony.xwo ...
- 【原创】Newlife.XCode的常见功能使用(一)查询与数据初始化
本博客所有文章分类的总目录:http://www.cnblogs.com/asxinyu/p/4288836.html Newlife XCode组件相关文章目录:http://www ...
- Abp公共连接和事务管理方法
Conection 和事务管理在使用数据库的应用中是一个最重要的概念.当你打开一个连接,开始一个事务,如何来处理这些连接等等. 您也许知道,.NET使用了连接池.所以,创建一个连接实际上是从连接池里得 ...