做.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学习笔记---轻松搞懂模块相关推荐

  1. Python学习,轻松搞懂Python递归函数的原理与应用

    递归: 在函数的定义中,函数内部的语句调用函数本身. 1.递归的原理 学习任何计算机语言过程中,"递归"一直是所有人心中的疼.不知你是否听过这个冷笑话:"一个面包,走着走 ...

  2. Python学习笔记:常用第三方模块(1)

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  3. ES6学习——一文搞懂ES6

    ES6学习--一文搞懂ES6 es6介绍 ES全称EcmaScript,是脚本语言的规范,而平时经常编写的EcmaScript的一种实现,所以ES新特性其实就是指JavaScript的新特性. 为什么 ...

  4. php service原理,轻松搞懂WebService工作原理

    用更简单的方式给大家谈谈WebService,让你更快更容易理解,希望对初学者有所帮助. WebService是基于网络的.分布式的模块化组件. 我们直接来看WebService的一个简易工作流程: ...

  5. Python学习笔记:常用第三方模块3

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  6. Python学习笔记:常用内建模块7XML

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  7. Python学习笔记:常用内建模块5

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  8. Python学习笔记:常用内建模块2:collections

    前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...

  9. proe常用c语言语句,带你轻松搞懂Proe条件语句

    原标题:带你轻松搞懂Proe条件语句 本文通过几个简单的例子介绍Proe中的条件语句,希望对你能有所帮助.Proe中使用的IF条件语句和C语言中的IF语句原理是一样的,其结构稍有差别.首先我们了解一下 ...

最新文章

  1. Hybird混合开发APP初学体验
  2. numpy matplotlib
  3. 数据挖掘系列(5)使用mahout做海量数据关联规则挖掘
  4. DYNP_VALUES_READ
  5. 一文彻底搞懂Java中的值传递和引用传递!
  6. Python操作文件文档
  7. java基础覆盖——知识库搭建-1
  8. 深圳地图echarts
  9. 定制家具设计拆单用什么软件好?
  10. ISO27001(BS7799/ISO17799)国标
  11. c语言函数能返回结构体,从C中的函数返回`struct`
  12. 管理员后台页面html代码,HTML5技术实现的管理员后台模板界面
  13. IE8兼容html5视频播放
  14. can总线不加末端电阻_【干货】80%修理工不知道的CAN线电阻知识点
  15. jpa 人大金仓数据库方言_KDC——人大金仓数据库比对工具
  16. MyBatis之ResultMap结果集映射
  17. Ubuntu安装Google Chrome,报NSS version的错误
  18. 从大脑结构到人工神经网络
  19. 全面认识硬盘格式化 详解硬盘低格操作方法
  20. 分享一个简洁大气的门户网站模版

热门文章

  1. 游戏建模入门教程方法
  2. 汽车车身修复技术【5】
  3. 密码学系列之:csrf跨站点请求伪造
  4. ubuntu VScode 无法输入中文
  5. 异常检测论文阅读《PANDA: Adapting Pretrained Features for Anomaly Detection and Segmentation》
  6. 解析五育融合之下的steam教育模式
  7. Kind Kubernetes | 通过 Helm 部署定制化 Prometheus-Operator 上传 Dockerhub?
  8. 关于ReportViewer打印多一页空白页
  9. 14-TDengine安装报警模块实现报警监测Webhook回调与邮件推送
  10. 自签名免费(SSL)私有证书