ABP中的模块初始化过程(一)
在总结完整个ABP项目的结构之后,我们就来看一看ABP中这些主要的模块是按照怎样的顺序进行加载的,在加载的过程中我们会一步步分析源代码来进行解释,从而使自己对于整个框架有一个清晰的脉络,在整个Asp.Net Core项目中,我们启动一个带Swagger UI的Web API项目为例,在介绍这个Web API项目之前我们先来看看整个Swagger 文档的样式。
我们定义的WebAPI最终都会以Swagger文档这种形式来展现出来,通过这种形式也是非常方便我们进行代码的调试的,在进行网站的前后端分离开发的过程中,前端去定义接口后端根据前端定义的接口进行开发,这个模式能够实现整个开发的分离,当然这篇文章主要不是介绍如何去进行前后端分离开发而是重点介绍如何ABP模块中代码的加载顺序,前面的截图是整个ABP项目的启动界面,通过这些能够让我们对整个项目有一个概念性的认识和理解。
在整个项目的运行过程中,首先也是从Program类中开始的,首先执行Program类中的静态Main方法,然后在Main方法中会创建一个IWebHost对象,然后执行Run方法,看起来像下面的形式:
public class Program
{
private static IConfiguration Configuration { get; set; } public static void Main(string[] args)
{
BuildWebHost(args).Run();
} public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
在这里面会执行UseStartup<Startup>()这个方法,然后会将控制主逻辑转移到Startup这个类中,下面我们再来看一看Startup这个类中执行了些什么操作?
public class Startup
{
private const string _defaultCorsPolicyName = "localhost"; private readonly IConfigurationRoot _appConfiguration; public Startup(IHostingEnvironment env)
{
_appConfiguration = env.GetAppConfiguration();
} public IServiceProvider ConfigureServices(IServiceCollection services)
{
// MVC
services.AddMvc(
options => options.Filters.Add(new CorsAuthorizationFilterFactory(_defaultCorsPolicyName))
); IdentityRegistrar.Register(services);
AuthConfigurer.Configure(services, _appConfiguration); #if FEATURE_SIGNALR_ASPNETCORE
services.AddSignalR();
#endif // Configure CORS for angular2 UI
services.AddCors(
options => options.AddPolicy(
_defaultCorsPolicyName,
builder => builder
.WithOrigins(
// App:CorsOrigins in appsettings.json can contain more than one address separated by comma.
_appConfiguration["App:CorsOrigins"]
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.RemovePostFix("/"))
.ToArray()
)
.AllowAnyHeader()
.AllowAnyMethod()
)
); // Swagger - Enable this line and the related lines in Configure method to enable swagger UI
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Info { Title = "Server API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true); // Define the BearerAuth scheme that's in use
options.AddSecurityDefinition("bearerAuth", new ApiKeyScheme()
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = "header",
Type = "apiKey"
});
// Assign scope requirements to operations based on AuthorizeAttribute
options.OperationFilter<SecurityRequirementsOperationFilter>();
}); // Configure Abp and Dependency Injection
return services.AddAbp<ServerWebHostModule>(
// Configure Log4Net logging
options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig("log4net.config")
)
);
} public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseAbp(options => { options.UseAbpRequestLocalization = false; }); // Initializes ABP framework. app.UseCors(_defaultCorsPolicyName); // Enable CORS! app.UseStaticFiles(); app.UseAuthentication(); app.UseAbpRequestLocalization(); #if FEATURE_SIGNALR
// Integrate with OWIN
app.UseAppBuilder(ConfigureOwinServices);
#elif FEATURE_SIGNALR_ASPNETCORE
app.UseSignalR(routes =>
{
routes.MapHub<AbpCommonHub>("/signalr");
});
#endif app.UseMvc(routes =>
{
routes.MapRoute(
name: "defaultWithArea",
template: "{area}/{controller=Home}/{action=Index}/{id?}"); routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
}); // Enable middleware to serve generated Swagger as a JSON endpoint
app.UseSwagger();
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Server API V1");
options.IndexStream = () => Assembly.GetExecutingAssembly()
.GetManifestResourceStream("SunLight.Server.Web.Host.wwwroot.swagger.ui.index.html");
}); // URL: /swagger
} #if FEATURE_SIGNALR
private static void ConfigureOwinServices(IAppBuilder app)
{
app.Properties["host.AppName"] = "Server"; app.UseAbp(); app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJSONP = true
};
map.RunSignalR(hubConfiguration);
});
}
#endif
}
上面的过程熟悉Asp.Net Core开发的都应该十分熟悉,在Startup类中定义了两个主要的方法:ConfigureServices和Configure方法,这两个方法是从ConfigureServices开始进行服务配置,包括MVC配置、CORS配置,Swagger的一些配置以及最关键的ABP的配置,这里仅仅列出最为关键的过程,然后对着这些代码来一步步进行分析。
return services.AddAbp<ServerWebHostModule>(
// Configure Log4Net logging
options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig("log4net.config")
)
);
这里面最为关键的就是执行AddAbp方法了,整个ABP框架的执行也将从这里拉开序幕,我们来看看ABP项目的源码
public static class AbpServiceCollectionExtensions
{
/// <summary>
/// Integrates ABP to AspNet Core.
/// </summary>
/// <typeparam name="TStartupModule">Startup module of the application which depends on other used modules. Should be derived from <see cref="AbpModule"/>.</typeparam>
/// <param name="services">Services.</param>
/// <param name="optionsAction">An action to get/modify options</param>
public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
where TStartupModule : AbpModule
{
var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction); ConfigureAspNetCore(services, abpBootstrapper.IocManager); return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
} private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver)
{
//See https://github.com/aspnet/Mvc/issues/3936 to know why we added these services.
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>(); //Use DI to create controllers
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); //Use DI to create view components
services.Replace(ServiceDescriptor.Singleton<IViewComponentActivator, ServiceBasedViewComponentActivator>()); //Change anti forgery filters (to work proper with non-browser clients)
services.Replace(ServiceDescriptor.Transient<AutoValidateAntiforgeryTokenAuthorizationFilter, AbpAutoValidateAntiforgeryTokenAuthorizationFilter>());
services.Replace(ServiceDescriptor.Transient<ValidateAntiforgeryTokenAuthorizationFilter, AbpValidateAntiforgeryTokenAuthorizationFilter>()); //Add feature providers
var partManager = services.GetSingletonServiceOrNull<ApplicationPartManager>();
partManager?.FeatureProviders.Add(new AbpAppServiceControllerFeatureProvider(iocResolver)); //Configure JSON serializer
services.Configure<MvcJsonOptions>(jsonOptions =>
{
jsonOptions.SerializerSettings.ContractResolver = new AbpContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
}); //Configure MVC
services.Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(services);
}); //Configure Razor
services.Insert(0,
ServiceDescriptor.Singleton<IConfigureOptions<RazorViewEngineOptions>>(
new ConfigureOptions<RazorViewEngineOptions>(
(options) =>
{
options.FileProviders.Add(new EmbeddedResourceViewFileProvider(iocResolver));
}
)
)
);
} private static AbpBootstrapper AddAbpBootstrapper<TStartupModule>(IServiceCollection services, Action<AbpBootstrapperOptions> optionsAction)
where TStartupModule : AbpModule
{
var abpBootstrapper = AbpBootstrapper.Create<TStartupModule>(optionsAction);
services.AddSingleton(abpBootstrapper);
return abpBootstrapper;
}
}
整个ABP项目在AbpServiceCollectionExtensions这个类里面定义了一个AddABP的方法,就像当前方法的注释写的那样,让ABP项目与Asp.Net Core结合,在这个方法中首先就是创建唯一的AbpBootstrapper的实例,在这里创建的方式采用的是静态方法Create方法,下面通过源代码来分析一下这个方法。
/// <summary>
/// Creates a new <see cref="AbpBootstrapper"/> instance.
/// </summary>
/// <typeparam name="TStartupModule">Startup module of the application which depends on other used modules. Should be derived from <see cref="AbpModule"/>.</typeparam>
/// <param name="optionsAction">An action to set options</param>
public static AbpBootstrapper Create<TStartupModule>([CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
where TStartupModule : AbpModule
{
return new AbpBootstrapper(typeof(TStartupModule), optionsAction);
}
这个方法是一个泛型方法,用于创建一个唯一的AbpBootstrapper的实例,这里的泛型参数是TStartupModule,这个是整个项目的启动的Module,一般是XXXWebHostModule,后面的参数是一个参数类型为AbpBootstrapperOptions的Action类型委托,这个类型是一个可为空类型。
接下来我们再看看在私有的AbpBootstrapper构造函数中做了哪些事情,然后来一步步分析,首先来看看源代码。
/// <summary>
/// Creates a new <see cref="AbpBootstrapper"/> instance.
/// </summary>
/// <param name="startupModule">Startup module of the application which depends on other used modules. Should be derived from <see cref="AbpModule"/>.</param>
/// <param name="optionsAction">An action to set options</param>
private AbpBootstrapper([NotNull] Type startupModule, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
{
Check.NotNull(startupModule, nameof(startupModule)); var options = new AbpBootstrapperOptions();
optionsAction?.Invoke(options); if (!typeof(AbpModule).GetTypeInfo().IsAssignableFrom(startupModule))
{
throw new ArgumentException($"{nameof(startupModule)} should be derived from {nameof(AbpModule)}.");
} StartupModule = startupModule; IocManager = options.IocManager;
PlugInSources = options.PlugInSources; _logger = NullLogger.Instance; if (!options.DisableAllInterceptors)
{
AddInterceptorRegistrars();
}
}
首先Check.NotNull是一个静态方法,用于判断整个ABP项目中启动Module是否为Null,如果为Null则直接抛出异常,然后第一步就是创建AbpBootstrapperOptions,在这个options里面定义了整个ABP项目中唯一的依赖注入容器IocManager ,这个容器是通过 IocManager = Abp.Dependency.IocManager.Instance,来完成的,这里我们就来简单看一下我们用的唯一的一个依赖注入容器。
/// <summary>
/// Creates a new <see cref="IocManager"/> object.
/// Normally, you don't directly instantiate an <see cref="IocManager"/>.
/// This may be useful for test purposes.
/// </summary>
public IocManager()
{
IocContainer = new WindsorContainer();
_conventionalRegistrars = new List<IConventionalDependencyRegistrar>(); //Register self!
IocContainer.Register(
Component.For<IocManager, IIocManager, IIocRegistrar, IIocResolver>().UsingFactoryMethod(() => this)
);
}
在我们的项目中,我们使用的是WindsorContainer,这个老牌的依赖注入容器作为全局的唯一的一个依赖注入容器,关于Castel Windsor这个著名的开源的依赖注入容器我们可以去它的官网去了解其详细信息,请点击这里访问Castle Project项目。
在使用这个依赖注入容器之前首先要将this也就是自己作为第一个实例注入到WindsorContainer容器中去,关于这个容器还有很多的内容,这个需要我们查看源码查看具体的实现,这个IocContainer类中还有很多的关于注册外部的实例到容器的方法,这个在后续的内容中会逐步去分析。
另外在AbpBootstrapperOptions这个类的构造函数中除了创建整个ABP项目中唯一的依赖注入容器IocManager以外,还定义了一个PlugInSources的公共属性,这个主要是为构建插件化、模块化项目提供插件模块的一个程序集集合,关于这个部分这里来看一下有哪些内容?
public class PlugInSourceList : List<IPlugInSource>
{
public List<Assembly> GetAllAssemblies()
{
return this
.SelectMany(pluginSource => pluginSource.GetAssemblies())
.Distinct()
.ToList();
} public List<Type> GetAllModules()
{
return this
.SelectMany(pluginSource => pluginSource.GetModulesWithAllDependencies())
.Distinct()
.ToList();
}
}
这个主要是一个为了加载外部的一些继承自AbpModule的一些程序集,包括一些外部文件夹里面的一些可扩展的程序集,我们来看一下ABP中为我们实现了哪些类型的扩展。1 FolderPlugInSource、2 PlugInTypeListSource、3 AssemblyFileListPlugInSource。
在分析完AbpBootstrapper类中AbpBootstrapperOptions的构建后,我们接着来分析AbpBootstrapper构造函数中其它的逻辑,在后面首先判断传入的泛型参数TStartupModule是否是继承自ABP项目中的基类AbpModule,否则的话就会抛出参数的异常。
后面的一个重点内容就是就是如果没有默认关掉所有的ABP拦截器的话,就会初始化ABP中所有的拦截器,这个是一个很大的内容,在后面我会花一篇文章来专门介绍ABP中的各种拦截器。
private void AddInterceptorRegistrars()
{
ValidationInterceptorRegistrar.Initialize(IocManager);
AuditingInterceptorRegistrar.Initialize(IocManager);
EntityHistoryInterceptorRegistrar.Initialize(IocManager);
UnitOfWorkRegistrar.Initialize(IocManager);
AuthorizationInterceptorRegistrar.Initialize(IocManager);
}
这里暂时先不去分析这些内容,只是让对这个框架先有一个整体上的把握。在完成所有的AbpBootstrapper类的初始化,后面就是执行ConfigureAspNetCore这个方法了,这个方法主要是用于配置一些常用的服务,下面我们通过具体的代码来一步步去分析。
private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver)
{
//See https://github.com/aspnet/Mvc/issues/3936 to know why we added these services.
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>(); //Use DI to create controllers
services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); //Use DI to create view components
services.Replace(ServiceDescriptor.Singleton<IViewComponentActivator, ServiceBasedViewComponentActivator>()); //Change anti forgery filters (to work proper with non-browser clients)
services.Replace(ServiceDescriptor.Transient<AutoValidateAntiforgeryTokenAuthorizationFilter, AbpAutoValidateAntiforgeryTokenAuthorizationFilter>());
services.Replace(ServiceDescriptor.Transient<ValidateAntiforgeryTokenAuthorizationFilter, AbpValidateAntiforgeryTokenAuthorizationFilter>()); //Add feature providers
var partManager = services.GetSingletonServiceOrNull<ApplicationPartManager>();
partManager?.FeatureProviders.Add(new AbpAppServiceControllerFeatureProvider(iocResolver)); //Configure JSON serializer
services.Configure<MvcJsonOptions>(jsonOptions =>
{
jsonOptions.SerializerSettings.ContractResolver = new AbpContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
};
}); //Configure MVC
services.Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(services);
}); //Configure Razor
services.Insert(0,
ServiceDescriptor.Singleton<IConfigureOptions<RazorViewEngineOptions>>(
new ConfigureOptions<RazorViewEngineOptions>(
(options) =>
{
options.FileProviders.Add(new EmbeddedResourceViewFileProvider(iocResolver));
}
)
)
);
}
这里面主要是配置一些核心的Asp.Net Core服务,比如用ServiceBasedControllerActivator来替换默认的DefaultControllerActivator ,
使用ServiceBasedViewComponentActivator来替换默认的DefaultViewComponentActivator,这里面我们重点来关注一下services.Configure<MvcOptions>这个方法,我们来看一下最终在里面做了些什么。
internal static class AbpMvcOptionsExtensions
{
public static void AddAbp(this MvcOptions options, IServiceCollection services)
{
AddConventions(options, services);
AddFilters(options);
AddModelBinders(options);
} private static void AddConventions(MvcOptions options, IServiceCollection services)
{
options.Conventions.Add(new AbpAppServiceConvention(services));
} private static void AddFilters(MvcOptions options)
{
options.Filters.AddService(typeof(AbpAuthorizationFilter));
options.Filters.AddService(typeof(AbpAuditActionFilter));
options.Filters.AddService(typeof(AbpValidationActionFilter));
options.Filters.AddService(typeof(AbpUowActionFilter));
options.Filters.AddService(typeof(AbpExceptionFilter));
options.Filters.AddService(typeof(AbpResultFilter));
} private static void AddModelBinders(MvcOptions options)
{
options.ModelBinderProviders.Insert(0, new AbpDateTimeModelBinderProvider());
}
}
在这个方法中,主要实现了三个部分:AddConventions、AddFilters、AddModelBinders这三个方法,第一个就是添加默认的协定、第二个就是我们的Asp.Net Core服务中添加各种过滤器,这些过滤器会添加到Asp.Net Core请求过程中,这些Filter的主要作用是在Action执行前和执行后进行一些加工处理,关于Asp.Net Core中的Filter请参考下面的这篇文章,第三个部分就是为默认的MvcOptions中添加默认的ModelBinder。
在AddAbp方法最后执行的是 WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services)这个方这个最后的方法就是替换掉了 Asp.Net Core 默认的 Ioc 容器,最终使用的是 CastleWindsor 的IocContainer。通过这上面的这些过程完成了整个Asp.Net Core 的服务配置过程,在后面的一片文章中我们将重点分析在StarpUp类中Configure方法中调用UseAbp()方法了,这个方法将会去一步步分析整个Module是如何查找,如何加载如何运行的。
最后,点击这里返回整个ABP系列的主目录。
ABP中的模块初始化过程(一)的更多相关文章
- 通俗理解ABP中的模块Module
网上有不少文章说ABP的模块,有的直接翻译自官网介绍,有的分析Modlue的源代码,有的写一通代码,没什么注释,很少有能通俗说清的.那么,有两个问题:1.ABP中的模块到底是什么?2.搞这个东西是干嘛 ...
- ABP中模块初始化过程(二)
在上一篇介绍在StartUp类中的ConfigureService()中的AddAbp方法后我们再来重点说一说在Configure()方法中的UserAbp()方法,还是和前面的一样我们来通过代码来进 ...
- java中对象产生初始化过程
以前面试的时候,很多公司的笔试题中有关new一个对象有关一系列初始化的过程的选择题目.请看下面的题目. class Parent { static { System.out.println(" ...
- java中对象的初始化过程
class Parent{ int num = 8;// ->3 Parent(){ //super(); // ->2 //显示初始化 // ->3 //构造代码段 // -> ...
- Framebuffer 驱动学习总结(二)---- Framebuffer模块初始化
---恢复内容开始--- Framebuffer模块初始化过程:--driver\video\fbmem.c 1. 初始化Framebuffer: FrameBuffer驱动是以模块的形式注册到系统 ...
- linux PCI设备初始化过程
linux PCI设备初始化过程 start_kernel->rest_init 这个函数会启动一个核心线程0, 核心线程然后调用init -> do_basic_setup. 然后我们开 ...
- ES6中的模块
前面的话 JS用"共享一切"的方法加载代码,这是该语言中最容出错且容易令人感到困惑的地方.其他语言使用诸如包这样的概念来定义代码作用域,但在ES6以前,在应用程序的每一个JS中定义 ...
- ABP架构学习系列二:ABP中配置的注册和初始化
一.手工搭建平台 1.创建项目 创建MVC5项目,手动引入Abp.Abp.Web.Abp.Web.Mvc.Abp.Web.Api 使用nuget添加Newtonsoft.Json.Castle.Cor ...
- Java中的成员初始化顺序和内存分配过程
Java中的成员初始化顺序和内存分配过程 原帖是这样描述的: http://java.dzone.com/articles/java-object-initialization?utm_source= ...
随机推荐
- 拼多多、饿了么、蚂蚁金服Java面试题大集合
自己当初找工作时参加过众多一线互联网公司的Java研发面试,这段时间处于寒冬,然而前几天跳槽找工作,两天面了3家,已经拿了两个offer,觉得可以和大家分享下: 下面为拼多多.饿了么.蚂蚁金服.哈啰出 ...
- Spring Boot 整合 docker
一.什么是docker ? 简介 Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的.可移植的.自给自足的容器.开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括VMs ...
- Flask 系列之 部署发布
说明 操作系统:Windows 10 Python 版本:3.7x 虚拟环境管理器:virtualenv 代码编辑器:VS Code 实验目标 通过 Windows 的 WSL,将我们的项目网站部署到 ...
- Fundebug发布Vue插件,简化BUG监控接入代码
摘要: 代码越短越好! 我们发布了fundebug-vue插件,可以简化Vue框架接入Fundebug的代码. Vue如何接入Fundebug 1. 安装fundebug-javascript与fun ...
- 谷歌AI涉足艺术、太空、外科手术,再强调AI七原则
谷歌AI涉足艺术.太空.外科手术,再强调AI七原则 https://mp.weixin.qq.com/s/MJG_SvKCEBKRvL3IWpL0bA 9月18日上午,Google在上海的2018世界 ...
- iOS ----------各种判断
iOS 判断数字 - (BOOL) deptNumInputShouldNumber:(NSString *)str { if (str.length == 0) { return NO; } NSS ...
- 关于如何使用xposed来hook某支付软件
由于近期有业务上的需要,所以特地花时间去研究了一下如何使用hook技术.但是当我把xposed环境和程序编写完成时,突然发现手机上的某个支付软件无法使用了.这个时候我意识到,应该是该软件的安全机制在起 ...
- ionic3 导航的应用(页面跳转与参数传递)
about.html(跳转页面) <ion-content padding> <ion-list> <ion-item *ngFor="let he of co ...
- window64位电脑如何通过VMware Workstation12.5.6安装苹果操作系统 macOS High Sierra 10.13
1.下载 VMware-workstation-full-12.5.6.exe,macOS High Sierra 10.13.iso 2.安装 VMware-workstation时不要选择C盘,因 ...
- Linux 环境 Intelij Idea 安装与快捷图标配置
索引: 目录索引 参看代码 GitHub: intelij-idea.txt 一.Linux (DeepinOS) 环境 1.官网下载 ideaIU-.tar.gz 2.解压 .tar.gz -C ~ ...