net搭建热插拔式web框架(沙箱的构建)

上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中。上一篇文章很多人看了以后,都表示不解,觉得不知道我到底要干什么,可能就像隔行如隔山吧,就像做移动端开发的人很少去考虑分布式中的通信一样。大家都知道模块化,但模块化的思路有很多,我的只是其中一种,也许你看到最后会觉得这种思路在经过不断地演化后会成为一种很好的解决方案,当然这离不开以后大家对代码及思想的贡献。

  好了不扯了,还是回到主题上来吧....

  沙箱是什么?怎么用呢?

 沙箱说白了就是插件、模块运行的环境,有点像docker又不像(哈哈)。沙箱主要由两部分组成:沙箱管道沙箱启动器(为了显得高大上一些,就起了两个难以理解的名字),还有一个以后为功能做铺垫的实体类——controller/action 封包类

  首先贴一下这个实体类的代码:

/// <summary>controller/action 封包类
/// </summary>
public class CAModel
{
public string ControllerName{get;set;}
public string ActionName{get;set;}
public string UrlPath { get; set; }
/// <summary>构造
/// </summary>
/// <param name="controllerName">controller 全名(带命名空间)</param>
/// <param name="actionName">action 全名(不带参数)</param>
public CAModel(string controllerName,string actionName)
{
ControllerName=controllerName;
ActionName=actionName;
UrlPath = controllerName.Replace(".Areas.", "/").Replace(".Controllers.", "/");//controller转Url
if (UrlPath.EndsWith("Controller"))
{
UrlPath = string.Format("/{0}/{1}", UrlPath.Substring(0, UrlPath.Length - 10),actionName);
}
} public CAModel()
{
}
} 
  这个封装类主要是为了以后系统核心功能:权限管理,方便获取模块内所有action对应的Url路径,现在就不过多投入精力了。
  下边我们说一下沙箱管道(SandBoxChannel ):沙箱管道服务于沙箱启动器,这个类需要继承MarshalByRefObject,也就是必须要支持跨域访问。这个类中最关键的就是_assembly和InvokeMothod。

