1.现象
在从jboss迁移到jetty后,有一个应用页面报了如下异常:
1 2 3 4 5 6 |
net.sf.json.JSONException: java.lang.ClassCastException: com.ali.martini.biz.marketing.time.Parser$PeriodType cannot be cast to java.lang.String at com.ali.martini.web.marketing.SendTimeDtoUtil$1.setProperty(SendTimeDtoUtil.java:210) at net.sf.json.JSONObject.setProperty(JSONObject.java:1497) at net.sf.json.JSONObject.toBean(JSONObject.java:387) at com.ali.martini.common.JsonUtil.JSONStringToBean(JsonUtil.java:44) at com.ali.martini.web.marketing.SendTimeDtoUtil.JSONStringToBean(SendTimeDtoUtil.java:216) |
看了下代码大概是这么一个操作:
1 2 3 4 5 6 7 |
JsonConfig config = new JsonConfig(); config.setPropertySetStrategy(new PropertySetStrategy() { public void setProperty(Object bean, String key, Object value) throws JSONException { if("periodType".equals(key)){ Object val = PeriodType.valueOf((String)value); } } |
2.原因
换了容器报错,第一能想到的就是jar包的问题,是否这个类加载了不同版本的Class,导致以前传入的value是一个String,现在不是了。
为了求证这个问题,在该类的调用处使用了如下方式:
1 2 3 4 5 6 7 8 9 10 |
try { Enumeration<URL> urls = this.getClass().getClassLoader().getResources("net/sf/json/JSONObject.class"); while(urls.hasMoreElements()) { URL url = urls.nextElement(); System.out.println("url!!="+url); logger.info("url!!="+url); } } catch (IOException e) { e.printStackTrace(); } |
然后观察发现,jboss的顺序是这样的:
1 2 |
jar:file:/D:/alibaba/jboss-4.2.2.GA/server/default/deploy/eve.war/WEB-INF/lib/ajax.json__json-lib-2.2-jdk15.jar-2.2.jar!/net/sf/json/JSONObject.class jar:file:/D:/alibaba/jboss-4.2.2.GA/server/default/deploy/eve.war/WEB-INF/lib/sourceforge.json-lib-2.2.3.jar!/net/sf/json/JSONObject.class |
到jetty下顺序就反过来了,sourceforge.json-lib-2.2.3.jar!/net/sf/json/JSONObject.class排到了前面,所以会出现这个问题。
用eclipse maven插件或者 mvn dependency:tree可以看到,以上两个jar包分别是在两个二方库引入的依赖(shy2和aranda)
考察jetty的jar包加载顺序:
jetty在启动的过程中,使用WebAppContext的configure来加载lib的路径,具体方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
@Override public void configure(WebAppContext context) throws Exception { //cannot configure if the context is already started if (context.isStarted()) { if (Log.isDebugEnabled()){Log.debug("Cannot configure webapp "+context+" after it is started");} return; } Resource web_inf = context.getWebInf(); // Add WEB-INF classes and lib classpaths if (web_inf != null && web_inf.isDirectory() && context.getClassLoader() instanceof WebAppClassLoader) { // Look for classes directory Resource classes= web_inf.addPath("classes/"); if (classes.exists()) ((WebAppClassLoader)context.getClassLoader()).addClassPath(classes); // Look for jars Resource lib= web_inf.addPath("lib/"); if (lib.exists() || lib.isDirectory()) ((WebAppClassLoader)context.getClassLoader()).addJars(lib); } ... } |
而调用的classloader具体的addJars方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* ------------------------------------------------------------ */ /** Add elements to the class path for the context from the jar and zip files found * in the specified resource. * @param lib the resource that contains the jar and/or zip files. */ public void addJars(Resource lib) { if (lib.exists() && lib.isDirectory()) { String[] files=lib.list(); for (int f=0;files!=null && f<files.length;f++) { try { Resource fn=lib.addPath(files[f]); String fnlc=fn.getName().toLowerCase(); if (!fn.isDirectory() && isFileSupported(fnlc)) { String jar=fn.toString(); jar=StringUtil.replace(jar, ",", "%2C"); jar=StringUtil.replace(jar, ";", "%3B"); addClassPath(jar); } } catch (Exception ex) { Log.warn(Log.EXCEPTION,ex); } } } } |
addClassPath最终调用父类URLClassLoader的addURL方法把
1 2 3 |
protected void addURL(URL url) { ucp.addURL(url); } |
加入到classloader的路径中。在类加载的时候,就是根据这个顺序一一查找path,看是否能找到对应的jar包。
因此,addClassPath的顺序就决定了那个jar包中的类被加载的问题。
而从上面的程序可以看到,file被list的顺序是由 String[] files=lib.list();决定的,因此查看lib.list
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public String[] list() { String[] list =_file.list(); if (list==null) return null; for (int i=list.length;i-->0;) { if (new File(_file,list[i]).isDirectory() && !list[i].endsWith("/")) list[i]+="/"; } return list; } |
实际使用的是java.io.file的list方法:
1 2 3 4 5 6 7 |
public String[] list() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } return fs.list(this); } |
最终调用的是 FileSystem的抽象方法
public abstract String[] list(File f);
在往下就是平台相关的代码了.
因为jdk源码查询不熟,所以写了一个简单代码,用外科的方式来考察:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class ListFileTest { public static void main(String[] args) { if (args.length == 0 || args[0] == null) { System.out.println("no args"); System.exit(0); } File file = new File(args[0]); String [] files = file.list(); for (String f :files) { System.out.println(f); } } } |
通过strace检查系统调用,得到如下有用信息:
15381 open(".", O_RDONLY|O_NONBLOCK|O_DIRECTORY) = 9 15381 fstat(9, {st_mode=S_IFDIR|0777, st_size=20480, ...}) = 0 15381 fcntl(9, F_SETFD, FD_CLOEXEC) = 0 15381 getdents(9, /* 75 entries */, 4096) = 4072 15381 getdents(9, /* 74 entries */, 4096) = 4080 15381 getdents(9, /* 76 entries */, 4096) = 4080 15381 getdents(9, /* 72 entries */, 4096) = 4064
可以发现最终调用的是getdents(最终好像是调用readdir),然后这个系统函数list的文件是什么顺序,目前我也没有搞懂,
有说法是按inode号,试试下好像也不是,总是,顺序是操作系统相关的且不能保证的。
3.解决方案:
1.复写jetty的webAppClassloader,将list出来的文件排序,甚至可以配置指定几个包的顺序在前。
2.通过maven配置exclude一个依赖,但要保证兼容,如果不兼容,需要沟通两方二方库人员解决
3.山寨办法,打包时对jar包重命名,不是很靠谱。
相关推荐
开发者在使用时,需要通过Android的`ClassLoader`加载这个库,并调用相应的API来启动和配置服务器。 `i-jetty-master`则可能是i-jetty项目的源码,这对于深入理解其工作原理和进行定制开发非常有帮助。源码通常包括...
- **ClassLoader加载的资源**:包括`log4j.properties`、`springContext.xml`等配置文件。 - **ServletContext加载的资源**:包括静态资源如`*.html`、`*.jpg`等。 综上所述,Jetty不仅在启动过程、组件装配等方面...
同时,手册还列举了实施过程中可能遇到的问题,如classloader系列问题和其他异常情况的解决方案,这对于顺利推进项目实施至关重要。 通过上述对“jetty实施手册”的深入解析,我们可以看到,无论是技术选型、配置...
5. **其他辅助功能**:还包括`console`命令提供REPL环境,`thread`命令查看线程状态,`classloader`命令探索类加载器关系等。 在`embedded-jetty-websocket-examples`这个压缩包中,包含了Arthas与嵌入式Jetty ...
内容概要:该文档介绍了常见的三种Java应用服务器(JBoss, Tomcat, Jetty)的整体架构及其启动流程,并深入探讨了它们各自的特性与配置要点。此外,文中还详细阐述了应用服务器的关键组件如类加载器(ClassLoader)的...
其次深入介绍Java技术,包括I/O技术、中文编码问题、Javac编译原理、class文件结构解析、ClassLoader工作机制及JVM的内存管理等。最后介绍Java服务端技术,主要包括Servlet、Session与Cookie、Tomcat与Jetty服务器、...
其次深入介绍了Java 技术,包括I/O 技术、中文编码问题、Javac 编译原理、class 文件结构解析、ClassLoader 工作机制及JVM 的内存管理等。最后介绍了Java 服务端技术,主要包括Servlet、Session 与Cookie、Tomcat 与...
- **Web容器**:现代Web容器(如Tomcat、Jetty等)广泛使用类加载器来支持多应用共存环境下的隔离性,每个Web应用都有自己的类加载器,这样可以避免不同应用之间的类冲突问题。 - **OSGi**:OSGi是一个Java平台的...
6. **使用容器或服务器**:在某些情况下,如Tomcat、Jetty等应用服务器或Spring Boot这样的微服务框架,它们有自己的类加载机制,可以更好地处理多JAR依赖关系。 每种解决方案都有其适用场景和优缺点,选择哪种方法...
最后介绍了Java 服务端技术,主要包括Servlet、Session 与Cookie、Tomcat 与Jetty服务器、Spring 容器、iBatis 框架和Velocity 框架等原理介绍,并介绍了服务端的一些优化技术。 《深入分析Java Web技术内幕(修订版...
主要有三个类加载器:Bootstrap ClassLoader、Common ClassLoader和Webapp ClassLoader。Bootstrap加载JDK的类,Common加载`common.loader`指定的类,而Webapp类加载器则负责加载每个Web应用自己的类。理解类加载...
内置Tomcat、Jetty等容器,支持嵌入式运行。 - **Spring MVC**: 作为Spring框架的一部分,提供了强大的MVC功能,可以轻松集成Spring生态中的其他组件。 - **MyBatis**: 一个优秀的持久层框架,提供了基于SQL的查询...
原因是JavaWeb项目不是通过本地的JRE运行的,而是部署到Web服务器(如Tomcat、Jetty)上。这些服务器都实现了自身的类加载器。以Tomcat为例,ClassLoader的加载顺序如下: 1. common.CommonClassLoader 2. server....
- `java.lang.Thread`、`java.lang.Number`和`java.lang.ClassLoader`可以被继承,因为它们不是final类。 - `java.lang.Double`、`java.lang.Math`、`java.lang.Void`和`java.lang.Class`不可被继承,因为它们是...
SpringBoot的核心特性包括自动配置、内嵌Web服务器(如Tomcat或Jetty)以及可执行JAR包的支持。通过自动配置,开发者可以快速搭建项目结构,而无需繁琐的XML配置。 在描述中提到的文章链接已给出,但在这个平台无法...
ClassLoader Viewer portlet 263 JNDI Viewer portlet 265 Dependency Viewer portlet 267 Web Server administration 268 HTTP connectors 269 HTTPS connectors 271 AJP connectors 273 Web Server Logs ...
* Client CLAP connector to access to the Classloader resources. * Client RIAP connector to access to the Restlet internal resources, directly inside the JVM. Available Representations * Built-in ...
2. **boot** 目录:包含用于启动Maven内嵌的Jetty服务器的类加载器(ClassLoader)。 3. **conf** 目录:存储Maven的配置文件,比如`settings.xml`,它定义了Maven的全局配置,如本地仓库位置、远程仓库和代理设置...
Spring Boot基于约定优于配置的原则,通过内嵌的Servlet容器(如Tomcat或Jetty),我们可以快速地启动一个Web服务。在Spring Initializr中,我们可以选择web依赖来创建一个新的Spring Boot项目。 创建项目后,我们...
例如,`java.lang.Math`、`java.lang.Void`和`java.lang.Class`不能被继承,而`java.lang.Thread`和`java.lang.ClassLoader`可以。 2. **抽象类与接口的区别**: - 抽象类可以包含具体实现方法,而接口只能声明...