惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V
Visual Studio Blog
T
The Blog of Author Tim Ferriss
GbyAI
GbyAI
Y
Y Combinator Blog
雷峰网
雷峰网
Last Week in AI
Last Week in AI
Jina AI
Jina AI
月光博客
月光博客
G
Google Developers Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
博客园 - 三生石上(FineUI控件)
Hacker News - Newest:
Hacker News - Newest: "LLM"
N
News | PayPal Newsroom
H
Heimdal Security Blog
Recorded Future
Recorded Future
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
腾讯CDC
AWS News Blog
AWS News Blog
NISL@THU
NISL@THU
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 【当耐特】
P
Privacy International News Feed
I
Intezer
V
Vulnerabilities – Threatpost
The GitHub Blog
The GitHub Blog
L
LINUX DO - 最新话题
S
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
小众软件
小众软件
博客园 - 聂微东
V2EX - 技术
V2EX - 技术
W
WeLiveSecurity
Security Latest
Security Latest
PCI Perspectives
PCI Perspectives
The Hacker News
The Hacker News
T
Threatpost
C
Check Point Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Latest news
Latest news
L
LINUX DO - 热门话题
J
Java Code Geeks
A
Arctic Wolf
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Troy Hunt's Blog

博客园 - 开发手游啦啦啦

用TypeScript开发爬虫程序 ABP的语言切换 AngularJs自定义指令详解(10) - 执行次序 AngularJs自定义指令详解(9) - terminal AngularJs自定义指令详解(8) - priority AngularJs自定义指令详解(7) - multiElement AngularJs自定义指令详解(6) - controller、require AngularJs自定义指令详解(5) - link AngularJs自定义指令详解(4) - transclude AngularJs自定义指令详解(3) - scope AngularJs自定义指令详解(2) - template AngularJs自定义指令详解(1) - restrict ABP的Zero Sample ABP的工作单元 ABP的数据过滤器(Data Filters) ABP的事件总线和领域事件(EventBus & Domain Events) 初入Cocos2d-x 2.2 quick-cocos2d-x之testlua之VisibleRect.lua quick-cocos2d-x之testlua之mainMenu.lua
ABP导航源码分析
开发手游啦啦啦 · 2015-07-27 · via 博客园 - 开发手游啦啦啦

按步骤看:

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列表。

实现类:MenuDefinitionMenuItemDefinition

前者是根菜单,后者是子菜单,而子菜单也还包含子菜单,形成树状结构。

两个类都有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),具体类是UserMenuUserMenuItem

查看两个具体类的构造函数就知道它们是通过对应的定义类来初始化的。

(菜单定义和用户菜单二者必须分开,原因很简单,用户菜单由于权限等原因,可能会排除掉一些对于特定用户来说无权限的菜单项,也就是对该用户不予展示,因此菜单定义和用户菜单需要区别对待。详情参看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);

        }