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系列的主目录。

原文地址:https://www.cnblogs.com/seekdream/p/9317429.html

时间: 2024-10-03 01:36:05

ABP中的模块初始化过程(一)的相关文章

Nginx 中 HTTP模块初始化

概述 在前面的文章< Nginx 配置解析>简单讲解了通用模块的配置项解析,并且大概讲解了HTTP 模块的配置项解析过程,本文更具体的分析 HTTP 模块的初始化过程.HTTP 模块初始化过程主要有:上下文结构初始化.配置项解析.配置项合并.server 相关端口设置. HTTP 模块接口 ngx_http_module_t 结构体 在 Nginx 中,结构体 ngx_module_t 是 Nginx 模块最基本的接口.对于每一种不同类型的模块,都有一个具体的结构体来描述这一类模块的通用接口.

java中对象的初始化过程

class Parent{ int num = 8;// ->3 Parent(){ //super(); // ->2 //显示初始化 // ->3 //构造代码段 // ->4 show(); // ->5 } {// ->4 System.out.println("Parent constructor code run->"); } public void show(){//被覆盖 System.out.println("num

ABP中模块初始化过程(二)

在上一篇介绍在StartUp类中的ConfigureService()中的AddAbp方法后我们再来重点说一说在Configure()方法中的UserAbp()方法,还是和前面的一样我们来通过代码来进行一步步分析. public static class AbpApplicationBuilderExtensions { public static void UseAbp(this IApplicationBuilder app) { app.UseAbp(null); } public sta

java中一个对象的初始化过程

// 对象初始化的过程 class Fu { int num=55;// 1, num=0;对象中的成员变量默认初始化.5, 显示初始化 num=55 { System.out.println("FU类构造代码块");// 6, 构造代码块初始化 } Fu() { // 3 FU类构造函函数初始化 super(); // 4 object构造函函数初始化 // 5 显示初始化,初始化成员变量 // 6 构造代码块初始化类 System.out.println("FU构造函数&

Framebuffer 驱动学习总结(二)---- Framebuffer模块初始化

---恢复内容开始--- Framebuffer模块初始化过程:--driver\video\fbmem.c 1.  初始化Framebuffer: FrameBuffer驱动是以模块的形式注册到系统中,在模块初始化时,创建FrameBuffer对应的设备文件及proc文件,并注册FrameBuffer设备操作接口函数fb_fops. static int __init fbmem_init(void) { proc_create("fb", 0, NULL, &fb_proc

4.4 模块初始化与结束化

4.4.1模块入口代码 编译器为每个Delphi模块(可执行程序.动态链接库或包)生成模块入口代码.同类型的程序或模块,其入口代码是相同的. 编译器为可执行程序.EXE)生成的入口代码为: Project1.dpr.9:begin 0044CA9855push ebp 0044CA99 8BEC mov ebp,esp 0044CA9B 83C4F0 add esp,-$10 0044CA9EB8B8C84400 mov eax,$0044c8b8//传入单元初始化表地址 0044CAA3 E8

[转]在static代码块或static变量的初始化过程中使用ServiceManager提供的api的陷阱

一. 案例 1.源码: /** @hide */ private TelephonyManager(int slotId) { mContext = null; mSlotId = slotId; if (sRegistry == null) { if (sRegistry == null) { sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( "telephony.registry")

Java中的初始化过程(转)

原文:http://www.cnblogs.com/mmbo/archive/2009/10/05/1578156.html 1.类成员自动初始化,基本类型总是最先初始化为0(boolean为false,(char)0),对象的引用初始化为null:2.类成员指定初始化,可以在类成员定义或构造器(包括其他方法)中给基本类型和非基本类型对象初始化,但这并不能阻止自动初始化首先进行:3.按类成员定义的顺序决定初始化的顺序:4.静态成员只有在第一次被访问时(class对象首次加载)才会被初始化,此后不

spring源码研究之IoC容器在web容器中初始化过程

前段时间在公司做了一个项目,项目用了spring框架实现,WEB容器是Tomct 5,虽然说把项目做完了,但是一直对spring的IoC容器在web容器如何启动和起作用的并不清楚.所以就抽时间看一下spring的源代码,借此了解它的原理. 我们知道,对于使用Spring的web应用,无须手动创建Spring容器,而是通过配置文件,声明式的创建Spring容器.因此在Web应用中创建Spring容器有如下两种方式: 1. 直接在web.xml文件中配置创建Spring容器. 2. 利用第三方MVC