Spring的IOC容器如何嵌入到我们运行中的容器中的?
通过Servlet来嵌入,通过ContextLoaderListener来进行IOC容器的启动,其实是通过ServletContextListener来监听ServletContext相关的事件来初始化IOC容器,Spring在Web容器中初始化的IOC容器最后是一个XMLWebApplicationContext,关于loadResource中如何查找Bean的资源文件,即我们常说的Beans.xml文件,是通过获取到ServletContext中的相关参数的(即配置在web.xml)中的相关参数,我们可以通过Servelt这个句柄来获取,注意是在ContextLoader中设置的。ContextLoader是初始化SpringIOC容器的相关类,而ContextLoaderListener是组合相关的Servlet与Spring的粘合剂,真正的初始化的工作是ContextLoader来做的。
默认的Bean.xml文件时WEB-INF/applicationContext.xml
在ContextLoaderListener中,实现的是ServletContextListener接口,这个接口里的函数会结合Web容器的生命周期被调用。因为ServletContextListener是ServletContext的监听者,如果ServletContext发生变化,会触发出相应的事件,而监听器一直对这些事件监听,如果接受到监听的事件,就会做出预先设计好的响应动作。由于ServletContext的变化而触发的监听器的响应包括:在服务器启动时,ServletContext被创建的时候;服务器关闭时,ServletContext将被销毁的时候等。对应这些事件及web容器状态的变化,在监听器中定义了对应的事件响应的回调方法。比如在服务器启动时,ServletContextListener的contextInitialized()方法被调用,服务器将要关闭时,ServletContextListener的contextDestroyed()方法被调用。
使用什么IOC作为Web上下文是在ContextLoader中的determineContextClass决定的,获取的是ContextLoader.properties中配置的org.springframework.web.context.support.XmlWebApplicationContext,当然也可以在web.xml中配置contextClass属性来优先使用作为上下文。
默认设置双亲上下文ContextLoader中
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(sc);
注意此处的ContextLoader 和DispatchServelt的先后初始化的顺序,web.xml中不同组件加载的顺序,
context-param -> listener -> filter -> servlet
DispatcherServlet会重新初始化一个IOC的容器,和之前初始化的基本相同也是XMLWebApplicationContext
但是会将之前初始化的XMLWebApplicationContext作为父容器放入这次创建的子上下文中。在这个初始化的过程中,一个新的上下文被建立起来,这个DispatcherServlet持有的上下文是被设置成根上下文的子上下文。可以认为,根上下文是和Weby应用相对应的一个上下文,而DIspatcherServlet持有的上下文是和Servlet对应的一个上下文。在一个应用中,往往可以容纳多个Servlet的存在,与此对应的,对于应用在Web容器中的上下文体系,一个根上下文可以作为许多Servlet上下文的双亲上下文。了解了这一点,对于在Web容器中IOC容器中的Bean设置和检索会有更多的了解,IOC工作原理中获取Bean,如果当前上下文获取不到Bean,就会去双亲上下文中获取Bean。也就是说根上下文中定义的Bean是可以被各个Servlet持有的上下文得到和共享的。
DispatherServlet中初始化IOC容器,initStrategies()方法。
doDispatch的处理流程:
SpringMvc中视图模块的呈现
1、如何寻找到合适的视图展现
2、如何解释相关的视图参数
3、视图资源的转发
SpringMvc的整个运作原理:
SpringMvc主要是以DispatcherServlet为中心的,在DispatcherServlet中建立起了IOC容器,并且使用了ContextLoader创建的WebApplicationContext作为父容器。在做onRefresh的时候,初始化相关的SpringMvc的组件,包括handerMapping,MutiPartResolver,LocalResolver,HandlerAdapter,ThemeResolver等等。组件初始化好了之后,DispatcherServlet利用doService来处理进来的相关的http请求,找到相关的处理器(C),映射相关的ModelView,处理相关视图中的元素,转发请求。
SpringMvc需要实现的以下几个步骤:
1、需要建立Controller控制器和Http请求之间的映射关系,即在SpringMvc实现中是如何根据请求得到对应的Controller的?通过分析可以看到,在SpringMvc中,这个工作是由在handlerMapping中封装的HandlerExecutionChain对象来完成的,而对Controller控制器和Http请求的映射关系的配置是在Bean定义中描述,并在IOC容器初始化的时候,通过初始化handlerMapping来完成,这些定义的映射关系会被载入到一个HandlerMapping中。
2、在初始化的过程中,Controller对象和Http请求之间的映射关系建立好之后,为SpringMvc接收Http请求并完成响应处理做好了准备。在MVC框架接收到HTTP请求的时候,DispatchServlet会根据相关的url请求信息,在HandlerMapping中进行查询,找到对应的HandlerExecutionChain,在这个HandlerExecutionChain中封装了真正处理的Controller,这个请求对应的Controller就会完成相关的响应动作。生成需要的ModelAndView对象,这个对象就和它的名字一样,可以从该对象中获取Model模型数据和View视图对象。
3、得到ModelAndView以后,DispatchServlet把获得的模型数据交给特定的视图对象,从而完成这些数据的视图呈现工作。这个视图呈现由视图对象的render方法来处理。对于不同的视图,render方法会完成不同的视图处理,为用户提供丰富的WebUi表现。
关于Request对象的在Controller中的传递:
Request保持的数据,是通过ThreadLocal保存的,在其它地方获取相关的Request参数。
ThreadLocal变量仅仅是把变量从方法的生命周期衍生到了线程生命周期。只不过获取的方法变得比较方便了而已。避免创建一个共享变量,特别是在单例模式中(一般创建成多线程可见的),而带来多线程的问题。
HandlerMapping 与HandlerAdapter的区别:
HandlerMapping的意义在于,request与内部处理对象(一般而言是Controller)之前映射关系,当然包含一些Interceptor,形成一个处理的链条HandlerExecutionChain,
HandlerAdapter的意义,个人认为只是为了触发匹配上的handler的调用,因为相关的handler没有公共的接口定义,为了匹配不同的处理器(Handler)设计出了一个适配层,可能不同的handler没有统一的方法调用,当然这样的方式是更灵活,但是也引入了更多的复杂性。