ApplicationContext是Spring的核心,Context咱们一般解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了:P,Spring把Bean放在这个容器中,在须要的时候,用getBean方法取出,虽然我没有看过这一部分的源代码,但我想它应该是一个相似Map的结构。
在Web应用中,咱们会用到WebApplicationContext,WebApplicationContext继承自ApplicationContext,先让咱们看看在Web应用中,怎么初始化WebApplicationContext,在web.xml中定义:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- OR USE THE CONTEXTLOADERSERVLET INSTEAD OF THE LISTENER
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
-->
能够看出,有两种方法,一个是用ContextLoaderListener这个Listerner,另外一个是ContextLoaderServlet这个Servlet,这两个方法都是在web应用启动的时候来初始化WebApplicationContext,我我的认为Listerner要比Servlet更好一些,由于Listerner监听应用的启动和结束,而Servlet得启动要稍微延迟一些,若是在这时要作一些业务的操做,启动的先后顺序是有影响的。
那么在ContextLoaderListener和ContextLoaderServlet中到底作了什么呢?
以ContextLoaderListener为例,咱们能够看到
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
protected ContextLoader createContextLoader() {
return new ContextLoader();
}
ContextLoader是一个工具类,用来初始化WebApplicationContext,其主要方法就是initWebApplicationContext,咱们继续追踪initWebApplicationContext这个方法(具体代码我不贴出,你们能够看Spring中的源码),咱们发现,原来ContextLoader是把WebApplicationContext(XmlWebApplicationContext是默认实现类)放在了ServletContext中,ServletContext也是一个“容器”,也是一个相似Map的结构,而WebApplicationContext在ServletContext中的KEY就是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,咱们若是要使用WebApplicationContext则须要从ServletContext取出,Spring提供了一个WebApplicationContextUtils类,能够方便的取出WebApplicationContext,只要把ServletContext传入就能够了。
上面咱们介绍了WebApplicationContext在Servlet容器中初始化的原理,通常的Web应用就能够轻松的使用了,可是,随着Struts的普遍应用,把Struts和Spring整个起来,是一个须要面对的问题,Spring自己也提供了Struts的相关类,主要使用的有org.springframework.web.struts.ActionSupport,咱们只要把本身的Action继承自ActionSupport,就是能够调用ActionSupport中getWebApplicationContext()的方法取出WebApplicationContext,但这样一来在Action中,须要取得业务逻辑的地方都要getBean,看上去不够简洁,因此Spring又提供了另外一个方法,用org.springframework.web.struts.ContextLoaderPlugIn,这是一个Struts的Plug,在Struts启动时加载,对于Action,能够像管理Bean同样来管理,在struts-config.xml中Action的配置变成相似下面的样子
<action attribute="aForm" name="aForm" path="/aAction" scope="request" type="org.springframework.web.struts.DelegatingActionProxy">
<forward name="forward" path="forward.jsp" />
</action>
注意type变成了org.springframework.web.struts.DelegatingActionProxy,以后咱们须要创建action-servlet.xml这样的文件,action-servlet.xml符合Spring的spring-beans.dtd标准,在里面定义相似下面的
<bean name="/aAction" class="com.web.action.Aaction" singleton="false">
<property name="businessService">
<ref bean="businessService"/>
</property>
</bean>
com.web.action.Aaction是Action的实现类,businessService是须要的业务逻辑,Spring会把businessService注入到Action中,在Action中只要写businessService的get和set方法就能够了,还有一点,action的bean是singleton="false",即每次新建一个实例,这也解决了Struts中Action的线程同步问题,具体过程是当用户作“/aAction”的HTTP请求(固然应该是“/aAction.do”),Struts会找到这个Action的对应类org.springframework.web.struts.DelegatingActionProxy,DelegatingActionProxy是个代理类,它会去找action-servlet.xml文件中“/aAction”对应的真正实现类,而后把它实例化,同时把须要的业务对象注入,而后执行Action的execute方法。
使用了ContextLoaderPlugIn,在struts-config.xml中变成相似这样配置
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml,/WEB-INF/action-servlet.xml" />
</plug-in>
而在web.xml中再也不须要ContextLoaderListener或是ContextLoaderServlet。
说到这里不知道你们会不会有这样的问题,若是使用ContextLoaderPlugIn,若是咱们有些程序是脱离Struts的Action环境,咱们怎么处理,好比咱们要自定义标记库,在标记库中,咱们须要调用Spring管理的业务层逻辑对象,这时候咱们就很麻烦,由于只有在action中动态注入业务逻辑,其余咱们彷佛不能取得Spring的WebApplicationContext。
别急,咱们仍是来看一下ContextLoaderPlugIn的源码(源码再也不贴出),咱们能够发现,原来ContextLoaderPlugIn仍然是把WebApplicationContext放在ServletContext中,只是这个KEY不太同样了,这个KEY值为ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX+ModuleConfig.getPrefix()(具体请查看源代码),这下好了,咱们知道了WebApplicationContext放在哪里,只要咱们在Web应用中可以取到ServletContext也就能取到WebApplicationContext了:) web