精华内容
下载资源
问答
  • 创建初始上下文
    2016-04-25 21:28:53

    InitialContext类实现Context接口,调用其构造方法便可创建初始上下文(即搜索请求对象的起始点)。创建初始上下文需要设定以下两个环境属性

         ①上下文工厂对象spi Context.INITIAL_CONTEXT_FACTORY,指定要使用哪个具体服务提供程序。

         ②连接字符串Context.PROVIDER_URL,指定服务的位置和初始上下文的起始点。

    (1)在Weblogic下创建初始上下文

         ①建立Hashtable变量,将两个环境属性存入其中。

            Hashtable ht = new Hashtable();        ht.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory";

    ht.put(Context.PROVIDER_URL,"t3://localhost:7001");

               ②使用上述Hashtable变量作为InitialContext构造方法的参数,创建一个Context实例。

          try{
                     Context ctx = new InitialContext(ht);
            }

     (2)绑定和查找对象

         ①绑定对象:调用Context接口中的bind()和rebind()方法来实现。

         Context.bind (String name,Object obj);

         Context.rebind(String name,Object obj);

         ②查找对象:调用Context接口中的lookup(String name)方法返回当前上下文中参数name对应的绑定对象。

         Object Context.lookup(String name);





         


    更多相关内容
  • DDD—上下文映射图

    千次阅读 2019-11-17 16:34:22
    一个项目的上下文映射图(Context Map)可以用两种方式表示, (1)比较容易的一种是画一个简单的框图来表示两个或多个限界上下文之间的映射关系。该框图表示了不同的限界上下文在解决方案空间中是如何通过集成相互...

    一个项目的上下文映射图(Context Map)可以用两种方式表示,
    (1)比较容易的一种是画一个简单的框图来表示两个或多个限界上下文之间的映射关系。该框图表示了不同的限界上下文在解决方案空间中是如何通过集成相互关联的。

    (2)另一种更详细的方式通过限界上下文集成的源代码实现来表示。

    1、上下文映射图为什么重要

    在开始采用DDD时,首先应该为你当前的项目绘制一个上下文映射图,其中应该包含你项目中当前的限界上下文和它们之间的集成关系。下图表示一个抽象的上下文映射图,后文将不断地向里面添加细节内容。

    在这里插入图片描述

    比如,当你为一个大型企业进行限界上下文之间的集成时,你可能需要与大泥球进行交互。大泥球的维护团队才不关心你的项目呢,因为你依赖于他们的API、因此,他们并不会深入到你的上下文映射图中,然而你的映射图依然需要反映出和他们的集成关系,因为这样可以使你了解到映射图的内部,并且可以指明在哪些地方需要与其他团队进行交流。这样对你团队的成功是有帮助的。

    假定你期望大泥球维护团队提供一套新的API。然而,他们并不打算这样做。此时你的团队和大泥球的维护团队的关系便成了客户方-供应方的关系。由于大泥球团队决定维持现状,你的团队不得不陷入一种遵奉者关系中。尽早绘制上下文映射图,这样可以迫使呢仔细思考你的项目和你所依赖项目之间的关系。

    CollabOvation团队应该在建模之初就使用上下文映射图,然而当时他们对于战略建模一无所知。后来,通过经验积累,在之后的ProjectOvation中,尝到了建模的甜头。

    上下文映射图表现的是项目当前的状态,如果项目发生变化,你可以更新上下文映射图。注意上下文映射图不需要画得太正式了,手绘即可。

    2、产品和组织关系

    这一节主要重复下,整个专栏的案例说明,详情见:DDD案例说明。

    SaaSOvation公司正在开发的3个产品:

    1、CollabOvation——一款社交协作软件。该产品允许注册用户发布对业务有价值的内容,发布方式是一些流行的基于Web的工具,比如论坛、共享日历、博客和wiki等。这是SaaSOvation公司的旗舰产品,也是该公司的第一个核心域。开发团队后来从CollabOvation中提取了IdOvation模型。对于CollabOvation来说,IdOvation是一个通用子域,而CollabOvation本身又作为ProjectOvation的支撑子域。

    2、IdOvation——一款可重用的身份和访问管理产品。IdOvation为注册用户提供安全的、基于角色的访问管理。这些功能一开始和CollabOvation混合在一起,这样导致的问题是:实现受到了限制,功能不可重用。SaaSOvation对CollabOvation进行了重构,引入了一个新的、结构清晰的限界上下文。SaaSOvation公司决定支持多个租户,这种功能对于SaaS产品来说是至关重要的。对于消费方来说,IdOvation扮演着通用子域的角色。

    3、ProjectOvation——一个敏捷项目管理产品。这是SaaSOvation公司的核心域。ProjectOvation采用基于Scrum的项目运行框架,用户可以创建项目管理资产,同时对项目资产进行分析和设计,还能跟踪项目进度。和CollabOvation一样,ProjectOvation将IdOvation作为一个通用子域来使用。该产品的一大创新是将CollabOvation引入了敏捷项目管理,这样用户可以围绕Scrum的产品、发布、冲刺和待定项展开讨论。

    这些限界上下文之间的关系如何,不同开发团队之间的关系又如何?让我们看下各种关系之间的定义:

    • 合作关系(partnership):如果两个限界上下文的团队要么一起成功,要么一起失败,此时他们需要建立起一种合作关系。他们需要协调开发计划和集成管理。

    • 共享内核(share kernel):对模型和代码的共享将产生一种紧密的依赖性,对于设计来说,这种依赖性可好可坏。我们需要为共享的部分模型指定一个显式的边界,并保持共享内核的小型化。共享内核具有特殊的状态,在没有与另一个团队协商的情况下,这种状态是不能改变的。我们应该引入一种持续集成过程来保证共享内核与通用语言的一致性。

    • 客户方——供应方开发(Customer-Supplier Development):当两个团队处于一种上游-下游关系时,上游团队可能独立于下游团队完成开发,此时下游团队开发可能会受到影响。因此,在上游团队的计划中,我们应该顾及到下游团队的需求。

    • 遵奉者(conformist):在存在上游-下游关系时,如果上游团队已经没有动力提供下游团队之所需,下游团队便孤军无助了。只能盲目地使用上游团队的模型。

    • 防腐层(Anticorruption layer):在集成两个良好的限界上下文时,翻译层可能很简单,甚至可以很优雅地实现。但是,当共享内核、合作关系或客户方-供应方关系无法顺利实现时,此时的翻译将变得复杂。对于下游客户来说,你需要根据自己的领域模型创建一个单独的层,该层作为上游系统的委派向你的系统提供功能。防腐层通过已有的接口与其他系统交互,而其他系统只需要做很小的修改,甚至无须修改。在防腐层内部,它在你自己的模型和他方模型之间进行翻译转化。

    • 开放主机服务(open host service):定义一种协议,让你的子系统通过该协议来访问你的服务。你需要将该协议公开,这样任何与你集成的人都可以使用该协议。在有新的集成需求时,你应该对协议进行改进或者扩展。对于一些特殊的需求,你可以采用一次性的翻译予以处理,这样可以保持协议的简单性和连贯性。

    • 发布语言(published language):在两个限界上下文之间翻译模型需要一种公用的语言。此时你应该使用一种发布出来的共享语言来完成集成交流。发布语言通常与开放主机一起使用。

    • 另谋他路(separateWay):在确定需求时,我们应该做到坚持到底。如果两套功能没有显著的关系,那么它们也可以被完全解耦的。声明两个限界上下文之间不存在任何关系,这样使得开发者去寻找简单的、专门的方法来解决问题。

    • 大泥球(Big Ball of mud):当我们检查已有系统时,经常会发现系统中存在混杂在一起的模型,它们之间的边界是非常模糊的。此时,你应该为整个系统绘制一个边界,然后将其归纳在大泥球范围之列。在这个边界之内,不要试图使用复杂的建模手段来化解问题。同时,这样的系统有可能会向其他系统蔓延,你应该对此保持警觉。

    在与身份与访问上下文集成时,协作上下文和敏捷项目管理上下文均没有采用另谋他路的手法。诚然,在上下文范围之内,另谋他路的手法可以应用在特殊的系统上,同时它也可以用于一些个例。比如,一个团队可能拒绝使用集中式的安全管理系统,但他们依然会与另外类型的安全管理系统集成。

    在集成时,他们将会用到开放主机服务和发布语言,有可能还会用到防腐层。虽然这两者都会在限界上下文之间创建开放的标准,但是它们并不矛盾。通过使用下游上下文中的基本原则,他们依然可以得到由独立翻译所带来的好处,并且不会像与大泥球集成那样复杂。翻译层将变得简单、优雅。

    在上下文映射图中,我们使用以下缩写来表示各种关系:

    • ACL 表示防腐层
    • OHS 表示开放主机服务
    • PL 表示发布语言

    3、映射3个示例限界上下文

    当CollabOvation团队意识到自己已经陷入僵局时,他们研究发现“上下文映射图”的工具非常实用。

    从团队设计的第一张映射图可以看出,他们已经知道应该创建一个名为“协作上下文”的限界上下文。从图中怪异的边界又可以看出,他们可能希望创建第二个限界上下文,但是却不知道如何将这个上下文从核心域中分离出来。
    在这里插入图片描述

    团队成员意识到安全、用户和权限并不属于协作上下文,需要将这些概念从核心域中分离出来,是这些概念只有在得到同意的时候才能进入核心域。

    在对子域进行了分析,或对问题空间进行了评估之后,团队所绘制的上下文映射图如图所示,从一个限界上下文中分离出了两个子域。由于子域和限界上下文最好保持一对一的关系,我们应该将原来的协作上下文分离成两个限界上下文。

    在这里插入图片描述

    对子域和边界的分析要求我们做出决定。当人们需要使用CollabOvation的功能时,他们扮演者时参与者、作者和主持者的身份。有了这样的角色分离,我们便可以绘制一个更高层次的上下文映射图,如图所示。团队使用了分离内核对系统进行重构。

    在这里插入图片描述

    当下一个ProjectOvation项目启动时,团队将使用这个新的核心域——敏捷项目管理上下文来增强已有的上下文映射图,如图所示。

    在这里插入图片描述

    SaaSOvation的开发团队已经对核心的模型分离有了很好的理解。与CollabOvation相似,当ProjectOvation的用户扮演的是产品负责人或团队成员的角色。身份与访问上下文已经从核心域中分离出去了。协作上下文对于ProjectOvation来说只是一个支撑子域。任何时候,这个新的模型都受到了上下文边界的保护,外部概念需要通过翻译才能进入核心域中。

    身份与访问上下文位于最上游,它对协作上下文和敏捷项目管理上下文均会产生影响。同时,协作上下文又是敏捷项目管理上下文的上游,因为后者的模型依赖于前者的模型和服务。

    在限界上下文中我们提到,ProjectOvation将自治地运行,而不会依赖于周边的环境。这并不是说自治服务就可以完全独立于上游模型,而是我们的设计应该尽可能地限制实时依赖性。虽然ProjectOvation是自治的,但是它依然属于其他系统的下游。

    上图中,上游系统的连接框,都标以OHS/PL,分别表示开放主机服务和发布语言。所有下游的连接框都标以ACL,即防腐层。简单地来说,这些集成模式采用以下技术:

    • 开放主机服务:该模式可以通过REST实现。通常来讲,我们可以将开放主机服务看成是远程过程调用(RPC)的API。同时,他也可以通过消息机制实现。

    • 发布语言:发布语言可以通过多种形式实现,但最常见的是使用XML Schema。在使用REST服务时,发布语言用来表示领域概念,此时可以使用XML和JSON。发布语言既可以使用标准的媒体类型进行发布,也可以使用自定义类型。同时,发布语言还可以用于事件驱动架构(Event-Driven Architecture),其中领域事件以消息的形式发送给订阅方。

    • 防腐层:在下游上下文中,我们可以为每个防腐层定义相应的领域服务(Domain Service)。同时,你也可以将防腐层用于资源接口。在使用REST时,客户端的领域服务将访问远程的开放主机服务,远程服务以发布语言的形式返回,下游的防腐层将返回内容翻译成本地上下文的领域对象。比如,协作上下文向身份与访问上下文请求“具有Moderator角色的用户”。所返回的数据可能是XML格式或JSON格式,然后防腐层将这些数据翻译成协作上下文中的Moderator对象,该对象是一个值对象。这个Moderator实例反映的是下游模型中的概念,而不是上游模型。

    由于协作上下文是第一个核心域,让我们把它放大来看看。首先我们将接触到一些简单的集成方式,然后再是更高级的。

    4、协作上下文

    协作上下文作为SaaSOvation公司的第一个核心域。开发团队已经对其有很好的理解了。这里他们使用的集成方式比较简单,但是在可靠性和自治性上还稍逊一筹。要将此上下文映射图进行放大还是相对容易的。

    身份与访问上下文通过REST的方式向外发布服务。作为该上下文的客户,协作上下文通过传统类似于RPC的方式来获取外部资源。协作上下文并不会永久性地记录下从身份与访问上下文中获取来的数据,而是在每次需要数据时重新向系统发出请求。显然,协作上下文高度依赖于远程服务,它不具有自治性。但目前SaaSOvation公司愿意采取这种方式集成,以满足交付计划。由于之后的ProjectOvation项目将使用自治性服务,CollabOvation到时可以借鉴ProjectOvation的做法。

    在这里插入图片描述

    上图是放大后的上下文映射图,下游系统的边界对象采用同步的方式向上游系统获取到资源。当获取到远程数据模型数据之后,边界对象取出所需数据,再将其翻译成适当的值对象实例。

    在这里插入图片描述

    在上图中,翻译图(Translation Map)将所获取数据转化成一个值对象。这里,身份与访问上下文中一个具有Moderator角色的User被翻译成了协作上下文中的Moderator值对象。

    不幸的是,如果由于远程系统不可用而导致同步请求失败,那么本地系统也将跟着失败。虽然REST并不是真正意义上的RPC,但它却具有与RPC相似的特征。彻底的系统失败并不多见,但它却是一个潜在的问题。CollabOvation团队急切地希望解决这个问题。

    5、敏捷项目管理上下文

    为了达到比RPC更高的自治性,敏捷项目管理上下文团队将尽量限制对RPC的使用,此时他们可以选择异步请求,或者事件处理等方式。

    如果系统所依赖的状态已经存在于本地,那么我们将获得更大自治性。有人可能认为这只是对所有依赖对象进行缓存,但这不是DDD的做法。DDD的做法是:在本地创建一些由外部模型翻译而成的领域对象,这些对象保留着本地模型所需的最小的状态集。为了初始化这些对象,我们要有限的RPC调用或REST请求。然而,要与远程模型保持同步,最好的方式是在远程系统中采用面向消息的通知机制。消息通知可以通过服务总线进行发布,也可以采用消息队列或者REST。

    被同步的状态应该是本地模型所需远程模型的最小属性集。这里并不只是限制我们的对数据的需求,还应该对概念进行恰当的建模。

    6、和身份与访问上下文集成

    在这里插入图片描述

    在图中我们看到,对于身份与访问上下文的领域事件,系统将以URI的方式向外发布事件通知。这种功能是通过NotificationResource提供的,它向外发布REST资源。这里的通知资源是一组被发布的领域事件。对于消费方来说,每个事件总是可用的。消费方应该避免对事件的重复消费。

    一个自定义的媒体类型表明客户可以请求两种资源:
    在这里插入图片描述

    通过第一个资源URI,客户可以(使用HTTPGET请求)获取当前的通知日志(一个固定大小的通知集合)。对于自定义的媒体类型:
    在这里插入图片描述

    可以看出,这里的URI是全新的,并且是稳定的,因此它不会改变。无论当前的通知日志中包含了什么样的内容,该URI都会将其发布。

    事实上,ProjectOvation团队并不打算全盘使用REST。比如,他们目前正与CollabOvation团队协商是否可以使用消息机制,例如rabbitMQ。但就目前而言,它们和身份与访问上下文的集成依然是基于REST的。

    现在,让我们忽略技术细节,看看映射图中各个交互对象所扮演的角色。如图表示集成过程的序列图:

    在这里插入图片描述

    • MemberService是一个领域服务,它向本地模型提供ProductOwner和TeamOwner对象,同时作为基本防腐层的接口。maintainService方法用于周期性地检查身份与访问上下文所发出的通知。该方法不由模型的正常用户调用,而是由通知组件周期性地调用,在上图中,MemberSynchronizer表示这样的同步组件,它会把请求委派给MemberService。
    • MemberService进一步把请求委派给IdentityAccessNotificationAdapter,该类在领域服务和远程开放主机服务之间扮演着适配器的角色。该适配器作为远程系统的客户端而存在。与远程系统NotificationResource交互并没有显示在图中。
    • 一旦适配器从远程的开放主机服务获取到了数据,它将调用Member
      Translator的toMember()方法将发布语言中的媒体数据翻译成本地系统中的领域对象Member。如果该对象在本地系统中已经存在,则更新该对象。MemberService的updateMember()方法用于更新一个Member
      对象,此时它把委托操作委派给了自己。Member的子类有ProductOwner和TeamMember,它们反映了本地系统中的上下文概念。

    我们不应该将重点放在技术实现和集成产品上,而应该放在限界上下文之间的分离上,这样我们可以保持每个上下文的纯洁性,同时将一个上下文中的数据用于另一个上下文的概念中。

    让我们看看敏捷管理上下文是如何与协作上下文集成的。同样,我们关注的是系统的自治性,但这给集成带来了更多的困难与挑战。

    ProjectOvation将使用CollabOvation所提供的附加功能,比如论坛和共享日历等。ProjectOvation用户并不直接与CollabOvation交互。对于某租户来说,ProjectOvation必须决定出哪些CollabOvation的功能时该租户可用的。然后,ProjectOvation将协调对CollabOvation资源的创建。

    比如,Forum和Discussion必须在协作上下文进行创建的。在身份与访问上下文中,对象是先前存在的。而对于敏捷项目管理上下文来说,对象是不会预先存在的,直到被请求为止。这对于实现系统自治性来说是一个潜在的障碍,因为我们依赖于协作上下文来远程地创建资源。

    在使用领域事件和事件驱动架构时,我们应该仔细思考最终一致性。本地系统产生的事件通知并不是只能由远程系统消费。在ProjectOvation中,当ProjectInitiated事件产生时,该事件将由本地系统进行处理。本地系统要求Forum和Discussion在远程完成创建。这可以通过RPC或消息机制完成。

    如果项目负责人是图使用一个不存在的Discussion会发生什么问题吗?有时是由于调用失败,有时是由于没有预先付款导致协作功能不可用,这不一定是一个技术原因。对于最终一致性的处理我们应该将其考虑在建模访问之内。

    处理资源不可用的一个好办法便是将其显现出来。考虑以下由标准类型实现的状态和模式。此时的状态是一个值对象。
    在这里插入图片描述
    在该设计中,由DiscussionAvailability定义的状态将对象Discussion其保护作用。当有人是图参加关于一个Product的讨论时,该设计将正确地处理Discussion的状态。如果状态不为READY,参与者将得到以下消息之一:
    (1)要使用团队协作功能,你需要购买附加功能。
    (2)产品负责人还没请求创建产品讨论。
    (3)讨论启动失败,请稍后再试。

    此时,ProjectOvation团队还不清楚采用哪种方式与CollabOvation进行集成。在讨论了客户方-供应方关系后,它们得到了下图:
    在这里插入图片描述

    敏捷项目管理上下文可以使用第二个防腐层来处理与协作上下文之间的集成。事实上CollaborationAdapter并不是单个,此处只是一个占位符,表示有多个适配器。

    敏捷项目上下文在本地有DiscussionService和SchedulingService,它们是领域服务,用于管理协作系统中的讨论和日历条目。

    以上的例子已经展示了上下文映射图的某些细节。

    对于一个非常详细的上下文映射图,我们很有可能无法对其进行实时更新。保持简单性和敏捷性,拒绝繁文缛节,这样我们创建的上下文映射图将对项目起推动作用,而不是阻碍作用。

    展开全文
  • Spring mvc 上下文初始化过程

    千次阅读 2016-04-21 23:59:37
    正是如此,Spring由于其IOC、AOP、事务处理、持久化驱动等特点,使得其起到了一个应用平台的作用。Spring MVC是Spring的一个重要的模块,其web应用的实现,是由Spring的来支撑的,Spring MVC的是实现也是依托再...

    在软件开发的中,如果某些特性的使用比较普遍,那么这些特性往往可以作为平台特性来实现,通过对这些平台特性进行有效的封装,使其向其他应用开放。正是如此,Spring由于其IOC、AOP、事务处理、持久化驱动等特点,使得其起到了一个应用平台的作用。Spring MVC是Spring的一个重要的模块,其web应用的实现,是由Spring的来支撑的,Spring MVC的是实现也是依托再Spring平台提供的基础特性的。本文主要是介绍Spring mvc容器初始化的过程,从中可以看出Spring MVC的对于Spring的依赖。

    一、从Web应用角度看Spring MVC

    在Servlet模型中,请求-响应的实现依赖于两大元素的共同配合:

    1. 配置Servlet及其映射关系(在web.xml中) 
    2. 在Servlet实现类中完成响应逻辑

    项目规模扩大之后,请求-响应的映射关系全部定义在web.xml中,将造成web.xml的不断膨胀而变得难以维护。针对这个问题,SpringMVC提出的方案就是:提炼一个核心的Servlet覆盖对所有Http请求的处理。这一被提炼出来的Servlet,通常被我们称之为:核心分发器。在SpringMVC中,核心分发器就是org.springframework.web.servlet.DispatcherServlet

    核心分发器要解决的是下面两个问题:

    问题1:核心Servlet应该能够建立起一整套完整的对所有Http请求进行规范化处理的流程。

    问题2:核心Servlet应该能够根据一定的规则对不同的Http请求分发到不同的Servlet对象上去进行处理。

    针对上面的这个两个问题,SpringMVC的解决方案是:将整个处理流程规范化,并把每一个处理步骤分派到不同的组件中进行处理。 

    处理流程规范化 :将处理流程划分为若干个步骤(任务),并使用一条明确的逻辑主线将所有的步骤串联起来
    处理流程组件化 : 将处理流程中的每一个步骤(任务)都定义为接口,并为每个接口赋予不同的实现模式
     

    处理流程规范化的首要内容就是考虑一个通用的Servlet响应程序大致应该包含的逻辑步骤: 

    • 对Http请求进行初步处理,查找与之对应的Controller处理类(方法)
    • 调用相应的Controller处理类(方法)完成业务逻辑
    • 对Controller处理类(方法)调用时可能发生的异常进行处理
    • 根据Controller处理类(方法)的调用结果,进行Http响应处理

    所谓的组件化,实际上也就是使用编程语言将这些逻辑语义表达出来。在Java语言中,最适合表达逻辑处理语义的语法结构是接口,而接口可以有不同的实现,因此上述的四个流程也就被定义为了四个不同接口,它们分别是: 

    • HandlerMapping
    • HandlerAdapter
    • HandlerExceptionResolver
    • ViewResolver

    二、从Spring角度看Spring MVC

    从上面可以看出,组件是核心分发器(DispatchServlet)的核心所在,它们是http请求处理的逻辑载体,DispatcherServlet是逻辑处理的调度中心,组件则是被调度的操作对象。而Spring容器在这里所起到的作用,是协助DispatcherServlet更好地对组件进行管理

    我们知道,SpringMVC的组件是一个个的接口定义,当我们在SpringMVC的核心配置文件中定义一个组件时,使用的却是组件的实现类,用具体的实现类来指定组件的行为模式,不同的实现类代表了不同的行为模式,它们在Spring中是可以共存的。Spring容器对这些实现类进行管理,具体如何使用,由应用程序本身来决定。


    上图是Spring官方reference中的一幅图,DispatchServlet对外接收http的请求,而请求的处理的是依靠组件来完成的,组件的接口实现的是依靠Spring IOC容器(WebApplicationContext)来管理。从这个图中我们可以看出,Spring MVC实现web应用是依赖与Spring提供的基础特性(IOC等)。图中的两个WebApplicationContext的区别,留到下面再讲。

    三、Spring MVC 入口配置文件web.xml

    Spring mvc 有哪些配置文件:

    1. 入口配置文件:web.xml;由web或应用服务器为每个web项目加载的配置文件。
    2. 应用上下文:包括web框架特有配置文件:SpringMVC的${dispatcherServletName}-servlet.xml配置文件。和Spring的配置文件applicationContext.xml,applicationContext-*.xml。 

    遵循servlet规范,Spring MVC的web应用的入口配置文件也是web.xml。web容器的初始化首先是加载web.xml文件,Jetty在启动时首先加载此配置文件,并且对其中定义的listener和servlet等进行相应的加载和初始化。Jetty并不清楚(也不关心)其他配置文件的存在,因此,加载其他配置文件应该是你(框架)的事情。那么怎么加载呢?前面说过Jetty在启动时会自动加载和初始化listener和servlet,那么我们可以自定义一个listener(ContextLoaderListener)或servlet(DispatcherServlet),Jetty会根据web.xml加载和初始化他们,而他们则负责加载相应的配置文件。

    在web.xml配置文件中,有两个主要的配置:ContextLoaderListener和DispatcherServlet。同样的关于spring配置文件的相关配置也有两部分:context-param和DispatcherServlet中的init-param。那么,这两部分的分别表示是Spring 容器的初始化和web容器的初始化。DispatcherServlet和ContextLoaderListener提供了在Web容器中对spring的接口。ServletContext为Spring的IOC容器提供了一个宿主环境,在宿主环境中,Spring MVC建立起了一个IOC容器体系。

    下面看一下Spring mvc 中web.xml文件的相关配置内容:

     

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
        <display-name>push-center-server</display-name>
        <description><span style="font-family: Arial, sans-serif;">test</span></description>
        <context-param>
            <param-name>webAppRootKey</param-name>
            <param-value>test</param-value>
        </context-param>
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath:applicationContext.xml
            </param-value>
        </context-param>
    
    
        <context-param>
            <param-name>log4jConfigLocation</param-name>
            <param-value>classpath:log4j2.xml</param-value>
        </context-param>
    
    
        <!-- 项目使用的配置文件位置.项目启动自动读取 -->
        <context-param>
            <param-name>propertiesConfigLocation</param-name>
            <param-value>/WEB-INF/conf/config.properties</param-value>
        </context-param>
    
    
        <!--jmonitor-->
        <context-param>
            <param-name>jmonitor-configfile</param-name>
            <param-value>jmonitor.properties</param-value>
        </context-param>
        <listener>
            <listener-class>com.meituan.jmonitor.servlet.ContextListener</listener-class>
        </listener>
    
    
        <listener>
            <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
        </listener>
    
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
    
    
    
        <listener>
            <listener-class>
                org.springframework.web.context.request.RequestContextListener
            </listener-class>
        </listener>
    
    
    
    
        <!--jmonitor http collector-->
        <filter>
            <filter-name>JMonitorHttpMonitorFilter</filter-name>
            <filter-class>com.meituan.jmonitor.collector.http.HttpMonitorFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>JMonitorHttpMonitorFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    
        <servlet>
            <servlet-name>appServlet</servlet-name>
            <servlet-class>com.sankuai.meituan.web.MtDispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:webmvc-config.xml</param-value>
            </init-param>
            <load-on-startup>2</load-on-startup>
        </servlet>
    
    
        <servlet-mapping>
            <servlet-name>appServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    
    </web-app>
     


    四、Spring IOC容器(根上下文)的初始化

     Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext接口扩展ApplicationContext,使得拥有web功能,WebApplicationContext接口默认的实现是XmlWebApplicationContext。那么,Spring MVC是如何在web环境中创建IoC容器呢?

    先看一下WebApplicationContext的源码,

    WebApplicationContext

    public interface WebApplicationContext extends ApplicationContext {
        //用于在ServletContext中存取根上下文
        String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
         
        String SCOPE_REQUEST = "request";
         
        String SCOPE_SESSION = "session";
         
        String SCOPE_GLOBAL_SESSION = "globalSession";
         
        String SCOPE_APPLICATION = "application";
         
        String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
         
        String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
         
        String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
     
        //取得当前web容器的ServletContext
        ServletContext getServletContext();
    }

     

          在Spring MVC中,Spring Context是以父子的继承结构存在的。Web环境(ServletContext)中存在一个根上下文,这个Context是整个应用的根上下文,是其他context的双亲Context。同时Spring MVC也对应的持有一个独立的Context,它是根上下文的子上下文。

    由ContextLoaderListener首先启动的上下文为根上下文,该上下文是与ServletContext相伴而生的,在根上下文的基础上,Spring MVC对应持有的一个用来管理控制器需要的对象的子上下文。下图是ContextLoaderListener继承关系,它实现了ServletContextListener接口,这个接口提供了与Servlet生命周期结合的回调,比如contextInitialized和contextDestroyed方法。建立WebApplicationContext的过程是在contextInitialized的接口实现中完成的,具体的载入IOC容器的过程是由ContextLoader来完成的。


     ContextLoaderListener

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        private ContextLoader contextLoader;
     
        public ContextLoaderListener() {
        }
         
        public ContextLoaderListener(WebApplicationContext context) {
            super(context);
        }
     
        public void contextInitialized(ServletContextEvent event) {
            this.contextLoader = createContextLoader();
            if (this.contextLoader == null) {
                this.contextLoader = this;
            }
            this.contextLoader.initWebApplicationContext(event.getServletContext());
        }
         
        @Deprecated
        protected ContextLoader createContextLoader() {
            return null;
        }
         
        @Deprecated
        public ContextLoader getContextLoader() {
            return this.contextLoader;
        }
     
         
        public void contextDestroyed(ServletContextEvent event) {
            if (this.contextLoader != null) {
                this.contextLoader.closeWebApplicationContext(event.getServletContext());
            }
            ContextCleanupListener.cleanupAttributes(event.getServletContext());
        }

    从ContextLoaderListener源码可以看出,实现的是ServletContextListener接口,这个接口里的函数会结合Web容器的生命周期被调用。因为ServletContextListener是ServletContext的监听者,当servletContext启动或者停止的时候,会触发响应的事件,监听器ServletContextListener会接收到事件,会做出相应的响应,比如这里的contextInitialized方法和contextDestroyed方法。Spring IOC容器(根上下文)的生成与销毁就是通过这个两个方法的,所以根上下文是与ServletContext相伴而生的。

    所以当Web应用启动时,contextInitialized方法会执行载入根上下文(IOC容器),具体过程是首先从Servlet事件中得到ServletContext,然后以ServletContext为宿主环境,载入根上下文(IOC容器),具体的载入过程是由ContextLoader处理的。

    下图所示为根上下文的加载过程,下面将结合源码来看一下这个过程是如何实现的。

     

    根上下文的载入过程:


          ROOT Context是在ContextLoaderListener中配置的,ContextLoaderListener读取context-param中的contextConfigLocation指定的配置文件,创建ROOT Context。下面看一下ContextLoaderListener中创建context的源码:

    ContextLoader加载根上下文的源码

    //这里开始对WebApplicationContext进行初始化
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
      
    //在整个web应用中,只能有一个根上下文,判断ServletContext中是否已经有根上下文
            if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
                throw new IllegalStateException(
                        "Cannot initialize context because there is already a root application context present - " +
                        "check whether you have multiple ContextLoader* definitions in your web.xml!");
            }
            Log logger = LogFactory.getLog(ContextLoader.class);
            servletContext.log("Initializing Spring root WebApplicationContext");
            if (logger.isInfoEnabled()) {
                logger.info("Root WebApplicationContext: initialization started");
            }
            long startTime = System.currentTimeMillis();
            try {
                // Store context in local instance variable, to guarantee that
                // it is available on ServletContext shutdown.
                if (this.context == null) {
    // 执行了创建WebApplicationContext的操作
                    this.context = createWebApplicationContext(servletContext);
                }
                if (this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                    if (!cwac.isActive()) {
                        // The context has not yet been refreshed -> provide services such as
                        // setting the parent context, setting the application context id, etc
                        if (cwac.getParent() == null) {
                            // The context instance was injected without an explicit parent ->
                            // determine parent for root web application context, if any.
    //载入根上下文的双亲上下文
                            ApplicationContext parent = loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }
                        configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }
      
    //将根上下文放置在servletContext中
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
                if (ccl == ContextLoader.class.getClassLoader()) {
                    currentContext = this.context;
                }
                else if (ccl != null) {
                    currentContextPerThread.put(ccl, this.context);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
                            WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
                }
                if (logger.isInfoEnabled()) {
                    long elapsedTime = System.currentTimeMillis() - startTime;
                    logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
                }
                return this.context;
            }
            catch (RuntimeException ex) {
                logger.error("Context initialization failed", ex);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
                throw ex;
            }
            catch (Error err) {
                logger.error("Context initialization failed", err);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
                throw err;
            }
        }
      
        protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    // 根据web.xml中的配置,决定用那种WebApplicationContext,默认用XmlWebApplicationContext
            Class<?> contextClass = determineContextClass(sc);
    //判断contextClass是否继承ConfigurableWebApplicationContext或者是其接口实现
            if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
                throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                        "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
            }
    //直接实例化需要产生的IOC容器
            return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
        }
      
    //设置IOC容器各个参数,然后通过refresh启动容器的初始化
    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    //设置application context ID
            if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
                // The application context id is still set to its original default value
                // -> assign a more useful id based on available information
                String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
                if (idParam != null) {
                    wac.setId(idParam);
                }
                else {
                    // Generate default id...
                    if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
                        // Servlet <= 2.4: resort to name specified in web.xml, if any.
                        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                                ObjectUtils.getDisplayString(sc.getServletContextName()));
                    }
                    else {
                        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                                ObjectUtils.getDisplayString(sc.getContextPath()));
                    }
                }
            }
    //设置ServletContext
            wac.setServletContext(sc);
    //设置配置文件的位置参数
            String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
            if (initParameter != null) {
                wac.setConfigLocation(initParameter);
            }
    //在refresh之前,根据配置的config locations重新定制(Customize)ConfigurableWebApplicationContext
            customizeContext(sc, wac);
    //启动容器的初始化
            wac.refresh();
        }

    从上面的源码中可以看出来,IOC容器在web容器中的启动过程,与在应用中启动IOC容器的方式相似,不同的是这里需要考虑web容器的环境的特点,比如各种参数的设置,IOC容器与web容器ServletContext的结合等。在初始化上下文以后,该上下文被存储再ServletContext中,这样就建立了一个全局的关于整个应用的上下文,即所谓的根上下文。同时在启动Spring MVC的时候,DispatchServlet在进行自己持有的上下文的初始化时,是将此根上下文设置为自己的双亲上下文的。

    http://blog.csdn.net/and1kaney/article/details/51214193   这篇文章可以看到根容器初始化过程的整个的call hierarchy。

    五、Spring MVC容器(子上下文)的初始化

    以上是web容器中根上下文的加载与初始化,在完成对ContextLoaderListener的初始化以后,web容器开始初始化DispatchServlet,DispatchServlet会建立自己的上下文来管理Spring MVC的bean对象。在建立这个自己持有的上下文的时候,会从ServletContext中得到根上下文作为DispatchServlet持有的上下文的双亲上下文,再对自己持有的上下文进行初始化,最后把自己持有的这个上下文也保存到ServletContext中

    我们先看下DispatchServlet的继承关系,如下图。DispatchServlet通过继承FrameworkServlet和HttpServletBean而继承了HttpServlet。HttpServletBean是Spring对于Servlet最低层次的抽象。在这一层抽象中,Spring会将这个Servlet视作是一个Spring的bean,并将web入口配置文件web.xml中DispatchServlet定义的init-param参数中的值作为bean的属性注入进来。

     

    DispatcherServlet也是一个Servlet,根据Servlet规范的定义,Servlet中的两大核心方法init方法和service方法: 

    1. init方法 

    在整个系统启动时运行,且只运行一次。因此,在init方法中我们往往会对整个应用程序进行初始化操作。这些初始化操作可能包括对容器(WebApplicationContext)的初始化、组件和外部资源的初始化等等。 

    2. service方法 

    在整个系统运行的过程中处于侦听模式,侦听并处理所有的Web请求。因此,在service及其相关方法中,我们看到的则是对Http请求的处理流程。 

     

    这篇文章主要是介绍Spring mvc 容器的初始化,所以主要是介绍iDisipatchServlet的init方法。



    HttpServletBean

     @Override
        public final void init() throws ServletException {
            if (logger.isDebugEnabled()) {
                logger.debug("Initializing servlet '" + getServletName() + "'");
            }
            // Set bean properties from init parameters.
            try {
    //读取web.xml中DispatchServlet定义中的<init-param>,对Bean属性进行配置
                PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    //生成一个BeanWrapper,将当前的这个Servlet类转化为一个BeanWrapper,从而能够以Spring的方式来对init-param的值进行注入
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
    //将init-param中的值注入
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                throw ex;
            }
            // 调用子类的initServletBean进行具体的初始化
            initServletBean();
            if (logger.isDebugEnabled()) {
                logger.debug("Servlet '" + getServletName() + "' configured successfully");
            }
        }

    FrameworkServlet则是在HttpServletBean的基础之上的进一步抽象。通过FrameworkServlet真正初始化了一个Spring的容器(WebApplicationContext),并引入到Servlet对象之中: 

    FrameworkServlet 

    /**
         * Overridden method of {@link HttpServletBean}, invoked after any bean properties
         * have been set. Creates this servlet's WebApplicationContext.
         */
        @Override
        protected final void initServletBean() throws ServletException {
            getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
            if (this.logger.isInfoEnabled()) {
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
            }
            long startTime = System.currentTimeMillis();
            try {
    //对Spring的容器(webApplicationContext)进行初始化
                this.webApplicationContext = initWebApplicationContext();
                initFrameworkServlet();
            }
            catch (ServletException ex) {
                this.logger.error("Context initialization failed", ex);
                throw ex;
            }
            catch (RuntimeException ex) {
                this.logger.error("Context initialization failed", ex);
                throw ex;
            }
            if (this.logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                        elapsedTime + " ms");
            }
        }
      
    protected WebApplicationContext initWebApplicationContext() {
    //这里调用WebApplicationContextUtils静态类来从ServletContext中得到根上下文,使用这个根上下文作为当前MVC上下文的双亲上下文。
            WebApplicationContext rootContext =
                    WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            WebApplicationContext wac = null;
            if (this.webApplicationContext != null) {
                // 如果一个context的实例被注入了,直接用
                wac = this.webApplicationContext;
                if (wac instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                    if (!cwac.isActive()) {
                        // 如果此上下文还没有初始化,就设置上下文的参数,如双亲上下文、application context id等
                        if (cwac.getParent() == null) {
                            // 如果被注入的context实例没有双亲上下,则将根上下文设置为其双亲上下文
                            cwac.setParent(rootContext);
                        }
    //设置其他参数,并启动容器初始化
                        configureAndRefreshWebApplicationContext(cwac);
                    }
                }
            }
            if (wac == null) {
                // No context instance was injected at construction time -> see if one
                // has been registered in the servlet context. If one exists, it is assumed
                // that the parent context (if any) has already been set and that the
                // user has performed any initialization such as setting the context id
    //没有注入的context实例,这里从ServletContext中查找是否有context实例已经注册到了servlet context了
                wac = findWebApplicationContext();
            }
            if (wac == null) {
                // No context instance is defined for this servlet -> create a local one
    //在ServletContext没有context实例,所以需要创建一个WebApplicationContext,以根上下文为双亲下文创建
                wac = createWebApplicationContext(rootContext);
            }
    //刷新上下文(执行组件的初始化),这个方法由子类DispatchServlet的方法实现
            if (!this.refreshEventReceived) {
                // Either the context is not a ConfigurableApplicationContext with refresh
                // support or the context injected at construction time had already been
                // refreshed -> trigger initial onRefresh manually here.
                onRefresh(wac);
            }
    //把当前的上下文存到ServletContext中去,使用的属性名是和当前的Servlet名相关的
            if (this.publishContext) {
                // Publish the context as a servlet context attribute.
                String attrName = getServletContextAttributeName();
                getServletContext().setAttribute(attrName, wac);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                            "' as ServletContext attribute with name [" + attrName + "]");
                }
            }
            return wac;
        }
      
    //创建上下文
    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    // 根据web.xml中的配置,决定用那种WebApplicationContext,默认用XmlWebApplicationContext
            Class<?> contextClass = getContextClass();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Servlet with name '" + getServletName() +
                        "' will try to create custom WebApplicationContext context of class '" +
                        contextClass.getName() + "'" + ", using parent context [" + parent + "]");
            }
            if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
                throw new ApplicationContextException(
                        "Fatal initialization error in servlet with name '" + getServletName() +
                        "': custom WebApplicationContext class [" + contextClass.getName() +
                        "] is not of type ConfigurableWebApplicationContext");
            }
            ConfigurableWebApplicationContext wac =
                    (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    //设置上下文,并启动初始化
            wac.setEnvironment(getEnvironment());
            wac.setParent(parent);
            wac.setConfigLocation(getContextConfigLocation());
            configureAndRefreshWebApplicationContext(wac);
            return wac;
        }

    在这个调用关系中,可以看到MVC的初始化是再DispatchServlet的initStrategies方法中完成的,包括对各种MVC框架的实现元素,比如支持国际化的LocaleResolver、支持request映射的HandlerMappings、以及视图生成的ViewResolver等的初始化。对于具体实现元素的初始化就不一一列出源码了,这里以HandlerMappings为例来说明MVC框架元素的初始化过程。

    DispatchServlet

    @Override
        protected void onRefresh(ApplicationContext context) {
            initStrategies(context);
        }
        /**
         * Initialize the strategy objects that this servlet uses.
         * <p>May be overridden in subclasses in order to initialize further strategy objects.
         */
    初始化默认的Spring Web MVC框架使用的策略(如HandlerMapping)
        protected void initStrategies(ApplicationContext context) {
            initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            initHandlerMappings(context);
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            initViewResolvers(context);
            initFlashMapManager(context);
        }
      
    private void initHandlerMappings(ApplicationContext context) {
            this.handlerMappings = null;
    //这里导入所有的HandlerMapping Bean,这些Bean可以是在当前的DispatchServlet的IOC容器,也可以是其双亲上下文中的,这里detectAllHandlerMappings默认是为true的,从所有的IOC容器中取
            if (this.detectAllHandlerMappings) {
                // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
                Map<String, HandlerMapping> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
                if (!matchingBeans.isEmpty()) {
                    this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                    // We keep HandlerMappings in sorted order.
                    OrderComparator.sort(this.handlerMappings);
                }
            }
            else {
    //从当前IOC容器中通过getBean获取handlerMapping
                try {
                    HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                    this.handlerMappings = Collections.singletonList(hm);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Ignore, we'll add a default HandlerMapping later.
                }
            }
    //如果没有找到handlerMappings,设置默认的handlerMapping,默认值设置在DispatcherServlet.properties中
            // Ensure we have at least one HandlerMapping, by registering
            // a default HandlerMapping if no other mappings are found.
            if (this.handlerMappings == null) {
                this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
                }
            }
        }

    六、Spring MVC 上下初始化流程图


     

    参考:

    http://blog.csdn.net/c289054531/article/details/9196149

    http://blog.csdn.net/prince2270/article/details/5889117

    http://blog.arganzheng.me/posts/config-file-in-web-application.html


    展开全文
  • Spring各种上下文的关系详解

    千次阅读 2019-06-11 16:39:23
    要想很好理解这三个上下文的关系,可以Debug追踪源码加深自己的理解。这对于解决出现的问题和需要仿写类似的框架提供了很多的思路。最近发现去品读源码,对于框架有了更深的理解和解决了做项目期间遗留的种种疑惑。 ...

           要想很好理解这三个上下文的关系,可以Debug追踪源码加深自己的理解。这对于解决出现的问题和需要仿写类似的框架提供了很多的思路。最近发现去品读源码,对于框架有了更深的理解和解决了做项目期间遗留的种种疑惑。

    Spring的启动过程:

           首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;

           其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;

           再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是xmlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

    总结:

    ServletContext:  tomcat启动会创建一个ServletContext,作为全局上下文以及spring容器的宿主环境,可以理解为web容器(Servlet容器)
    WebApplicationContext:即初始化根上下文(即IOC容器)
    DispatcherServlet:WebApplicationContext设置为当前DispatcherServlet的父上下文。并且也把DispatcherServlet上下文存在ServletContext中
    通过init方法创建的dispatcherServlet上下文可以访问通过ServletContextListener中创建的WebApplicationContext上下文中的bean,反之则不行。因为WebApplicationContext是dispatcherServlet上下文的父容器。

    Spring API中的解释:

    public interface WebApplicationContext
    extends ApplicationContext

    Interface to provide configuration for a web application. This is read-only while the application is running, but may be reloaded if the implementation supports this.

    This interface adds a getServletContext() method to the generic ApplicationContext interface, and defines a well-known application attribute name that the root context must be bound to in the bootstrap process.

    Like generic application contexts, web application contexts are hierarchical. There is a single root context per application, while each servlet in the application (including a dispatcher servlet in the MVC framework) has its own child context.

    In addition to standard application context lifecycle capabilities, WebApplicationContext implementations need to detect ServletContextAware beans and invoke the setServletContext method accordingly.

    翻译:

    公共接口WebApplicationContext
    扩展ApplicationContext
    提供Web应用程序配置的接口。 这在应用程序运行时是只读的,但是如果实现支持这个,可以重新加载。
    该接口将一个getServletContext()方法添加到通用ApplicationContext接口,并定义一个众所周知的应用程序属性名称,该名称在引导进程中必须绑定根上下文。
    像通用应用程序上下文一样,Web应用程序上下文是分层的。 每个应用程序都有一个根上下文,而应用程序中的每个servlet(包括MVC框架中的调度器servlet)都有自己的子上下文。
    除了标准的应用程序上下文生命周期功能,WebApplicationContext实现还需要检测ServletContextAware bean,并相应地调用setServletContext方法。

    展开全文
  • java上下文

    2019-02-21 22:50:46
    1.context就是“容器”,放的就是应用程序的所有资源,要用...web上下文可以看成web应用的运行环境,一般用context名字来修饰,里面保存了web应用相关的一些设置和全局变量2.ServletContext,是一个全局的储存信息...
  • 1.web上下文、spring上下文、springMVC上下文之间的关系 ... 要想很好理解这三个上下文的关系,需要...spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程。 spr
  • spring上下文和springMVC上下文的关系

    千次阅读 2017-12-05 14:02:04
    在tomcat部署一个用SSM搭起来的项目,一般会有两个上下文容器,分别是spring和SpringMVC的容器,在spring中一个容器是可以有父容器的,那么spring的容器和SpringMVC的容器关系是怎么样的。 先看tomcat中的web.xml...
  • java web上下文理解

    千次阅读 2019-02-18 10:46:05
    1.context就是“容器”,放的就是应用程序的所有资源,要用时候就...web上下文可以看成web应用的运行环境,一般用context名字来修饰,里面保存了web应用相关的一些设置和全局变量2.ServletContext,是一个全局的储...
  • 官方文档里是说先理解应用上下文比较好,不过我还是觉得反过来,从请求上下文开始记录比较合适,所以这篇先记录请求上下文。 什么是请求上下文 通俗点说,其实上下文就像一个容器,包含了很多你需要的信息
  • NR 5G UE初始接入流程

    万次阅读 多人点赞 2019-03-29 15:29:28
    1. UE向gNB-DU发送RRC连接请求消息。 2. gNB-DU包括RRC消息,并且如果UE被允许,则在F1AP INITIAL UL RRC MESSAGE TRANSFER消息中包括用于UE的相应低层配置,并且传输到gNB-CU。 初始UL RRC消息传输消息包括由gNB-...
  • 一:初始化流程详解 转自:https://my.oschina.net/lichhao/blog/102315 在我们第一次学Servlet编程,学java web的时候,还没有那么多框架。我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根据...
  • 总结,AMF发送建立UE上下文请求、DU建立UE上下文、DU收到UE上下文建立请求时,向UE发送重配消息。 AMF发送UE上下文,DU负责建立UE上下文,UE接入需要重配消息 ue经过du与cu建立rrc连接之后,amf向cu发出配
  • Spring IoC是一个独立的模块,它并不是直接在Web容器中发挥作用的。如果要在Web环境中使用IoC容器,需要Spring为IoC设计一个启动过程,把IoC容器导人,并在Web容器中建立起来。具体说来,这个启动过程是和Web容器的...
  • 这说明在spring初始化bean的时候,如果bean实现了InitializingBean接口,会自动调用afterPropertiesSet方法。 原文地址:https://www.cnblogs.com/study-everyday/p/6257127.html 问题 实现InitializingBean接...
  • 在Spring web环境中,spring 上下文和spring mvc...spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程。 spring的启动过程: 一个web应用,其部署在web容器中...
  • 无线掉线率优化指导

    千次阅读 2020-04-29 10:38:33
    无线掉线率=(eNodeB发起的S1 RESET导致的UE Context释放次数+UE Context异常释放次数)/UE Context建立成功总次数*100% 2 信令流程: 如上图:【图1】中A点所示,当eNodeB向MME发送UE CONTEXT RELEASE REQUEST消息...
  • Chromium硬件加速渲染的OpenGL上下文调度过程分析

    万次阅读 热门讨论 2015-12-07 01:02:07
    Chromium的每一个WebGL端、Render端和Browser端实例在GPU进程中都有一个OpenGL上下文。这些OpenGL上下文运行在相同线程中,因此同一时刻只有一个OpenGL上下文处于运行状态。这就引发出一个OpenGL上下文调度问题。...
  • 为了达到这个目的,GPU线程分别为它们创建不同的OpenGL上下文,并且使得它们的GPU命令在各自的OpenGL上下文中执行。本文接下来就详细分析WebGL端、Render端和Browser端的OpenGL上下文的创建过程。
  • 一个TCP连接通常分为3个阶段:启动、数据...1.主动开启者(通常称为客户端)发送一个SYN报文段(即一个在TCP头部的SYN位字段置位的TCP/IP数据包),并指明自己想要连接的端口号和它的客户端初始序列号(记为ISN(c)...
  • 直接得出: 根据数组建立平衡二叉搜索树 java整体打印二叉树 判断平衡二叉树 判断完全二叉树 判断二叉搜索树 二叉搜索树实现 堆的简单实现 堆应用例题三连 一个数据流中,随时可以取得中位数。 金条 项目最大收益...
  • Selenium修改HTTP请求头三种方式

    千次阅读 2022-02-22 20:39:35
    标题可以根据其上下文进行分组: 请求头:HTTP 请求请求头用于提供有关正在获取的资源和发出请求的客户端的附加信息。 响应头:HTTP 响应头提供有关响应的信息。 以下是 HTTP 请求请求头中包含的主要信息: I
  • Android的应用程序开发采用JAVA语言,Activity本质也是一个对象,那上面的写法有什么问题呢?估计很多人说不清道不明。Android程序不像Java程序一样,随便创建一个类,写个main()方法就能运行,Android应用模
  • 上下文监听者ServletContextListener

    千次阅读 2015-02-28 21:07:43
    一个监听类,不是一个servlet或JSP,它能监听ServletContext一生中的两个...1.上下文初始化时得到通知(应用得到部署)。 a.从ServletContext得到上下文初始化参数。 b.使用初始化参数查找名建立一个数据库连接。
  • ngxin请求行与请求头处理

    千次阅读 2017-01-15 01:15:47
    一篇文件分析了nginx服务器接收到客户端的连接后,会创建一个连接对象,但此时没有涉及到任何的http模块。本文在上一章基础下分析nginx接收到http请求后的初始化流程、接收http请求行、接收http请求头部的流程。
  • 在上一篇博客SpringMVC源码分析--容器初始化(三)HttpServletBean我们介绍了HttpServletBean的init函数,其主要作用初始化了一下SpringMVC配置文件的地址contextConfigLocation的配置属性,然后其调用的子类...
  •     Activity启动流程源码实现(七)初始化目标Activity Android四大组件源码实现详解系列博客目录: Android应用进程创建流程大揭秘 Android四大组件之bindService源码实现详解 Android四大组件之Activity启动...
  • asp.net 请求、处理、响应原理浅析

    千次阅读 2016-03-07 18:48:49
    注意,两个对象的创建是有先后顺序的,先创建上下文对象在创建Application对象(Application对象的管道流通需要使用上下文对象作为参数)。接下来就是执行Application对象的ProcessRequest()方法执行管道,进行管道...
  • 5G SA携带SUCI的初始注册流程

    万次阅读 多人点赞 2020-01-10 21:10:38
    这里写自定义目录标题1、 UE-RAN Registration Request终端发起注册请求2、AMF Selection AMF选择3、RAN-AMF RegistrationRequest RAN转发注册请求4、AUSF Selection AUSF选择5、Authencation 鉴权5.1 AMF-AUSF ...
  • Java实现HTTPS请求及证书证书验证(附源码)   先发布一个初始的版本,主要以...3、服务器初始化创建SSLContext上下文类型,这个过程包括创建密钥管理库和信任库两部分,使用的是SUN509的套件。 4、创建一个SSLCo

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 80,619
精华内容 32,247
关键字:

初始上下文建立请求的作用