按步骤看:
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; }
这个接口要求含有一个菜单字典和一个主菜单定义
这个类的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 vNext 源码分析] - 文章目录
一.简要介绍 ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 2 月 18 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.16.0 ,但还属于预览版 ...
- [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)
一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...
- [Abp vNext 源码分析] - 3. 依赖注入与拦截器
一.简要说明 ABP vNext 框架在使用依赖注入服务的时候,是直接使用的微软提供的 Microsoft.Extensions.DependencyInjection 包.这里与原来的 ABP 框架 ...
- [Abp vNext 源码分析] - 2. 模块系统的变化
一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...
- [Abp vNext 源码分析] - 1. 框架启动流程分析
一.简要说明 本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的.总的来说 ,Abp vNext 比起 ABP 框架 ...
- [Abp vNext 源码分析] - 4. 工作单元
一.简要说明 统一工作单元是一个比较重要的基础设施组件,它负责管理整个业务流程当中涉及到的数据库事务,一旦某个环节出现异常自动进行回滚处理. 在 ABP vNext 框架当中,工作单元被独立出来作为一 ...
- [Abp vNext 源码分析] - 6. DDD 的应用层支持 (应用服务)
一.简要介绍 ABP vNext 针对于应用服务层,为我们单独设计了一个模块进行实现,即 Volo.Abp.Ddd.Application 模块. PS:最近博主也是在恶补 DDD 相关的知识,这里推 ...
- [Abp vNext 源码分析] - 7. 权限与验证
一.简要说明 在上篇文章里面,我们在 ApplicationService 当中看到了权限检测代码,通过注入 IAuthorizationService 就可以实现权限检测.不过跳转到源码才发现,这个 ...
- [Abp vNext 源码分析] - 9. 接口参数的验证
一.简要说明 ABP vNext 当中的审计模块早在 依赖注入与拦截器一文中有所提及,但没有详细的对其进行分析. 审计模块是 ABP vNext 框架的一个基本组件,它能够提供一些实用日志记录.不过这 ...
随机推荐
- Pycharm5注册方式
0x1 ,安装 0x2 , 调整时间到2038年. 0x3 ,申请30天试用 0x4, 退出pycharm 0x5, 时间调整回来. ##注册方法2### 注册方法: 在 注册时选择 Licen ...
- Mysql命令show global status求根溯源
近来,发现好多公司对mysql的性能监控是通过show global status实现的,因此对于这个命令想要探究一番,看他是否是实时更新的. 在此之前,我们必须搞明白mysql对于这个命令的执行过程 ...
- android:windowSoftInputMode属性详解
android:windowSoftInputMode activity主窗口与软键盘的交互模式,可以用来避免输入法面板遮挡问题,Android1.5后的一个新特性. 这个属性能影响两件事情: [一] ...
- Notes:indexedDB使用
indexedDB是浏览器端保存结构化数据的一种数据库,类似于mysql,oracle等数据库,但indexedDB使用对象存储数据,而不是用表. indexedDB是全局的宿主对象,使用window ...
- 给Easyui combobox设定默认值
今天做到那个北理工二期的项目,里面刚好有几个dialog需要弄一个默认值,一般是选择启用与否,但是,为了方便用户,最好有一个默认值,所以,增加一个默认值的属性.代码入下: JS代码: ...
- 站在巨人的肩膀上---重新自定义 android- ExpandableListView 收缩类,实现列表的可收缩扩展
距离上次更新博客,时隔略长,诸事繁琐,赶在去广州答辩之前,分享下安卓 android 中的一个 列表收缩 类---ExpandableListView 先上效果图: 如果想直接看实现此页面的代码请下滑 ...
- 跨域post请求实现方案小结--转
[名词解释] 跨域:https://developer.mozilla.org/en-US/docs/JavaScript/Same_origin_policy_for_JavaScript 同源策略 ...
- GIS项目中数据开源、工具开源、开发开源的解决方案
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 摆脱免费地图开发包的约束,拒绝商业地图软件的费用,高效.精确.完备是我 ...
- JAVA中关于锁机制
本文转自 http://blog.csdn.net/yangzhijun_cau/article/details/6432216 一段synchronized的代码被一个线程执行之前,他要先拿到执行这 ...
- 【Java基础】并发
Num1:同步访问共享的可变数据 关键字Synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块.. 同步不仅仅理解为互斥的方式,如果没有同步,一个线程的变化就不能 ...