+ /// <summary>沙箱管道

    /// </summary>
    public class SandBoxChannel : MarshalByRefObject
    {
        /// <summary>沙箱内加载的程序集
        /// </summary>
        private Assembly _assembly;
 
        /// <summary>加载程序集
        /// </summary>
        /// <param name="assemblyFile">程序集文件路径(主程序类库路径,依赖类库自动加载)</param>
        public void LoadAssembly(string assemblyFile)
        {
            try
            {
                _assembly = Assembly.LoadFrom(assemblyFile);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
 
        /// <summary>沙箱内方法调用
        /// </summary>
        /// <param name="typeName">类名称(全名称)</param>
        /// <param name="methodName">方法名称</param>
        /// <param name="args">参数</param>
        /// <returns></returns>
        public object InvokeMothod(string typeName, string methodName, params object[] args)
        {
            var _assembly_temp = _assembly;
            if (_assembly_temp == null)
                return null;
             
            if (typeName == "Huber.Kernel.Entity.CurVariable" && methodName == "SetCurWebDir")
            {//设置当前沙箱内的系统变量,非外部方法
                foreach (var in AppDomain.CurrentDomain.GetAssemblies())
                {
                    if (a.GetName().Name == "Huber.Kernel")
                    {
                        _assembly_temp = a;
                        break;
                    }
                }
            }
            Type tp = _assembly_temp.GetType(typeName, truefalse);
            if (tp == null)
                return null;
            MethodInfo method = tp.GetMethod(methodName);
            if (method == null)
                return null;
            Object obj = Activator.CreateInstance(tp);
            //ContentResult
            //FileContentResult
            //FileStreamResult
            //FilePathResult
            //HttpNotFoundResult
            //JavaScriptResult
            //JsonResult
            //PartialViewResult
            //RedirectToRouteResult
            //RedirectResult
            //ViewResult
            object result = method.Invoke(obj, args);
            return result;
 
        }
 
 
 
 
        /// <summary>获取所有的action
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, CAModel> GetAllAction()
        {
            Dictionary<string, CAModel> result = null;
            Type[] types = _assembly.GetTypes();
            MethodInfo[] meths = null;
            string controller = string.Empty;
            if (types != null)
            {
                result = new Dictionary<string, CAModel>();
                string url = string.Empty;
                CAModel temp;
                foreach (var in types)
                {
                    if (t.BaseType != null && t.BaseType.ToString() == "Huber.Kernel.MVC.HuberController")
                    {
 
                        meths = t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
                        foreach (var in meths)
                        {
                            temp = new CAModel(t.ToString(), m.Name);
                            result.Add(temp.UrlPath.ToLower(), temp);
                        }
                    }
                }
            }
 
            return result;
        }
 
 
        public override object InitializeLifetimeService()
        {
            //Remoting对象 无限生存期
            return null;
        }

  再看一下沙箱启动器(SandBoxDynamicLoader):所谓沙箱启动器就是创建一个启动器对象,把模块的类库、配置文件等加载进去,当然这个启动内部是一个沙箱(即appdomain)。在SandBoxDynamicLoader的构造方法中创建的了一个appdomain和一个SandBoxChannel对象,支持模块的加载与卸载

+ public class SandBoxDynamicLoader

   {
       /// <summary>沙箱对应的应用程序域
       /// </summary>
       private AppDomain appDomain;
       /// <summary>沙箱管道
       /// </summary>
       private SandBoxChannel channelChannel;
       /// <summary>程序域的ID
       /// </summary>
       public int AppDomainID { getset; }
       /// <summary>插件名称
       /// </summary>
       public string PluginName { getset; }
 
       /// <summary>构造
       /// </summary>
       /// <param name="ApplicationBase">插件的所在的目录(bin目录)</param>
       /// <param name="_PluginName">插件名称</param>
       /// <param name="configPath">config文件位置</param>
       /// <param name="_AppDomainID">域标识(唯一)</param>
       public SandBoxDynamicLoader(string ApplicationBase, string _PluginName, string configPath, int _AppDomainID)
       {
           PluginName = _PluginName;
           AppDomainID = _AppDomainID;
           AppDomainSetup setup = new AppDomainSetup();
           setup.ApplicationBase = ApplicationBase;
           DirectoryInfo di=new DirectoryInfo(ApplicationBase);
           if (configPath != string.Empty)
           {
               setup.ConfigurationFile = configPath;
           }
           setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private");
           setup.CachePath = setup.ApplicationBase;
           setup.ShadowCopyFiles = "true";
           setup.ShadowCopyDirectories = setup.ApplicationBase+"\\SandBoxRunShadow";
           AppDomain.CurrentDomain.SetShadowCopyFiles();
           this.appDomain = AppDomain.CreateDomain(PluginName, null, setup);
           this.appDomain.SetData("APP_CONFIG_FILE", configPath);
            
           String name = Assembly.GetExecutingAssembly().GetName().FullName;
           try
           {
               this.channelChannel = (SandBoxChannel)this.appDomain.CreateInstanceAndUnwrap(name, typeof(SandBoxChannel).FullName);
           }
           catch (Exception ex)
           {
 
           }
            
       }
 
       /// <summary>加载程序集
       /// </summary>
       /// <param name="assemblyFile"></param>
       public void LoadAssembly(string assemblyFile)
       {
           channelChannel.LoadAssembly(assemblyFile);
       }
 
       /// <summary>获取当前模块内所有action
       /// </summary>
       /// <returns></returns>
       public Dictionary<string, CAModel> GetAllAction()
       {
           if (channelChannel == nullreturn null;
           return channelChannel.GetAllAction();
       }
       /// <summary>方法调用
       /// </summary>
       /// <param name="typeName">类名称(全名称)</param>
       /// <param name="methodName">方法名称</param>
       /// <param name="args">参数</param>
       /// <returns></returns>
       public object InvokeMothod(string typeName, string methodName, params object[] args)
       {
           return channelChannel.InvokeMothod(typeName, methodName, args);
       }
       /// <summary>卸载
       /// </summary>
       public void Unload()
       {
           try
           {
               if (appDomain == nullreturn;
               AppDomain.Unload(this.appDomain);
               this.appDomain = null;
           }
           catch (CannotUnloadAppDomainException ex)
           {
               throw ex;
           }
       }
  到此沙箱模型就完了,其实整个过程可以归纳为:创建一个appdomain,利用反射调用方法处理请求。这个模型不仅在web平台上可以使用,其实他早就在系统服务型框架、窗体框架中大范围使用了。
转载请注明出处:http://www.cnblogs.com/eric-z/p/5028243.html

net搭建热插拔式web框架(沙箱的构建)的更多相关文章

  1. 第二篇 基于.net搭建热插拔式web框架(沙箱的构建)

    上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中.上一篇文章很多人看了以后,都表示不解,觉得不知道我到底 ...

  2. 第三篇 基于.net搭建热插拔式web框架(重造Controller)

    由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并且http上下文不支持跨域,所以我们要重造一个contro ...

  3. 基于.net搭建热插拔式web框架(实现原理)

    第一节:我们为什么需要一个热插拔式的web框架? 模块之间独立开发 假设我们要做一个后台管理系统,其中包括“用户活跃度”.“产品管理”."账单管理"等模块.每个模块中有自己的业务特 ...

  4. net搭建热插拔式web框架

    net搭建热插拔式web框架(重造Controller) 由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并 ...

  5. 第五篇 基于.net搭建热插拔式web框架(拦截器---请求管道)

    好了,前边我们把核心内容介绍完了,接下来要做的就是拦截用户的请求,并把请求转向沙箱内. 这里我们准备通过实现一个HttpModule类来完成请求的拦截与转发.新建一个HuberHttpModule类, ...

  6. 第四篇 基于.net搭建热插拔式web框架(RazorEngine实现)

    在开头也是先给大家道个歉,由于最近准备婚事导致这篇文章耽误了许久,同时也谢谢老婆大人对我的支持. 回顾上篇文章,我们重造了一个controller,这个controller中用到了视图引擎,我们的视图 ...

  7. 架构探险——第三章(搭建轻量级Java Web框架)

    解决的问题 servlet的数量会随业务功能的扩展而不断增加,我们有必要减少servlet的数量,交给controller处理,它负责调用service的相关方法,并将返回值放入request或res ...

  8. 读《架构探险——从零开始写Java Web框架》

    内容提要 <架构探险--从零开始写Java Web框架>首先从一个简单的 Web 应用开始,让读者学会如何使用 IDEA.Maven.Git 等开发工具搭建 Java Web 应用:接着通 ...

  9. 什么是web框架?

    英文原文:http://jeffknupp.com/blog/2014/03/03/what-is-a-web-framework/ 在原文基础上加上了自己在翻译过程中,查看的资料和自己的一些理解,同 ...

随机推荐

  1. Jetty:配置连接器

    连接器配置概览 连接器用于接收网络连接,配置一个连接器须要配置:  1)连接器的网络參数(比如:port):  2)连接器使用的服务(比如:executors,schedulers).  3)为接收连 ...

  2. Wix学习整理(7)——在开始菜单中为HelloWorld添加卸载快捷方式

    原文:Wix学习整理(7)--在开始菜单中为HelloWorld添加卸载快捷方式 通过前面的几篇随笔,我们已经给我们的HelloWorld提供了填写注册表信息,以及开始菜单快捷方式和桌面快捷方式.这些 ...

  3. POJ 1088 滑雪 记忆化优化题解

    本题有人写是DP,只是和DP还是有点区别的,应该主要是记忆化 Momoization 算法. 思路就是递归,然后在递归的过程把计算的结果记录起来,以便后面使用. 非常经典的搜索题目,这样的方法非常多题 ...

  4. 采用CXF+spring+restful创建一个web接口项目

    这篇文章是http://blog.csdn.net/zxnlmj/article/details/28880303下面,加入的基础上的restful特征 1.参加restful必jar包裹 jsr31 ...

  5. ZTESoft 持续集成 编年史 之 持续集成建设---自主研发(总括)

    最终选择了自主研发,考虑到我们团队对java以及flex知识的储备,展示层使用夸浏览器的flex开发,后端业务层使用java. 一.方案: BEC + ZCIPAgent + ZCIPServer + ...

  6. Windown安装Mysql安装图解

    一.MYSQL的安装 1.打开下载的mysql安装文件mysql-5.0.27-win32.zip,双击解压缩,运行“setup.exe”. 2.选择安装类型,有“Typical(默认)”.“Comp ...

  7. 收藏的一段关于java大数运算的代码

    收藏的一段关于java大数运算的代码: package study_02.number; import java.math.BigDecimal; import java.math.BigIntege ...

  8. c语言推断数是否是素数

    这是推断数是否是素数.网络版非常.我觉得有点问题.今天一个朋友问我这个问题.我知道,今天,我把自己的代码,非常实用哦!. #include<stdio.h> #include<mat ...

  9. Qt学习一门:直接使用QT具

    今天,通过直接使用QT一些工具来编写命令行程序.你可以看到一种Qt更一般的用法. 内容很easy,输出电流日期. 首先,用一个QDate分类,可以使用QDate类的静态方法currentDate为了得 ...

  10. 【Linux驱动】TQ2440 LED驱动程序

    ★整体介绍 LED驱动程序主要实现了TQ2440开发板上的4个LED灯的硬件驱动,实现了对引脚GPIOB5.GPIOB6.GPIOB7.GPIOB8的高低电平设置(common-smdk.c中已经实现 ...