警告(并不是错误):警告: Hessian/Burlap:'com.shine.exp.dto.ExpIdCardDTO' is an unknown class in WebappClassLoader
delegate: false
repositories:
/WEB-INF/classes/
----------> ParentClassloader:
java.net.FactoryURLClassLoader@68ad80cf
:
java.lang.ClassNotFoundException:com.shine.exp.dto.ExpIdCardDTO
场景重现:我的一个Service应用中将某个接口发布为Hessian服务,在这个接口中有一个参数Object dataObj,在另一个Web应用中去调用这个服务,但传入的dataObj是ExpIdCardDTO类型的,就会报如上警告。
public Long saveBusiDataWithCondition(String sessionId, int appId, String appWfCode, String uniqueId,
Object dataObj, boolean isSaveItems) throws ShineException;
原因:客户端在调用服务时会将参数自动序列化,服务提供方在反序列化时无法以最有效的方式反序列话,就会产生如上警告。但我们的程序还是行之有效的:
PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(dataObj);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
String proTypeName = propertyDescriptor.getPropertyType().getName();
//判断是否是基础类型
if(AttributeUtil.isBaseType(proTypeName)){
//To Do
}
}
注意Map对象是不可序列化的,因此dataObj只能对其子类,如 HashMap有特殊处理.
if (dataObj instanceof Map) {
Map<String, Object> mapData = (Map<String, Object>) dataObj;
//To Do
}
附上Tomcat的类装载原理:
- Tomcat的类载入器的结构
TomcatServer在启动的时候将构造一个ClassLoader树,以保证模块的类库是私有的
TomcatServer的ClassLoader结构如下:
+-----------------------------+
| Bootstrap |
| | |
| System |
| | |
| Common |
| / \ |
| Catalina Shared |
| / \ |
| WebApp1 WebApp2 |
+-----------------------------+
其中:
- Bootstrap -载入JVM自带的类和$JAVA_HOME/jre/lib/ext/*.jar
- System -载入$CLASSPATH/*.class
- Common -载入$CATALINA_HOME/common/...,它们对TOMCAT和所有的WEB APP都可见
- Catalina -载入$CATALINA_HOME/server/...,它们仅对TOMCAT可见,对所有的WEB APP都不可见
- Shared -载入$CATALINA_HOME/shared/...,它们仅对所有WEB APP可见,对TOMCAT不可见(也不必见)
- WebApp -载入ContextBase?/WEB-INF/...,它们仅对该WEB APP可见
2 - ClassLoader的工作原理
每个运行中的线程都有一个成员contextClassLoader,用来在运行时动态地载入其它类
系统默认的contextClassLoader是systemClassLoader,所以一般而言java程序在执行时可以使用JVM自带的类、$JAVA_HOME/jre/lib/ext/中的类和$CLASSPATH/中的类
可以使用Thread.currentThread().setContextClassLoader(...);更改当前线程的contextClassLoader,来改变其载入类的行为
ClassLoader被组织成树形,一般的工作原理是:
1)线程需要用到某个类,于是contextClassLoader被请求来载入该类
2)contextClassLoader请求它的父ClassLoader来完成该载入请求
3)如果父ClassLoader无法载入类,则contextClassLoader试图自己来载入
注意:WebApp?ClassLoader的工作原理和上述有少许不同:
它先试图自己载入类(在ContextBase?/WEB-INF/...中载入类),如果无法载入,再请求父ClassLoader完成
由此可得:
- 对于WEBAPP线程,它的contextClassLoader是WebApp?ClassLoader
- 对于TomcatServer线程,它的contextClassLoader是CatalinaClassLoader
3 类的查找
ClassLoader类中loadClass方法为缺省实现,用下面的顺序查找类:
1、调用findLoadedClass方法来检查是否已经被加载。如果没有则继续下面的步骤。
2、如果当前类装载器有一个指定的委托父装载器,则用委托父装载器的loadClass方法加载类,也就是委托给父装载器加载相应的类。
3、如果这个类装载器的委托层级体系没有一个类装载器加载该类,则使用类装载器定位类的特定实现机制,调用findClass方法来查找类。