Abp学习笔记---轻松搞懂模块
做.net开发的朋友或多或少都听说过这个框架,自己在差不多一年前也才开始听说,但是!!!之前也没太当回事,一来是工作项目上用不着,二来以为到时候需要用的时候再拿来用就好了。
现在看来却是大错特错!近段时间,自己身边发生很多事,自然而然的,听到abp 的次数多了了起来,开始被问及 abp 相关的问题。自己也只能知道这个框架做了很多集成,同时基于 ddd 思想设计(坦白,目前的自己还只能套用 ddd 代码层面的结构,精髓却是未能掌握)。所以也决定深入学习 abp ,看看它有多牛。
所以,这一篇是abp学习笔记的开篇,之后可能会不定期新增新内容,具体如何,就看学习的效率了。
其实网上有很多对abp这个框架的介绍,而博主在学习过程中主要参照了一下文章:
http://www.cnblogs.com/mienreal/p/4528470.html
https://www.jianshu.com/p/1e6efd9be629
http://www.cnblogs.com/1zhk/p/5281458.html
不得不说这些文章的内容都很优秀!
但是可能是自己水平太菜了或者是理解能力太差了,对于各位大牛讲 模块 内容不是很明白。
这个不明白不是这个模块从发现到注册的过程不明白(却是也有不少没理清楚,但这不是重点),而是“为什么要这样做?”,“具体是怎么做的呢?”。
之所以会有这样的疑问是因为,大牛们都会把模块的创建,到模块的生命周期,怎么加载的讲的很清晰,但具体这个的作用是什么,内部是怎么实现的,却模糊带过。亦或者有些是简略带过“使用模块是为了在任何地方都可以使用/复用这些模块的功能”,也没具体讲怎么实现。或者有些文章里边在讲模块部分的时候,拿abp 模块部分的源码过来 解析,这是相对挺好的了。但是对于我这个新手来说,还是不好理解,一开始愣是想了很久他们说的模块复用,随时随地使用,以及模块直接交互是怎么进行的。
扯多了。下面列下自己在学习中的疑问,然后再逐一回答,大牛路过勿笑:
1.模块是如何复用/使用的?
2.模块之间是怎么交互的?
3.这种模块编程跟引入dll程序集有什么区别?
4.模块的注册主要做了什么?
看起来很简单,但当初难到自己了(回答上了的也不用往下看了)
回答:
1.所谓的模块复用,其实也跟我们平时引用某个 dll 然后使用程序集提供的类或者方法的做法是一样的。像我们写一个http帮助类,它自己创建独立 dll,,然后哪个项目需要,我们就引入这个帮助类,这个复用是一样的。
使用上也是一样的,创建对象,然后调用。但是!它这个区别就是在创建上了,模块对象的创建是基于 ioc 容器(如果不清楚则需要先去了解一下了再往下看)自动创建的,这样,我们在使用模块中的服务时,不需要手动去new 一个对象,而是通过 依赖注入的方式进行自动注入。
2.模块之间的交互一般为两种:
一个是依赖,这个是单向,比如说模块A依赖模块B,那么可以在模块A的操作中根据需要通过 依赖注入使用B模块的对象。比如模块B是日志模块,那么在 A 模块直接使用模块B的日志对象,而模块B是不会也不能循环依赖 A 模块。
二个是并列,此时两个模块是被当作一个大模块向另一个模块提供服务。比如 模块C同时需要模块A和模块B的服务,这时候,对于AB之间,他们就是并列关系。
3.其实这个就是引用dll一样(既然一样,那还要个屁模块编程?,哈哈哈,看下面第四个问题)。
4.大牛们讲模块内容时,都会从头捋一遍模块的注册以及模块启动加载过程走一遍。但是这个模块注册具体是做什么,为什么要这样做呢?
实际上,模块的注册就是把模块(程序集,abp建议一个程序集只有一个模块)中的类型注册到 ioc 容器,没错就是这么简单,而这个ioc容器是全局的,这就是为什么 问题 1 中说的 能在任何地方调用,因为程序集中的公开类型都会被注册到容器中,需要某个类型的对象直接使用 ioc 容器创建。
那为什么这样做呢?如果有过在mvc Global.asax 中手动配置 ioc 容器,然后替换 mvc 默认控制器创建器的朋友就比较好理解了,我们在Global.asax 做配置时,不同的程序集我们都要 手动的 通过 Assembly 找到要注册的程序集,然后使用容器进行注册。平时简单的小注册没什么,但是,如果是动态使用某些模块,然后模块可能又依赖某一个模块呢?那样注册就会有问题了,一来要写很多重复的东西,二来万一哪天我换掉其中某一个模块,那改动的就麻烦了。
所以,就有个模块自己注册的做法,程序在启动时会去算模块的依赖顺序,然后去分别调用初始化,这样如果我的模块拿去人家那里,人家也只管引用,而不需要再启动文件注册,而且这种做法还能为每个模块做自己的个性化注册,因为每个模块可能有自己的约束或者规范,自己注册比交给人家注册清楚多了。
其实说了这么多,但是看文字的话,可能有人理解有人还没理解,写这个,其实主要是为了方便需要的朋友,不过最好是看了大牛的文章就能理解不要看这个了。
为了写这篇文章,自己还准备了一个 模块载入和使用的 demo,做的很粗糙,但是能用,如果有兴趣的话,且往下看。
注意,一下实现仅为自己理解的基础上实现的简单的过程,实际会复杂的多,自己也还在学习中
项目结构如下
其中:
InjectContainer 是 ioc 容器
ModuleCore 是这个模块编程的核心
其它的都是一个个的 模块module
特别说一下 ModuleCore:
Contaier 提供容器基本注册方法,具体实现是基于 InjectContaier
using System;
using System.Reflection;
using MyContainer = InjectContainer.Container;namespace ModuleCore.Container
{public class IocContainer : IIocContainer{private static IIocContainer instance;private static MyContainer container;public IocContainer() { }static IocContainer(){container = MyContainer.GetContainer();container.Register(typeof(IocContainer));instance = new IocContainer();}public static IIocContainer Getinstance(){return instance;}public object GetInstance(Type toType){return container.GetInstance<object>(toType);}public TTo GetInstance<TTo>(){return container.GetInstance<TTo>();}public void Register(Type toType){container.Register(toType);}public void Register<TTo>(){container.Register(typeof(TTo));}public void Register(Assembly assembly){var types = assembly.GetTypes();//.Where(t=>t.GetInterface(typeof(IDependService).FullName)!=null);foreach (var type in types){container.Register(type);}}}
}
Module下的 BaseModule类似与 AbpModule,是一个抽象类,这里只有一个方法 Register,每个模块都要有一个继承它的类
using ModuleCore.Container;namespace ModuleCore.Module
{public abstract class BaseModule{public IIocContainer Container { get; internal set; }public abstract void Register();}
}
ModuleManager 是加载和调用module 进行注册的管理类,这里提供 Initialize 方法,基本思路是传进启动的 模块,然后查看这个module 的依赖模块,有的话依次往下获取,然后进行排序,先注册最上层的模块。而这个注册是将类型跟起实现的接口做映射(自己写的ioc容器目前只是注册接口类型)
using ModuleCore.Container;
using System;
using System.Collections.Generic;
using System.Linq;namespace ModuleCore.Module
{public class ModuleManager : IModuleManager{private static Dictionary<Type, int> moduleOrder = new Dictionary<Type, int>();private static int orderIndex = 0;public void Initialize(Type startupModule){LoadModule(startupModule);moduleOrder = moduleOrder.OrderByDescending(item => item.Value).ToDictionary(item => item.Key, item => item.Value);foreach (var item in moduleOrder){var module = Activator.CreateInstance(item.Key) as BaseModule;module.Container = IocContainer.Getinstance();module.Register();}}private void LoadModule(Type currentModule){if(moduleOrder.ContainsKey(currentModule)){moduleOrder[currentModule] = orderIndex++;}else{moduleOrder.Add(currentModule, orderIndex++);}var depands = currentModule.GetCustomAttributes(typeof(DependOnAttribute), false);if (depands != null && depands.Count()>0){foreach (var depand in depands){var attr = depand as DependOnAttribute;foreach (var module in attr.modules){LoadModule(module);}}}}}
}
再一个是 ModuleStarter,不管是什么程序,总要有个入口的,而不管是怎么解耦,总会有地方要耦合的。而这个启动入口就是 ModuleStarter 了,这个有点类似 AbpBootstrapper,这个类提供开始初始化的静态方法 Start 以及一个获取容器的方法 GetContainer。
using ModuleCore.Container;
using ModuleCore.Module;
using System;namespace ModuleCore
{public class ModuleStarter{public static void Start<TStartModule>()where TStartModule:BaseModule{var manager = new ModuleManager();manager.Initialize(typeof(TStartModule));}public static IIocContainer GetContainer(){return IocContainer.Getinstance();}}
}
然后其它模块大概过一下,都是demo,就随便写写:
Encrypt:这里 的 IMylogger 在创建 EncryptHelper时, ioc 容器会自动注入,所以不需要 new 则直接使用
public interface IEncryptHelper:IDependService{string EncryptData(string source);}public class EncryptHelper : IEncryptHelper{private IMyLogger myLogger;public EncryptHelper(IMyLogger myLogger){this.myLogger = myLogger;}public string EncryptData(string source){myLogger.Write(source);return string.Format("<-{0}->", source);}}
[DependOn(typeof(LoggerModule))]public class EncryptModule : BaseModule{public override void Register(){Container.Register(Assembly.GetExecutingAssembly());}}
Logger:
public interface IMyLogger:IDependService{void Write(string log);}
public class MyLogger : IMyLogger{public void Write(string log){Console.WriteLine(log);}}
public class LoggerModule : BaseModule{public override void Register(){Container.Register(Assembly.GetExecutingAssembly());}}
Web ,这个模块主要是 继承 HttpApplication 实现自己的 Application_Start ,这样我们可以在实际的web应用中指定启动module方法,以便在程序启动时,开启模块初始化。像这个模块注册虽然注册了,但也没有什么服务能别用到的,习惯当作一个模块。
public class MyWebApplication<StartModule> :System.Web.HttpApplication where StartModule:BaseModule{protected virtual void Application_Start(){ModuleStarter.Start<StartModule>();}}public class WebModule : BaseModule{public override void Register(){Container.Register(Assembly.GetExecutingAssembly());}}
Web.Mvc 这个模块主要是在 Web模块的基础上,实现对 controller创建器做更改,更改为通过自己的容器创建。
public class MyControllerFactory: DefaultControllerFactory{private IIocContainer iocContainer;public MyControllerFactory(IIocContainer iocContainer){this.iocContainer = iocContainer;}protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType){try{return iocContainer.GetInstance(controllerType) as IController;}catch (Exception){return base.GetControllerInstance(requestContext, controllerType);}}}public class MyWebMvcApplication<StartModule>: MyWebApplication<StartModule> where StartModule:BaseModule{protected override void Application_Start(){base.Application_Start();ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory(ModuleStarter.GetContainer()));AreaRegistration.RegisterAllAreas();}}[DependOn(typeof(WebModule))]public class WebMvcModule : BaseModule{public override void Register(){Container.Register(Assembly.GetExecutingAssembly());}}
最后一个,就是我们的应用层面:WebApplication1,在这里,一个是注册controller,一个是 更改 Global.asax 中 继承 的 application,同时传入启动module为自身。
[DependOn(typeof(WebMvcModule),typeof(EncryptModule),typeof(LoggerModule))]public class ApplicationModule : BaseModule{public override void Register(){var assembly = Assembly.GetExecutingAssembly();Container.Register(assembly);//注册控制器var controllerTypes = assembly.GetTypes().Where(t => t.Name.EndsWith("Controller"));foreach (var item in controllerTypes){Container.Register(item);}}}public class MvcApplication : MyWebMvcApplication<ApplicationModule>{protected override void Application_Start(){base.Application_Start();RouteConfig.RegisterRoutes(RouteTable.Routes);}}//然后 控制器直接这么用public class HomeController : Controller{private IEncryptHelper encrypt;private IMyLogger myLogger;public HomeController(IEncryptHelper encrypt, IIocContainer iocContainer){myLogger = iocContainer.GetInstance<IMyLogger>();this.encrypt = encrypt;}// GET: Homepublic ActionResult Index(){ViewBag.Logger = myLogger?.ToString();ViewBag.Encrypt = encrypt?.ToString();var dd = encrypt.EncryptData("ssss");ViewBag.Data = dd;return View();}
我们注意到,我们的应用层不需要去管类型注册,直接在应用中使用 IMyLogger IEncryptHelper 这两个其它模块的服务 ,类型的注册在各自module初始化时都自己进行了处理,各司其职。
效果:
至此也差不多了,以上仅作为学习参考。
--------源码参考--------
Abp学习笔记---轻松搞懂模块相关推荐
- Python学习,轻松搞懂Python递归函数的原理与应用
递归: 在函数的定义中,函数内部的语句调用函数本身. 1.递归的原理 学习任何计算机语言过程中,"递归"一直是所有人心中的疼.不知你是否听过这个冷笑话:"一个面包,走着走 ...
- Python学习笔记:常用第三方模块(1)
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- ES6学习——一文搞懂ES6
ES6学习--一文搞懂ES6 es6介绍 ES全称EcmaScript,是脚本语言的规范,而平时经常编写的EcmaScript的一种实现,所以ES新特性其实就是指JavaScript的新特性. 为什么 ...
- php service原理,轻松搞懂WebService工作原理
用更简单的方式给大家谈谈WebService,让你更快更容易理解,希望对初学者有所帮助. WebService是基于网络的.分布式的模块化组件. 我们直接来看WebService的一个简易工作流程: ...
- Python学习笔记:常用第三方模块3
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- Python学习笔记:常用内建模块7XML
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- Python学习笔记:常用内建模块5
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- Python学习笔记:常用内建模块2:collections
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- proe常用c语言语句,带你轻松搞懂Proe条件语句
原标题:带你轻松搞懂Proe条件语句 本文通过几个简单的例子介绍Proe中的条件语句,希望对你能有所帮助.Proe中使用的IF条件语句和C语言中的IF语句原理是一样的,其结构稍有差别.首先我们了解一下 ...
最新文章
- Hybird混合开发APP初学体验
- numpy matplotlib
- 数据挖掘系列(5)使用mahout做海量数据关联规则挖掘
- DYNP_VALUES_READ
- 一文彻底搞懂Java中的值传递和引用传递!
- Python操作文件文档
- java基础覆盖——知识库搭建-1
- 深圳地图echarts
- 定制家具设计拆单用什么软件好?
- ISO27001(BS7799/ISO17799)国标
- c语言函数能返回结构体,从C中的函数返回`struct`
- 管理员后台页面html代码,HTML5技术实现的管理员后台模板界面
- IE8兼容html5视频播放
- can总线不加末端电阻_【干货】80%修理工不知道的CAN线电阻知识点
- jpa 人大金仓数据库方言_KDC——人大金仓数据库比对工具
- MyBatis之ResultMap结果集映射
- Ubuntu安装Google Chrome,报NSS version的错误
- 从大脑结构到人工神经网络
- 全面认识硬盘格式化 详解硬盘低格操作方法
- 分享一个简洁大气的门户网站模版
热门文章
- 游戏建模入门教程方法
- 汽车车身修复技术【5】
- 密码学系列之:csrf跨站点请求伪造
- ubuntu VScode 无法输入中文
- 异常检测论文阅读《PANDA: Adapting Pretrained Features for Anomaly Detection and Segmentation》
- 解析五育融合之下的steam教育模式
- Kind Kubernetes | 通过 Helm 部署定制化 Prometheus-Operator 上传 Dockerhub?
- 关于ReportViewer打印多一页空白页
- 14-TDengine安装报警模块实现报警监测Webhook回调与邮件推送
- 自签名免费(SSL)私有证书