`
michael.softtech
  • 浏览: 208465 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

关于Spring放在web容器公共目录共享lib下面的分析

阅读更多

************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也是可以互不影响的!

 

 

以上仅限于理论。我会找时间做一个测试,看看这种猜测是否准确。

如果有什么地方我忽略了,也欢迎大家拍砖~

 

 

 

 

 

 

分享到:
评论
3 楼 hello28 2013-04-14  
虽然其他有些问题,不过关于ClassLoader还是帮我解惑了,支持
2 楼 michael.softtech 2010-11-15  
恩。我试了一下。确实有不少问题。我试着弄了一下发现问题多多啊。
1. 首先就是bean加载的时候。bean class的加载流程?因为如果spring的classloader是commonclassloader,这个classloader采用双亲代理的模式,是不可能加载得到我们自己定义的bean class的。如果没有做一些classloader的特别处理(比如通过thread 的contextclassloader从而在一个上级的classloader里面能够引用到下级的classloader),那么应用肯定会报错。

2. spring引用的一些第三方包。比如:log4j,struts,ibatis,ehcache.spring提供了对这些第三方框架的支持。处于跟上面同样的原因,假如要多个应用共享spring包,那么这些包不可避免 的也要放在tomcat公共目录下面。这些第三方 包本身对这种跨应用的共享是否支持?

由此看来,我之前写的那些东西确实是欠考虑的。不好意思,误导大家了~我会在正文里面更正。
1 楼 Kisses99 2010-11-12  
WebapplicationContext是应该可以用,但是log4j动作是否正常呢?

相关推荐

    Spring Web Service 实战篇(1)

    4. `web`:这个目录可能包含Web应用的公共部分,比如`WEB-INF`目录,其中`web.xml`是Servlet容器的部署描述符,用于配置Web应用程序。此外,还可能包含HTML、CSS、JavaScript等前端资源。 5. `lib`:这是一个存放...

    Linux-memcached-tomcat8-session共享lib.rar

    这个名为“Linux-memcached-tomcat8-session共享lib.rar”的压缩包文件显然旨在解决一个特定的问题:在Linux系统上,通过Nginx作为负载均衡器,利用Memcached实现跨多个Tomcat实例的session共享。下面将详细解释这个...

    seesion共享需替换tomcat下lib的包

    需要在lib目录下添加Spring Session的相关jar包,然后在Spring配置中进行相应的配置。 5. **JWT(JSON Web Tokens)**:另一种非传统的方法是使用JWT来替代传统的Session。JWT是一种轻量级的身份验证标准,可以在...

    Spring2.5学习笔记

    ### Spring2.5 学习笔记详解 #### 一、Spring 框架简介 Spring 是一个开源的轻量级 ...以上就是关于 Spring2.5 学习笔记的主要知识点,通过这些内容的学习,可以更好地理解和掌握 Spring 框架的基本原理和使用方法。

    tomcat-lib

    在开发和部署Java Web应用时,开发者有时会将自定义的JAR文件放入Tomcat的`lib`目录,以便让所有Web应用共享这些库。这样做可以避免在每个应用的`WEB-INF/lib`目录下重复包含相同的库,从而减少内存占用和提高性能。...

    WEB服务器 Java

    WEB-INF下还有lib目录,用于存放应用所需的jar包,classes目录则存储编译后的类文件。 5. **HTTP协议**:理解Web服务器工作原理的基础是熟悉HTTP协议。HTTP是无状态的应用层协议,支持请求-响应模型。常见的HTTP...

    【tomcat6使用redis配置session共享】

    2. **添加依赖**:在Tomcat6的lib目录下,加入session共享jar包,通常包括Redis的Java客户端库(如Jedis)和其他处理Session的库(如Spring Session)。 3. **配置Context**:在Tomcat的`server.xml`或每个Web应用的...

    tomcat8-redis-session共享

    2. **添加依赖**:在Tomcat的`lib`目录下,添加与Redis相关的Java库,例如`spring-session-data-redis`或`jedis`。压缩包中的jar文件可能包含了这些库。 3. **配置Tomcat**:在`$CATALINA_HOME/conf/context.xml`或...

    web开发常用jar包

    3.2 手动添加:在没有构建工具的环境中,可以手动将`jar`包放入项目的`lib`目录,或者通过Web容器的`classloader`配置来加载外部`jar`。 四、创建自己的`jar`包 4.1 使用`jar`命令行工具:通过`jar cf jarfile ...

    java lib-jar

    Java库(JAR)是Java开发中的重要组成部分,它们封装了类、接口、资源和元数据,便于在不同项目间共享代码。标题“java lib-jar”表明这是一组Java库的集合,其中包含了187个JAR文件,这些文件可以供Java开发人员在...

    STRUTS2+SPRING+IBATIS搭建的Demo实例

    Struts2、Spring和iBatis是Java Web开发中非常重要的三个框架,它们共同构建了一个典型的MVC(Model-View-Controller)架构。这个Demo实例是将这三个框架整合在一起,实现了一个三层架构的开发环境,提供了自动装配...

    tomcat-redis实现session共享

    Tomcat是一款开源的Java Servlet容器,广泛用于部署Java Web应用程序。而Redis则是一种高性能的键值对存储数据库,支持多种数据结构如字符串、哈希、列表、集合和有序集合,非常适合用来存储Session数据。 接下来,...

    ruanjianProject.zip

    - JSP页面会被Web容器转换为Servlet,然后进行执行,其主要作用是生成动态内容。 4. **JavaBean**: - JavaBean是符合特定规范的Java类,常用于封装数据和业务逻辑,便于在JSP和Servlet之间共享数据。 - 它通常...

    utf8''tomcat7下基于redis的session共享所需jar包

    将它们放入Tomcat的`lib`目录下。 3. **配置Tomcat的Context**:在Tomcat的`conf/context.xml`文件中,我们需要添加一个`Manager`元素,指定使用`org.springframework.session.data.redis.RedisHttpSessionManager`...

    tomcat7 集群通过redis共享session 所需jar包

    在`lib`目录下的jar包可能是实现Tomcat与Redis交互所需的库,例如`jedis.jar`(Jedis是Java客户端用于操作Redis)和其他可能的依赖,如`spring-session-data-redis.jar`(Spring Session提供了一种在不同服务器间...

    tomcat基于redis实现session共享所依赖的jar包

    1. 将该jar包添加到Tomcat的lib目录,或者如果是Spring Boot项目,则将其作为依赖加入到pom.xml或build.gradle文件中。 2. 配置Tomcat的session管理器,指定使用RedisSessionManager,并设置相关的Redis连接参数。 ...

    tomcat7+redis完成session共享

    在每个Tomcat实例的`WEB-INF/lib`目录下,添加Redis的Java客户端库,如`jedis.jar`。 3. **配置Tomcat**:修改`$CATALINA_HOME/conf/context.xml`文件,在`<Context>`标签内添加以下配置,指定使用Redis作为Session...

    shiro1.2.2

    - `spring-web-x.x.x.jar` 和 `spring-webmvc-x.x.x.jar`:Spring Web 模块,支持 Web 应用的上下文和 MVC 框架。 - 可能还有其他依赖库,如日志库(log4j 或 slf4j)、JDBC 驱动等,这些是根据具体项目需求来决定的...

    tomcat7下基于redis的session共享所需jar包

    在Java Web开发中,Tomcat作为一款广泛应用的开源Servlet容器,常常被用来部署Web应用程序。随着互联网应用的扩展,单体应用可能需要处理大量的用户会话(Session),这时,单一服务器的Session管理可能会成为性能...

    Tomcat详细资料

    4. **目录结构**:Tomcat的目录结构包括`bin`(可执行文件)、`conf`(配置文件)、`lib`(库文件)、`webapps`(Web应用程序的部署目录)等。 5. **部署应用**:`.war`文件可以直接放在`webapps`目录下,Tomcat会...

Global site tag (gtag.js) - Google Analytics