按步骤看:
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. [Hadoop大数据]——Hive初识

    Hive出现的背景 Hadoop提供了大数据的通用解决方案,比如存储提供了Hdfs,计算提供了MapReduce思想.但是想要写出MapReduce算法还是比较繁琐的,对于开发者来说,需要了解底层的h ...

  2. Event的Propagate

    SSIS Package的Executable存在层次结构,例如Package位于层次结构的最顶层,Root Level:Container是其中包含的Executable(Task 或 Contai ...

  3. HTML5系列:HTML5与HTML4的区别

    1. 语法的改变 1.1 DOCTYPE声明 DOCTYPE声明在HTML文件中必不可少,位于文件第一行. HTML4中声明方法: <!DOCTYPE html PUBLIC "-// ...

  4. Qt Undo Framework Demo

    Qt Undo Framework Demo eryar@163.com Abstract. Qt’s Undo Framework is an implementation of the Comma ...

  5. 连接第二个 insance 到 first_local_net - 每天5分钟玩转 OpenStack(83)

    上一节在 first_local_net 中已经部署了 cirros-vm1,今天将再部署一个instance,并验证两个 instance 的连通性. 以同样的方式 launch instance ...

  6. 使用jQuery的animate方法制作滑动菜单

    周末看Ziv小威的博客<制作滑动条菜单,如何延时处理滑动效果,避免动画卡顿>,参见地址:http://www.cnblogs.com/zivxiaowei/p/3462964.html.是 ...

  7. Javascript对象的方法赋值

    Javascript对象编程学习中,一直不能很好的掌握对象的属性(property)和方法(method).今天在写代码过程中,又犯了一个低级错误. <!DOCTYPE html> < ...

  8. MVC中处理表单提交的方式(Ajax+Jquery)

    MVC中处理表单有很多种方法,这里说到第一种方式:Ajax+Jquery 先看下表单: <form class="row form-body form-horizontal m-t&q ...

  9. es6新特性分享

    1.字符串查找es5使用是indexOf() 返回字符第一次出现的位置int值es6新增了3个方法:includes()/startsWith()/endWith()返回bool值includes = ...

  10. 6-tips-for-managing-property-files-with-spring--转

    原文地址:http://www.summa.com/blog/2009/04/20/6-tips-for-managing-property-files-with-spring What could ...