按步骤看:
1,在Global.asax中执行:
base.Application_Start(sender, e);
2,在AbpWebApplication类的Application_Start()中执行:
AbpBootstrapper.Initialize();
3,在AbpBootstrapper.Initialize()中执行:
IocManager.Resolve<AbpStartupConfiguration>().Initialize();
4,在AbpStartupConfiguration.Initialize()中执行:
 Navigation = IocManager.Resolve<INavigationConfiguration>();
至此,我们获得一个INavigationConfiguration实例,该实例拥有一个NavigationProvider列表(暂时是空列表)
5,回到第3步,在AbpBootstrapper.Initialize()中继续执行:
 _moduleManager.InitializeModules();
6,在AbpModuleManager的InitializeModules()中执行:
sortedModules.ForEach(module => module.Instance.PreInitialize());
也就是所有模块的PreInitialize()方法都逐个执行一遍。
7,于是转到XXXWebModule(XXX为应用程序名)模块的PreInitialize()方法会执行:
 Configuration.Navigation.Providers.Add<XXXNavigationProvider>();
至此,原来还是空的NavigationProvider列表被填充进来一个XXXNavigationProvider。
8,回到第6步,在AbpModuleManager的InitializeModules()中继续执行:
 sortedModules.ForEach(module => module.Instance.PostInitialize());
也就是所有模块的PostInitialize()方法都逐个执行一遍。
9,于是转到AbpKernelModule模块的PostInitialize()方法,执行:
 IocManager.Resolve<NavigationManager>().Initialize();
10,NavigationManager的Initialize()方法遍历NavigationProvider列表,并执行NavigationProvider的SetNavigation()方法。
11,在XXXNavigationProvider的SetNavigation()方法里,构造了一个菜单的树状结构。
 
按接口和类看:
IHasMenuItemDefinitions接口:
声明了属性:IList<MenuItemDefinition> Items { get; }
这个接口要求必须含有一个MenuItemDefinition列表。
实现类:MenuDefinition、MenuItemDefinition
前者是根菜单,后者是子菜单,而子菜单也还包含子菜单,形成树状结构。
两个类都有AddItem方法。
MenuDefinition只增加了Name和DisplayName两个属性,而MenuItemDefinition更增加了Order、Icon、Url、RequiredPermissionName、RequiresAuthentication、IsLeaf属性。
两个类都实现IhasMenuItemDefinitions接口的好处,体现在HasMenuItemDefinitionsExtensions类中的两个方法:
GetItemByName、GetItemByNameOrNull,这两个方法都是根据传入的IhasMenuItemDefinitions对象和菜单项名称去查询一个MenuItemDefinition对象。遍历IhasMenuItemDefinitions对象下的Items,加上使用递归,就能实现这个查询了。
若获得的MenuItemDefinition为null,第一个方法会抛出异常,第二个方法直接返回null
另外,MenuItemDefinitionExtensions类为处理MenuItemDefinition增加了一些实用方法。
不过HasMenuItemDefinitionsExtensions和MenuItemDefinitionExtensions里定义的方法暂时都没用使用到,先不用理会。
注意MenuDefinition、MenuItemDefinition类都是对菜单的定义(Definition),具体类是UserMenu和UserMenuItem
查看两个具体类的构造函数就知道它们是通过对应的定义类来初始化的。
(菜单定义和用户菜单二者必须分开,原因很简单,用户菜单由于权限等原因,可能会排除掉一些对于特定用户来说无权限的菜单项,也就是对该用户不予展示,因此菜单定义和用户菜单需要区别对待。详情参看UserNavigationManager类的FillUserMenuItems方法)
 
INavigationManager接口:
 IDictionary<string, MenuDefinition> Menus { get; }
MenuDefinition MainMenu { get; }
这个接口要求含有一个菜单字典和一个主菜单定义
实现类:NavigationManager
这个类的Initialize()方法在AbpKernelModule类的PostInitialize方法中被调用:
 IocManager.Resolve<NavigationManager>().Initialize();
