ABP中模块初始化过程(二)
在上一篇介绍在StartUp类中的ConfigureService()中的AddAbp方法后我们再来重点说一说在Configure()方法中的UserAbp()方法,还是和前面的一样我们来通过代码来进行一步步分析。
public static class AbpApplicationBuilderExtensions
{
public static void UseAbp(this IApplicationBuilder app)
{
app.UseAbp(null);
} public static void UseAbp([NotNull] this IApplicationBuilder app, Action<AbpApplicationBuilderOptions> optionsAction)
{
Check.NotNull(app, nameof(app)); var options = new AbpApplicationBuilderOptions();
optionsAction?.Invoke(options); if (options.UseCastleLoggerFactory)
{
app.UseCastleLoggerFactory();
} InitializeAbp(app); if (options.UseAbpRequestLocalization)
{
//TODO: This should be added later than authorization middleware!
app.UseAbpRequestLocalization();
} if (options.UseSecurityHeaders)
{
app.UseAbpSecurityHeaders();
}
} public static void UseEmbeddedFiles(this IApplicationBuilder app)
{
app.UseStaticFiles(
new StaticFileOptions
{
FileProvider = new EmbeddedResourceFileProvider(
app.ApplicationServices.GetRequiredService<IIocResolver>()
)
}
);
} private static void InitializeAbp(IApplicationBuilder app)
{
var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>();
abpBootstrapper.Initialize(); var applicationLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(() => abpBootstrapper.Dispose());
} public static void UseCastleLoggerFactory(this IApplicationBuilder app)
{
var castleLoggerFactory = app.ApplicationServices.GetService<Castle.Core.Logging.ILoggerFactory>();
if (castleLoggerFactory == null)
{
return;
} app.ApplicationServices
.GetRequiredService<ILoggerFactory>()
.AddCastleLogger(castleLoggerFactory);
} public static void UseAbpRequestLocalization(this IApplicationBuilder app, Action<RequestLocalizationOptions> optionsAction = null)
{
var iocResolver = app.ApplicationServices.GetRequiredService<IIocResolver>();
using (var languageManager = iocResolver.ResolveAsDisposable<ILanguageManager>())
{
var supportedCultures = languageManager.Object
.GetLanguages()
.Select(l => CultureInfo.GetCultureInfo(l.Name))
.ToArray(); var options = new RequestLocalizationOptions
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
}; var userProvider = new AbpUserRequestCultureProvider(); //0: QueryStringRequestCultureProvider
options.RequestCultureProviders.Insert(1, userProvider);
options.RequestCultureProviders.Insert(2, new AbpLocalizationHeaderRequestCultureProvider());
//3: CookieRequestCultureProvider
options.RequestCultureProviders.Insert(4, new AbpDefaultRequestCultureProvider());
//5: AcceptLanguageHeaderRequestCultureProvider optionsAction?.Invoke(options); userProvider.CookieProvider = options.RequestCultureProviders.OfType<CookieRequestCultureProvider>().FirstOrDefault();
userProvider.HeaderProvider = options.RequestCultureProviders.OfType<AbpLocalizationHeaderRequestCultureProvider>().FirstOrDefault(); app.UseRequestLocalization(options);
}
} public static void UseAbpSecurityHeaders(this IApplicationBuilder app)
{
app.UseMiddleware<AbpSecurityHeadersMiddleware>();
}
}
在这个方法中首先也是检查传入的IApplicationBuilder是否为null,如果为空则抛出异常,在检查完之后会创建一个AbpApplicationBuilderOptions类的实例,我们来看看在这个类中做了哪些事情?
public AbpApplicationBuilderOptions()
{
UseCastleLoggerFactory = true;
UseAbpRequestLocalization = true;
UseSecurityHeaders = true;
}
这里比较简单就是对定义的几个属性进行初始化操作。在完成初始化操作后,首先就是定义ABP的创建Logger的工厂类,通过这个工厂类创建Logger的实例,这个主要是记录ABP的日志,这里面具体的日志使用的是log4net,具体输出日志的方式可以在log4net.config里面进行配置。
在配置完成后最重要的一步就是执行InitializeAbp方法,我们来看看这个过程中执行了哪些方法。
private static void InitializeAbp(IApplicationBuilder app)
{
var abpBootstrapper = app.ApplicationServices.GetRequiredService<AbpBootstrapper>();
abpBootstrapper.Initialize(); var applicationLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(() => abpBootstrapper.Dispose());
}
在这个初始化Abp方法中,首先就是通过app.ApplicationServices.GetRequiredService<AbpBootstrapper>()这个方法来获取到之前唯一的abpBootstrapper,获取到这个唯一的实例后就可以调用这个实例的Initialize方法。
/// <summary>
/// Initializes the ABP system.
/// </summary>
public virtual void Initialize()
{
ResolveLogger(); try
{
RegisterBootstrapper();
IocManager.IocContainer.Install(new AbpCoreInstaller()); IocManager.Resolve<AbpPlugInManager>().PlugInSources.AddRange(PlugInSources);
IocManager.Resolve<AbpStartupConfiguration>().Initialize(); _moduleManager = IocManager.Resolve<AbpModuleManager>();
_moduleManager.Initialize(StartupModule);
_moduleManager.StartModules();
}
catch (Exception ex)
{
_logger.Fatal(ex.ToString(), ex);
throw;
}
}
在整个初始化的过程中,第一步就是创建整个ABP中唯一的Logger对象,这个主要是用来记录系统的日志文件,这个具体创建方法就是通过IoCContainer容器来获取到之前注入的创建Logger日志的工厂类,然后调用它的Create方法来创建这个记录日志的对象。
private void ResolveLogger()
{
if (IocManager.IsRegistered<ILoggerFactory>())
{
_logger = IocManager.Resolve<ILoggerFactory>().Create(typeof(AbpBootstrapper));
}
}
在创建完日志之后,第一步就是用于判断当前的AbpBootrsrtapper类的实例有没有注入到整个依赖注入容器中,如果没有注入过则将当前唯一的实例this注入到容器中去。
private void RegisterBootstrapper()
{
if (!IocManager.IsRegistered<AbpBootstrapper>())
{
IocManager.IocContainer.Register(
Component.For<AbpBootstrapper>().Instance(this)
);
}
}
再接下来第一步就是执行IocManager.IocContainer.Install(new AbpCoreInstaller())这一步操作,这一步的主要作用是注册系统框架级的所有配置类,我们通过实际的代码来看看,在初始化整个ABP系统时候到底注册了哪些系统框架级别的配置类。
internal class AbpCoreInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IUnitOfWorkDefaultOptions, UnitOfWorkDefaultOptions>().ImplementedBy<UnitOfWorkDefaultOptions>().LifestyleSingleton(),
Component.For<INavigationConfiguration, NavigationConfiguration>().ImplementedBy<NavigationConfiguration>().LifestyleSingleton(),
Component.For<ILocalizationConfiguration, LocalizationConfiguration>().ImplementedBy<LocalizationConfiguration>().LifestyleSingleton(),
Component.For<IAuthorizationConfiguration, AuthorizationConfiguration>().ImplementedBy<AuthorizationConfiguration>().LifestyleSingleton(),
Component.For<IValidationConfiguration, ValidationConfiguration>().ImplementedBy<ValidationConfiguration>().LifestyleSingleton(),
Component.For<IFeatureConfiguration, FeatureConfiguration>().ImplementedBy<FeatureConfiguration>().LifestyleSingleton(),
Component.For<ISettingsConfiguration, SettingsConfiguration>().ImplementedBy<SettingsConfiguration>().LifestyleSingleton(),
Component.For<IModuleConfigurations, ModuleConfigurations>().ImplementedBy<ModuleConfigurations>().LifestyleSingleton(),
Component.For<IEventBusConfiguration, EventBusConfiguration>().ImplementedBy<EventBusConfiguration>().LifestyleSingleton(),
Component.For<IMultiTenancyConfig, MultiTenancyConfig>().ImplementedBy<MultiTenancyConfig>().LifestyleSingleton(),
Component.For<ICachingConfiguration, CachingConfiguration>().ImplementedBy<CachingConfiguration>().LifestyleSingleton(),
Component.For<IAuditingConfiguration, AuditingConfiguration>().ImplementedBy<AuditingConfiguration>().LifestyleSingleton(),
Component.For<IBackgroundJobConfiguration, BackgroundJobConfiguration>().ImplementedBy<BackgroundJobConfiguration>().LifestyleSingleton(),
Component.For<INotificationConfiguration, NotificationConfiguration>().ImplementedBy<NotificationConfiguration>().LifestyleSingleton(),
Component.For<IEmbeddedResourcesConfiguration, EmbeddedResourcesConfiguration>().ImplementedBy<EmbeddedResourcesConfiguration>().LifestyleSingleton(),
Component.For<IAbpStartupConfiguration, AbpStartupConfiguration>().ImplementedBy<AbpStartupConfiguration>().LifestyleSingleton(),
Component.For<IEntityHistoryConfiguration, EntityHistoryConfiguration>().ImplementedBy<EntityHistoryConfiguration>().LifestyleSingleton(),
Component.For<ITypeFinder, TypeFinder>().ImplementedBy<TypeFinder>().LifestyleSingleton(),
Component.For<IAbpPlugInManager, AbpPlugInManager>().ImplementedBy<AbpPlugInManager>().LifestyleSingleton(),
Component.For<IAbpModuleManager, AbpModuleManager>().ImplementedBy<AbpModuleManager>().LifestyleSingleton(),
Component.For<IAssemblyFinder, AbpAssemblyFinder>().ImplementedBy<AbpAssemblyFinder>().LifestyleSingleton(),
Component.For<ILocalizationManager, LocalizationManager>().ImplementedBy<LocalizationManager>().LifestyleSingleton()
);
}
}
这里面包括框架中各种重要的配置,比如实现整个框架插件化的核心管理类IAbpPlugInManager等等。在将这些核心的框架级别的配置类注入IoC容器中以后就是执行后续的各种操作了。首先第一步就是将放置在特定文件夹下面的实现AbpModule的程序集加载到一个唯一的集合中,这个具体是通过IocManager.Resolve<AbpPlugInManager>().PlugInSources.AddRange(PlugInSources)这个来实现的。
紧接着就是执行IocManager.Resolve<AbpStartupConfiguration>().Initialize(),这个方法主要是用于实例化ABP系统中的各种配置,包括:本地化、模块、验证、多租户配置等一系列配置的初始化,这个在后期都会使用到。关于后面的这些内容,这个系列后续会通过不同章节来分析进行分析和总结。
public void Initialize()
{
Localization = IocManager.Resolve<ILocalizationConfiguration>();
Modules = IocManager.Resolve<IModuleConfigurations>();
Features = IocManager.Resolve<IFeatureConfiguration>();
Navigation = IocManager.Resolve<INavigationConfiguration>();
Authorization = IocManager.Resolve<IAuthorizationConfiguration>();
Validation = IocManager.Resolve<IValidationConfiguration>();
Settings = IocManager.Resolve<ISettingsConfiguration>();
UnitOfWork = IocManager.Resolve<IUnitOfWorkDefaultOptions>();
EventBus = IocManager.Resolve<IEventBusConfiguration>();
MultiTenancy = IocManager.Resolve<IMultiTenancyConfig>();
Auditing = IocManager.Resolve<IAuditingConfiguration>();
Caching = IocManager.Resolve<ICachingConfiguration>();
BackgroundJobs = IocManager.Resolve<IBackgroundJobConfiguration>();
Notifications = IocManager.Resolve<INotificationConfiguration>();
EmbeddedResources = IocManager.Resolve<IEmbeddedResourcesConfiguration>();
EntityHistory = IocManager.Resolve<IEntityHistoryConfiguration>(); CustomConfigProviders = new List<ICustomConfigProvider>();
ServiceReplaceActions = new Dictionary<Type, Action>();
}
在这后面就是整个ABP项目中的重中之重了就是关于各个Module的初始化,这里的主要步骤首先就是获取AbpModuleManager的实例,然后调用该对象的Initialize方法,在执行这个方法时会将整个ABP项目的启动Module(WebHostModule)作为TStartupModule参数传递进去,我们来看看在这个Initialize()方法中执行了哪些重要的事情。
public virtual void Initialize(Type startupModule)
{
_modules = new AbpModuleCollection(startupModule);
LoadAllModules();
}
在这个方法中,首先创建一个AbpModule的集合,然后就开始加载所有的Module了。
private void LoadAllModules()
{
Logger.Debug("Loading Abp modules..."); List<Type> plugInModuleTypes;
var moduleTypes = FindAllModuleTypes(out plugInModuleTypes).Distinct().ToList(); Logger.Debug("Found " + moduleTypes.Count + " ABP modules in total."); RegisterModules(moduleTypes);
CreateModules(moduleTypes, plugInModuleTypes); _modules.EnsureKernelModuleToBeFirst();
_modules.EnsureStartupModuleToBeLast(); SetDependencies(); Logger.DebugFormat("{0} modules loaded.", _modules.Count);
}
在这个函数中,第一步就是找到所有的模块的类型,具体是通过FindAllModuleTypes这个函数来执行的,我们来看看到底是怎么实现的。
private List<Type> FindAllModuleTypes(out List<Type> plugInModuleTypes)
{
plugInModuleTypes = new List<Type>(); var modules = AbpModule.FindDependedModuleTypesRecursivelyIncludingGivenModule(_modules.StartupModuleType); foreach (var plugInModuleType in _abpPlugInManager.PlugInSources.GetAllModules())
{
if (modules.AddIfNotContains(plugInModuleType))
{
plugInModuleTypes.Add(plugInModuleType);
}
} return modules;
}
这里跳过一些步骤,直接看最核心的部分,执行到内部的一个AddModuleAndDependenciesRecursively方法,这个方法是一个迭代方法,在添加每一个Module时首先查找当前Module的依赖Module,如果当前Module已经添加到之前的集合中那么就不再重复添加。
private static void AddModuleAndDependenciesRecursively(List<Type> modules, Type module)
{
if (!IsAbpModule(module))
{
throw new AbpInitializationException("This type is not an ABP module: " + module.AssemblyQualifiedName);
} if (modules.Contains(module))
{
return;
} modules.Add(module); var dependedModules = FindDependedModuleTypes(module);
foreach (var dependedModule in dependedModules)
{
AddModuleAndDependenciesRecursively(modules, dependedModule);
}
}
这里面有一个重点的方法FindDependedModuleTypes方法,我们也来看看其内部的实现。
/// <summary>
/// Finds direct depended modules of a module (excluding given module).
/// </summary>
public static List<Type> FindDependedModuleTypes(Type moduleType)
{
if (!IsAbpModule(moduleType))
{
throw new AbpInitializationException("This type is not an ABP module: " + moduleType.AssemblyQualifiedName);
} var list = new List<Type>(); if (moduleType.GetTypeInfo().IsDefined(typeof(DependsOnAttribute), true))
{
var dependsOnAttributes = moduleType.GetTypeInfo().GetCustomAttributes(typeof(DependsOnAttribute), true).Cast<DependsOnAttribute>();
foreach (var dependsOnAttribute in dependsOnAttributes)
{
foreach (var dependedModuleType in dependsOnAttribute.DependedModuleTypes)
{
list.Add(dependedModuleType);
}
}
} return list;
}
在这个方法中我们首先判断当前类型当前moduleType是否定义了DependsOn的自定义属性,如果定义了这个这个自定义属性,那么首先获取定义的这些Module,然后再将这些ModuleType添加到一个集合中,这样就能够获取到一个ModuleType所依赖的所有其他ModuleType,通过上面的两个过程能够将整个ABP系统中 所有的AbpModule都加载到了一个集合中,而且不会重复。
后面接着执行AbpModuleManager中的StartModules()方法,这个方法中会执行一系列的操作。
public virtual void StartModules()
{
var sortedModules = _modules.GetSortedModuleListByDependency();
sortedModules.ForEach(module => module.Instance.PreInitialize());
sortedModules.ForEach(module => module.Instance.Initialize());
sortedModules.ForEach(module => module.Instance.PostInitialize());
}
分析到这里的时候我们可以看到我们非常熟悉的PreInitialze()、Initialize()、PostInitialize()方法了,在执行这三个虚方法之前,首先要将这些加载到的Module进行一个排序,具体排序规则我们称之为拓扑排序,就是如果A模块加载依赖于B模块,那么B模块就排在A模块的前面,并执行这三个过程。我们来分析一下GetSortedModuleListByDependency这个方法的具体实现。
public List<AbpModuleInfo> GetSortedModuleListByDependency()
{
var sortedModules = this.SortByDependencies(x => x.Dependencies);
EnsureKernelModuleToBeFirst(sortedModules);
EnsureStartupModuleToBeLast(sortedModules, StartupModuleType);
return sortedModules;
}
在这里涉及到三类不同的模块,首先是AbpKenelModule,这个是Abp中最核心的Module、第二类就是普通的业务实现相关的Module、第三类是StartupModule,这个模块会最后进行加载,关于模块之间的先后排列顺序我们可以来通过几个资料来进行了解,首先我们来看看SortByDependencies这个方法的实现。
public static class ListExtensions
{
/// <summary>
/// Sort a list by a topological sorting, which consider their dependencies
/// </summary>
/// <typeparam name="T">The type of the members of values.</typeparam>
/// <param name="source">A list of objects to sort</param>
/// <param name="getDependencies">Function to resolve the dependencies</param>
/// <returns></returns>
public static List<T> SortByDependencies<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getDependencies)
{
/* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp
* http://en.wikipedia.org/wiki/Topological_sorting
*/ var sorted = new List<T>();
var visited = new Dictionary<T, bool>(); foreach (var item in source)
{
SortByDependenciesVisit(item, getDependencies, sorted, visited);
} return sorted;
} /// <summary>
///
/// </summary>
/// <typeparam name="T">The type of the members of values.</typeparam>
/// <param name="item">Item to resolve</param>
/// <param name="getDependencies">Function to resolve the dependencies</param>
/// <param name="sorted">List with the sortet items</param>
/// <param name="visited">Dictionary with the visited items</param>
private static void SortByDependenciesVisit<T>(T item, Func<T, IEnumerable<T>> getDependencies, List<T> sorted, Dictionary<T, bool> visited)
{
bool inProcess;
var alreadyVisited = visited.TryGetValue(item, out inProcess); if (alreadyVisited)
{
if (inProcess)
{
throw new ArgumentException("Cyclic dependency found! Item: " + item);
}
}
else
{
visited[item] = true; var dependencies = getDependencies(item);
if (dependencies != null)
{
foreach (var dependency in dependencies)
{
SortByDependenciesVisit(dependency, getDependencies, sorted, visited);
}
} visited[item] = false;
sorted.Add(item);
}
}
}
除了代码注释中的这个URL涉及到的参考资料以外,可以点击这里参考这篇文章来进一步理解什么是拓扑排序规则。
在这里我们已经基本分析完了整个UseAbp的过程,这个在整个方法中最核心的就是找到所有的Module并进行拓扑排序,排序完成后一次执行定义在基类中的虚方法,最后在UseAbp中涉及的一个部分就是本地化等相关的内容,这个不是整个ABP项目中的核心,但是对整个ABP项目是一个非常重要的补充,这里我们仅仅贴出实现代码,不做具体分析。
if (options.UseAbpRequestLocalization)
{
//TODO: This should be added later than authorization middleware!
app.UseAbpRequestLocalization();
} if (options.UseSecurityHeaders)
{
app.UseAbpSecurityHeaders();
}
这里面UseAbpRequestLocalization()这个方法的实现如下所示。
public static void UseAbpRequestLocalization(this IApplicationBuilder app, Action<RequestLocalizationOptions> optionsAction = null)
{
var iocResolver = app.ApplicationServices.GetRequiredService<IIocResolver>();
using (var languageManager = iocResolver.ResolveAsDisposable<ILanguageManager>())
{
var supportedCultures = languageManager.Object
.GetLanguages()
.Select(l => CultureInfo.GetCultureInfo(l.Name))
.ToArray(); var options = new RequestLocalizationOptions
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
}; var userProvider = new AbpUserRequestCultureProvider(); //0: QueryStringRequestCultureProvider
options.RequestCultureProviders.Insert(1, userProvider);
options.RequestCultureProviders.Insert(2, new AbpLocalizationHeaderRequestCultureProvider());
//3: CookieRequestCultureProvider
options.RequestCultureProviders.Insert(4, new AbpDefaultRequestCultureProvider());
//5: AcceptLanguageHeaderRequestCultureProvider optionsAction?.Invoke(options); userProvider.CookieProvider = options.RequestCultureProviders.OfType<CookieRequestCultureProvider>().FirstOrDefault();
userProvider.HeaderProvider = options.RequestCultureProviders.OfType<AbpLocalizationHeaderRequestCultureProvider>().FirstOrDefault(); app.UseRequestLocalization(options);
}
}
上面就是对整个ABP项目的模块加载及初始化过程做的一个主要的分析,但是在理解这篇文章之前,最好先看之前的一篇文章,从而让自己对整个ABP模块加载有一个更加清楚的认识和理解。
最后,点击这里返回整个ABP系列的主目录。
ABP中模块初始化过程(二)的更多相关文章
- ABP中的模块初始化过程(一)
在总结完整个ABP项目的结构之后,我们就来看一看ABP中这些主要的模块是按照怎样的顺序进行加载的,在加载的过程中我们会一步步分析源代码来进行解释,从而使自己对于整个框架有一个清晰的脉络,在整个Asp. ...
- Abp 中 模块 加载及类型自动注入 源码学习笔记
注意 互相关联多使用接口注册,所以可以 根据需要替换. 始于 Startup.cs 中的 通过 AddApplication 扩展方法添加 Abp支持 1 services.AddApplicati ...
- Java中对象初始化过程
Java为对象初始化提供了多种选项. 当new一个对象的时候,对象初始化开始: 1.首先,JVM加载类(只加载一次,所以,即使多次new对象,下面的代码也只会在第一次new的时候执行一次),此时, 静 ...
- Framebuffer 驱动学习总结(二)---- Framebuffer模块初始化
---恢复内容开始--- Framebuffer模块初始化过程:--driver\video\fbmem.c 1. 初始化Framebuffer: FrameBuffer驱动是以模块的形式注册到系统 ...
- ABP中的拦截器之ValidationInterceptor(下)
在上篇我分析了整个ABP中ValitationInterceptor的整个过程,就其中涉及到的Validator过程没有详细的论述,这篇文章就这个过程进行详细的论述,另外任何一个重要的特性如何应用是最 ...
- ABP中的拦截器之EntityHistoryInterceptor
今天我们接着之前的系列接着来写另外一种拦截器EntityHistoryInterceptor,这个拦截器到底是做什么的呢?这个从字面上理解是实体历史?这个到底是什么意思?带着这个问题我们来一步步去分析 ...
- MFC原理第二讲.MFC的初始化过程
MFC原理第二讲MFC的初始化过程 一丶简介 通过上一讲.我们手工模拟了一个MFC程序. 但是上一讲留下了疑问. 就是WinMain在哪.以及消息处理在哪里. 这一节主要就是讲解Winmain在哪里. ...
- 【软件构造】-<笔记>-浅谈java中类的初始化过程
编写java程序时,每创建一个新的对象,都会对对象的内容进行初始化. 每一个类的方法中的局部变量都有严格的初始化要求,因此假如写出下面的程序: void f(){ int i; i++; } 编译时就 ...
- ABP架构学习系列二:ABP中配置的注册和初始化
一.手工搭建平台 1.创建项目 创建MVC5项目,手动引入Abp.Abp.Web.Abp.Web.Mvc.Abp.Web.Api 使用nuget添加Newtonsoft.Json.Castle.Cor ...
随机推荐
- Mybatis框架基础支持层——反射工具箱之泛型解析工具TypeParameterResolver(4)
简介:TypeParameterResolver是一个工具类,提供一系列的静态方法,去解析类中的字段.方法返回值.方法参数的类型. 在正式介绍TypeParameterResolver之前,先介绍一个 ...
- loj#6074. 「2017 山东一轮集训 Day6」子序列(矩阵乘法 dp)
题意 题目链接 Sol 设\(f[i][j]\)表示前\(i\)个位置中,以\(j\)为结尾的方案数. 转移的时候判断一下\(j\)是否和当前位置相同 然后发现可以用矩阵优化,可以分别求出前缀积和逆矩 ...
- 总结:当静态路由和BGP同时存在时路由优选BGP的两种方法
结论: 方法一.配置BGP协议的外部优先级比静态路由的优先级高,优选BGP. 优点:配置简单. 缺点:全局生效,如果用户有针对某个静态路由想提高优先级,不受动态路由影响,则针对每个静态路由都需要人为提 ...
- 三、View的事件体系
1.View基础知识 1.1.什么是View View是Android中所有控件的基类.View是一种界面层的控件的一种抽象,代表了一个控件.除了View,还有ViewGroup,内部包含了许多个控件 ...
- DVWA 黑客攻防演练(八)SQL 注入 SQL Injection
web 程序中离不开数据库,但到今天 SQL注入是一种常见的攻击手段.如今现在一些 orm 框架(Hibernate)或者一些 mapper 框架( iBatis)会对 SQL 有一个更友好的封装,使 ...
- 【公众号系列】浅谈SAP项目管理的技能
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[[公众号系列]浅谈SAP项目管理的技能 写 ...
- c/c++ linux 进程间通信系列1,使用signal,kill
linux 进程间通信系列1,使用signal,kill 信号基本概念: 软中断信号(signal,又简称为信号)用来通知进程发生了异步事件.进程之间可以互相通过系统调用kill发送软中断信号.内核 ...
- c#语法学习
自动属性.隐试类型.命名参数和自动初始化器. note:这里说的这些,是语法糖.按照一定的格式写,部分代码编译器帮我们实现了, 1.自动属性:自动属性是非常有用的语法糖,帮我我们做了两件事:1.自动帮 ...
- 启动Hadoop总是需要输入密码的问题.
start-all.sh 总是需要输入当前密码. 一开始以为是权限不够. 1.修改sudo配置文件 sudo visudo 增加 hduser ALL=(ALL) NOPASSWD:ALL 解决了 权 ...
- iis .apk .ipa下载设置
.apk .ipa无法下载 解决步骤:1).打开IIS服务管理器,找到服务器,右键-属性,打开IIS服务属性:2.单击MIME类型下的“MIME类型”按钮,打开MIME类型设置窗口:3).单击“新建” ...