************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也是可以互不影响的!
以上仅限于理论。我会找时间做一个测试,看看这种猜测是否准确。
如果有什么地方我忽略了,也欢迎大家拍砖~
分享到:
相关推荐
4. `web`:这个目录可能包含Web应用的公共部分,比如`WEB-INF`目录,其中`web.xml`是Servlet容器的部署描述符,用于配置Web应用程序。此外,还可能包含HTML、CSS、JavaScript等前端资源。 5. `lib`:这是一个存放...
这个名为“Linux-memcached-tomcat8-session共享lib.rar”的压缩包文件显然旨在解决一个特定的问题:在Linux系统上,通过Nginx作为负载均衡器,利用Memcached实现跨多个Tomcat实例的session共享。下面将详细解释这个...
需要在lib目录下添加Spring Session的相关jar包,然后在Spring配置中进行相应的配置。 5. **JWT(JSON Web Tokens)**:另一种非传统的方法是使用JWT来替代传统的Session。JWT是一种轻量级的身份验证标准,可以在...
### Spring2.5 学习笔记详解 #### 一、Spring 框架简介 Spring 是一个开源的轻量级 ...以上就是关于 Spring2.5 学习笔记的主要知识点,通过这些内容的学习,可以更好地理解和掌握 Spring 框架的基本原理和使用方法。
在开发和部署Java Web应用时,开发者有时会将自定义的JAR文件放入Tomcat的`lib`目录,以便让所有Web应用共享这些库。这样做可以避免在每个应用的`WEB-INF/lib`目录下重复包含相同的库,从而减少内存占用和提高性能。...
WEB-INF下还有lib目录,用于存放应用所需的jar包,classes目录则存储编译后的类文件。 5. **HTTP协议**:理解Web服务器工作原理的基础是熟悉HTTP协议。HTTP是无状态的应用层协议,支持请求-响应模型。常见的HTTP...
2. **添加依赖**:在Tomcat6的lib目录下,加入session共享jar包,通常包括Redis的Java客户端库(如Jedis)和其他处理Session的库(如Spring Session)。 3. **配置Context**:在Tomcat的`server.xml`或每个Web应用的...
2. **添加依赖**:在Tomcat的`lib`目录下,添加与Redis相关的Java库,例如`spring-session-data-redis`或`jedis`。压缩包中的jar文件可能包含了这些库。 3. **配置Tomcat**:在`$CATALINA_HOME/conf/context.xml`或...
3.2 手动添加:在没有构建工具的环境中,可以手动将`jar`包放入项目的`lib`目录,或者通过Web容器的`classloader`配置来加载外部`jar`。 四、创建自己的`jar`包 4.1 使用`jar`命令行工具:通过`jar cf jarfile ...
Java库(JAR)是Java开发中的重要组成部分,它们封装了类、接口、资源和元数据,便于在不同项目间共享代码。标题“java lib-jar”表明这是一组Java库的集合,其中包含了187个JAR文件,这些文件可以供Java开发人员在...
Struts2、Spring和iBatis是Java Web开发中非常重要的三个框架,它们共同构建了一个典型的MVC(Model-View-Controller)架构。这个Demo实例是将这三个框架整合在一起,实现了一个三层架构的开发环境,提供了自动装配...
Tomcat是一款开源的Java Servlet容器,广泛用于部署Java Web应用程序。而Redis则是一种高性能的键值对存储数据库,支持多种数据结构如字符串、哈希、列表、集合和有序集合,非常适合用来存储Session数据。 接下来,...
- JSP页面会被Web容器转换为Servlet,然后进行执行,其主要作用是生成动态内容。 4. **JavaBean**: - JavaBean是符合特定规范的Java类,常用于封装数据和业务逻辑,便于在JSP和Servlet之间共享数据。 - 它通常...
将它们放入Tomcat的`lib`目录下。 3. **配置Tomcat的Context**:在Tomcat的`conf/context.xml`文件中,我们需要添加一个`Manager`元素,指定使用`org.springframework.session.data.redis.RedisHttpSessionManager`...
在`lib`目录下的jar包可能是实现Tomcat与Redis交互所需的库,例如`jedis.jar`(Jedis是Java客户端用于操作Redis)和其他可能的依赖,如`spring-session-data-redis.jar`(Spring Session提供了一种在不同服务器间...
1. 将该jar包添加到Tomcat的lib目录,或者如果是Spring Boot项目,则将其作为依赖加入到pom.xml或build.gradle文件中。 2. 配置Tomcat的session管理器,指定使用RedisSessionManager,并设置相关的Redis连接参数。 ...
在每个Tomcat实例的`WEB-INF/lib`目录下,添加Redis的Java客户端库,如`jedis.jar`。 3. **配置Tomcat**:修改`$CATALINA_HOME/conf/context.xml`文件,在`<Context>`标签内添加以下配置,指定使用Redis作为Session...
- `spring-web-x.x.x.jar` 和 `spring-webmvc-x.x.x.jar`:Spring Web 模块,支持 Web 应用的上下文和 MVC 框架。 - 可能还有其他依赖库,如日志库(log4j 或 slf4j)、JDBC 驱动等,这些是根据具体项目需求来决定的...
在Java Web开发中,Tomcat作为一款广泛应用的开源Servlet容器,常常被用来部署Web应用程序。随着互联网应用的扩展,单体应用可能需要处理大量的用户会话(Session),这时,单一服务器的Session管理可能会成为性能...
4. **目录结构**:Tomcat的目录结构包括`bin`(可执行文件)、`conf`(配置文件)、`lib`(库文件)、`webapps`(Web应用程序的部署目录)等。 5. **部署应用**:`.war`文件可以直接放在`webapps`目录下,Tomcat会...