该方法的作用是遍历NavigationProvider(导航菜单的数据提供者)列表,并执行NavigationProvider的SetNavigation方法,如此一来,Menus和MainMenu就填充进数据了。
 
INavigationProviderContext接口:
INavigationManager Manager { get; }
这个接口要求含有一个INavigationManager实例。
实现类:NavigationProviderContext
很简单,就是通过构造函数传参,初始化Manager (IoC注入)
NavigationProvider抽象类:
声明了一个 SetNavigation(INavigationProviderContext context)方法。
这个抽象类是需要在具体项目中上实现的,例如在项目XXX.Web下:
    public class XXXNavigationProvider : NavigationProvider
    {
        public override void SetNavigation(INavigationProviderContext context)
        {
            context.Manager.MainMenu
                .AddItem(
                    new MenuItemDefinition(
                        "Home",
                        new LocalizableString("HomePage", XXXConsts.LocalizationSourceName),
                        url: "/",
                        icon: ""
                        )
                );
        }
    }
 
 
INavigationConfiguration接口:
ITypeList<NavigationProvider> Providers { get; }
这个接口声明了一个NavigationProvider列表。
实现类:NavigationConfiguration
也就是在构造函数里简单地初始化Providers 为一个空列表。
对这个列表的填充,是在XXXWebModule的PreInitialize()中进行的:
Configuration.Navigation.Providers.Add<XXXNavigationProvider>();

当然,我们在这里还可以填充更多的Provider,考虑到一个网站不止一个导航结构,可能还有第二个、第三个导航结构。

XXXNavigationProvider 使用代码方法构造一个树状结构,当然也可以通过读取XML配置来构造之。
Providers 会在NavigationManager中被遍历,并执行其SetNavigation方法,初始化导航的菜单定义及其树状结构。
 
IUserNavigationManager接口:
 Task<UserMenu> GetMenuAsync(string menuName, long? userId);
Task<IReadOnlyList<UserMenu>> GetMenusAsync(long? userId);
这个接口声明了获取用户菜单的两个方法,前者获取一个菜单,后者获取菜单列表。
实现类:UserNavigationManager
GetMenuAsync方法的实现:通过传入的menuName,在INavigationManager实例的Menus中获取对应的MenuDefinition对象,然后使用这个对象来初始化一个UserMenu
这还不够,因为UserMenu内还会包含子菜单,也就是UserMenu的Items属性需要初始化。
查看代码可知,在FillUserMenuItems方法内,遍历了MenuDefinition对象内的子菜单定义列表,对用户的权限进行了检查,如有权限,就加入到UserMenu的子菜单里。通过递归FillUserMenuItems,用户菜单的树状结构也就建立了。
GetMenusAsync方法的实现:遍历INavigationManager实例的Menus,把所有根菜单都通过上面的GetMenuAsync方法初始化出对应的UserMenu对象。这个方法正是Web项目中使用到的方法,在LayoutController控制器中可以看到:
        public PartialViewResult TopMenu(string activeMenu = "")
        {
            var model = new TopMenuViewModel
                        {
                            MainMenu = AsyncHelper.RunSync(() => _userNavigationManager.GetMenuAsync("MainMenu", CurrentSession.UserId)),
                            ActiveMenuItemName = activeMenu
                        };
 
            return PartialView("_TopMenu", model);
        }

