************IMPORTANT***************
下面这篇文章有欠考虑(但有些思路还是有点用处的,故不忍删除)。故此特别声明,以免误导大家。详细原因请看回复。
现在公司里面用Spring,都是把spring跟每个应用集成到一块。然后几个应用部署在同一台tomcat下面。
这样容易产生的一个问题是每个应用到加载一次Spring的相关jar。如果应用比较多,难免会影响到
jvm的PermanentGenerationSpace. 于是读了下Spring的源码,分析了下把Spring jar包放在tomcat公共目录下面的可行性。
结果发现,确实可以。下面以tomcat6为例,分析一下。
tomcat6 的classloader的结构大致如下:
Bootstrap
|
System
|
Common
/ \
Webapp1 Webapp2 ...
其中Common是加载tomcat的jar文件。WebappX对应的是每个web的一个classloader. 这些Webappx classloader不同于一般classloader的地方在于放弃了Parent Delegation 模型,加载class的时候,首先试图从当前classloader对应目录下面加载,如果本classloader加载不到,再把加载请求委托给上级classloader. 所以如果每个web application都有自己的一套Spring jar包。那么肯定是每个都分别加载一次的。下面要分析的就是如果把jar包放在tomcat6的common目录下面,而不是web applicaiton的lib下面,到底能不能加载?
从Spring的源码入手,以下是web application加载webapplicationContext的代码:
public WebApplicationContext initWebApplicationContext(ServletContext 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 {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
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;
}
}
可以看到每个web application的局部变量servletContext都存储了对应的webapplicationcontext.这就保证了
每当通过web的local instance servletContext来取webapplicationcontext时取到的是自己的context.
注意其中有这么一段:
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
如果Spring的jar包是放在tomcat的common目录下面而没有放在web application下面的,那么
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
这个返回的是WebappX classloader;
而ContextLoader.class.getClassLoader() 返回的是tomcat的 common classloader.
两个classloader是不一样的。于是执行如下代码:
currentContextPerThread.put(ccl, this.context);
注意当前类的classloader也同样是Common classloader. 于是在里面维护了一个
map,这个map的key是common classloader的下级classloader--也就是WebappX classloader;对应的
value是每个web application根据配置文件生成的webapplicationcontext.
这样做的目的在于当通过ConetxtLoader的静态方法获取context的时候,能保证获取的是当前web application的context.实际上就是对于tomcat下面的任何一个线程,我们都能很方便的找出这个线程对应的webapplicationContext.于是在一些不能方便获取servletContext的场合,我们可以通过当前线程获取webapplicationContext.
代码如下:
public static WebApplicationContext getCurrentWebApplicationContext() {
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl != null) {
WebApplicationContext ccpt = currentContextPerThread.get(ccl);
if (ccpt != null) {
return ccpt;
}
}
return currentContext;
}
当然也可以通过每个web application的servletContext获取webapplicationContext(还记得上面提到的设置的servletContext局部变量吗?). 因为是局部变量,所以也可以顺利的取到对应的webapplicationContext.
(同时,这种方式也有另一个作用。那就是某个web application卸载的时候,可以很方便地在ContextLoader里面获取web对应的context并且移除,从而避免了内存溢出。)
于是,截至到webapplicationContext的生成和获取,是OK 的。
下面需要注意的就是因为webapplicationContext以及他的子类的classloader都是common classloader.
那么必须要保证webapplicationContext的实现类里面没有跟webapplication相关的class变量。否则
多个webapplicaton将能够互相影响。看了一下XmlWebApplicationContext的类层次关系(这个类是在web系统中默认的webapplicationContext的实现类),发现没有static变量。OK.
由此可以断定,哪怕是common classloader加载,各个web application的context也是可以互不影响的!
以上仅限于理论。我会找时间做一个测试,看看这种猜测是否准确。
如果有什么地方我忽略了,也欢迎大家拍砖~
分享到:
相关推荐
最新版spring-web-5.2.3.RELEASE和spring-webmvc-5.2.3.RELEASE
spring-web.jar spring-web.jar
开发工具 spring-web-4.3.6.RELEASE开发工具 spring-web-4.3.6.RELEASE开发工具 spring-web-4.3.6.RELEASE开发工具 spring-web-4.3.6.RELEASE开发工具 spring-web-4.3.6.RELEASE开发工具 spring-web-4.3.6.RELEASE...
开发工具 spring-webmvc-4.3.6.RELEASE开发工具 spring-webmvc-4.3.6.RELEASE开发工具 spring-webmvc-4.3.6.RELEASE开发工具 spring-webmvc-4.3.6.RELEASE开发工具 spring-webmvc-4.3.6.RELEASE开发工具 spring-web...
最新的spring3.0的lib包,该包将组件分的更细致,可以灵活使用,不再是一个spring.jar就可以使用的,需要哪个组件用哪个
META-INF/MANIFEST.MF META-INF/license.txt org.springframework.remoting.caucho.BurlapClientInterceptor.class org.springframework....org/springframework/web/util/HtmlCharacterEntityReferences.properties
spring-webmvc-4.jar 下载spring-webmvc-4.jar 下载spring-webmvc-4.jar 下载spring-webmvc-4.jar 下载spring-webmvc-4.jar 下载spring-webmvc-4.jar 下载
好像没有在csdn下载到spring框架所需完整的lib包,现在打包上传
这个jar文件包含Spring MVC框架相关...spriing-webmvc 依赖于 spring-web如果直接使用spring-webmvc,就会隐式地添加 spring-web。不必显示添加 spring-web。 该jar包含Spring MVC框架相关的所有类,如Servlets,Web MVC
spring-web-5.0.4.RELEASE.jar; spring-web-5.0.4.RELEASE.jar
spring-web.jar spring-webmvc.jar spring mvc框架需要用到的
Spring源代码解析(二):IoC容器在Web容器中的启动.doc
spring-webmvc-4.1.0.RELEASE.jar用于Spring框架的mvc注解
Spring源代码解析(二):ioc容器在Web容器中的启动 Spring源代码分析(三):Spring JDBC Spring源代码解析(四):Spring MVC Spring源代码解析(五):Spring AOP获取Proxy Spring源代码解析(六):Spring声明式事务处理...
spring容器启动和关闭时事件监听;spring容器启动和关闭时事件监听;spring容器启动和关闭时事件监听
spring-webmvc-portlet-3.2.7.RELEASE.jarspring-webmvc-portlet-3.2.7.RELEASE.jarspring-webmvc-portlet-3.2.7.RELEASE.jarspring-webmvc-portlet-3.2.7.RELEASE.jar
spring-webmvc-struts.jar对Struts和Spring整合时需要用到的包
spring-web的源码
spring-web-5.1.5.RELEASE.jar