zoukankan      html  css  js  c++  java
  • WebActivator的实现原理详解

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

     

     

     

     

     

  • 相关阅读:
    C++ 对象间通信框架 V2.0 ××××××× 之(三)
    C++ 对象间通信框架 V2.0 ××××××× 之(二)
    C++ 对象间通信框架 V2.0 ××××××× 之一
    C++ 对象间通讯机制 框架实现
    websocket 传输数据帧打包 (client端)
    SetwindowText 之线程阻塞
    oracle虚拟索引的创建
    函数索引
    关于is null和is not null不能利用索引的测试
    索引的选择性
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3775989.html
Copyright © 2011-2022 走看看