ABP导航源码分析的更多相关文章

  1. [Abp vNext 源码分析] - 文章目录

    一.简要介绍 ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 2 月 18 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.16.0 ,但还属于预览版 ...

  2. [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)

    一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...

  3. [Abp vNext 源码分析] - 3. 依赖注入与拦截器

    一.简要说明 ABP vNext 框架在使用依赖注入服务的时候,是直接使用的微软提供的 Microsoft.Extensions.DependencyInjection 包.这里与原来的 ABP 框架 ...

  4. [Abp vNext 源码分析] - 2. 模块系统的变化

    一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...

  5. [Abp vNext 源码分析] - 1. 框架启动流程分析

    一.简要说明 本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的.总的来说 ,Abp vNext 比起 ABP 框架 ...

  6. [Abp vNext 源码分析] - 4. 工作单元

    一.简要说明 统一工作单元是一个比较重要的基础设施组件,它负责管理整个业务流程当中涉及到的数据库事务,一旦某个环节出现异常自动进行回滚处理. 在 ABP vNext 框架当中,工作单元被独立出来作为一 ...

  7. [Abp vNext 源码分析] - 6. DDD 的应用层支持 (应用服务)

    一.简要介绍 ABP vNext 针对于应用服务层,为我们单独设计了一个模块进行实现,即 Volo.Abp.Ddd.Application 模块. PS:最近博主也是在恶补 DDD 相关的知识,这里推 ...

  8. [Abp vNext 源码分析] - 7. 权限与验证

    一.简要说明 在上篇文章里面,我们在 ApplicationService 当中看到了权限检测代码,通过注入 IAuthorizationService 就可以实现权限检测.不过跳转到源码才发现,这个 ...

  9. [Abp vNext 源码分析] - 9. 接口参数的验证

    一.简要说明 ABP vNext 当中的审计模块早在 依赖注入与拦截器一文中有所提及,但没有详细的对其进行分析. 审计模块是 ABP vNext 框架的一个基本组件,它能够提供一些实用日志记录.不过这 ...

随机推荐

  1. 详解 JavaScript的 call() 和 apply()

    定义 ECMAScript规范为所有函数都包含两个方法(这两个方法非继承而来), call 和 apply .这两个函数都是在特定的作用域中调用函数,能改变函数的作用域,实际上是改变函数体内 this ...

  2. Atitit 软件开发中 瓦哈比派的核心含义以及修行方法以及对我们生活与工作中的指导意义

    Atitit 软件开发中 瓦哈比派的核心含义以及修行方法以及对我们生活与工作中的指导意义 首先我们指明,任何一种行动以及教派修行方法都有他的多元化,只看到某一方面,就不能很好的评估利弊,适不适合自己使 ...

  3. DB的IO统计

    对数据的IO操作,都是写入到数据库文件中,sys.dm_io_virtual_file_stats Returns I/O statistics for data and log files. sys ...

  4. SSIS的 Data Flow 和 Control Flow

    Control Flow 和 Data Flow,是SSIS Design中主要用到的两个Tab,理解这两个Tab的作用,对设计更高效的package十分重要. 一,Control Flow 在Con ...

  5. 【前端性能】必须要掌握的原生JS实现JQuery

    很多时候,我们经常听见有人说jquery有多快多快.在这个各种类库满天飞的时候,不得不说的是,能有原生JS快吗? 是的,明显原生JS要更快,因为诸如JQuery这样的库必须要兼容各种浏览器和低版本和许 ...

  6. react+redux教程(一)connect、applyMiddleware、thunk、webpackHotMiddleware

    今天,我们通过解读官方示例代码(counter)的方式来学习react+redux. 例子 这个例子是官方的例子,计数器程序.前两个按钮是加减,第三个是如果当前数字是奇数则加一,第四个按钮是异步加一( ...

  7. JavaScript中Promises/A+规范的实现

    Promises是一种异步编程模型,通过一组API来规范化异步操作,这样也能够让异步操作的流程控制更加容易. 下面的代码是假设执行一个异步队列,每一项都会使用上一项返回的数据: function ne ...

  8. PC网站CSS分享

    这里想总结些编写网页的经验,以PC端的为主,前面总结过后台的,今天写些前台的,参考了bootstrap3.3.5.bootstrip2.3.2和模版matrix. 前段时间还写过一篇<前端基础学 ...

  9. JavaWeb路径问题打包总结--小心出门右转404

    话说,培训和自学就不是一个回事,两周讲完java基础,两天讲完jsp,两节课讲完servlet,还真不是一般人能受得了的,这两天学习jsp和servlet频繁被路径问题困扰,倒不是出错,只是各种act ...

  10. 数据库SQL Service 2014中文版的安装和配置教程

    一.我的电脑环境 1.windows8.1(64位) 2.之前电脑没有安装数据库的软件 二.装机之前准备(我这儿提供百度云保存和下载) 1.下载一个“Sql service 2014中文版” http ...