背景
项目上使用的Weblogic版本为12.1.1,我们用JAWR来减少压缩JS和CSS,JAWR默认使用JSMin来压缩JS,我尝试修改JAWR的JS
压缩器为压缩率更高的YUI Compressor,YUI依赖javascript解析器js.jar,由于Weblogic自身已经包含了js.jar中的class,所以我们将js.jar放在weblogic的domain lib下,并修改了weblogic的启动脚本使得启动的时候先加载项目用到的js.jar。
问题
修改JAWR的JS压缩器为YUI后,在Tomcat下测试通过,但在Weblogic下报如下的错误:
1 IlegeAccessException
2 Caused By: java.lang.IllegalAccessError: tried to access class org.mozilla.javascript.DefaultErrorReporter from class org.mozilla.javascript.CompilerEnvirons
3 at org.mozilla.javascript.CompilerEnvirons.
(CompilerEnvirons.java:48) 4 at
com.yahoo.platform.yuipressor.JavaScriptCompressor.parse(JavaScriptCompressor.java:310) 5 at
com.yahoo.platform.yuipressor.JavaScriptCompressor.(JavaScriptCompressor.java:533) 6 at
net.jawr.web.resource.bundle.postprocess.impl.yui.YUIJSCompressor.doPostProcessBundle(YUIJSCompressor.java:66) 7 at
net.jawr.web.resource.bundle.postprocess.AbstractChainedResourceBundlePostProcessor.postProcessBundle(AbstractChainedResourceBundlePostProcessor.java:71)
8 Truncated. see log file for complete stacktrace
初步怀疑是jar包冲突。可能是因为错误里涉及的2个类是由不同的类加载器加载引起的。
Java类加载器
解决问题前,先了解下Java类加载器的原理。
我们知道java中所有类都是通过类加载器ClassLoader来加载的,Java提供了四种类加载器:
1. BootStrap:在java虚拟机启动的时候会利用这个类加载器来加载
System.getProperty("sun.boot.class.path")所指定的路径或jar,一般为JDK安装目录下的 /JRE/LIB/rt.jar,这个类加载器不是类 ,只是作为一个java中类的起源工具。BootStrap是用C++语言写的。
2. ExpClassLoader:这个类加载器加载System.getProperty("java.ext.dirs")所指定的路径或jar,一般为JDK安装目录下的/JRE/LIB/ext 目录中的类 我们只要把我们的类打包成JAR包放在这里即可。
3. AppClassLoader:这个类加载器加载System.getProperty("java.class.path")所指定的路径或jar。我们在java程序中classpath对应的类都由这个AppClassLoader导入进来。
4. URLClassLoader用来加载网络上远程的类。
这4种ClassLoader的优先级依次从高到低。在java中,一个类通过认证的类全名来唯一标识。认证的类全名是由包名和类名两部分组成。但是在一个类被加载到JVM中则是通过认证的类全名,还有加载这个类的加载器来唯一标识。因此,一个类的类名为C1,包名为Pg,被类加载器类KClassLoader的一个实例k1加载,则C1,也就是C1.class ,的类实例,在JVM中将被解释为(C1,Pg,k1)。这就意味着两个不同的类加载器实
(Cl, Pg, kl1) 和 (Cl, Pg, kl2) ,加载的类在JVM中将有不同的类实例对象,不是类型可比型(type-compatible)的。
双亲委派模型
如果一个网络类装载器被请求装载一个java.lang.Integer,它会首先把请求发送给上一级的类路径装载器,如果返回已装载,则网络类装载器将不会装载这个java.lang.Integer,如果上一级的类路径装载器返回未装载,它才会装载java.lang.Integer。类似地,类路径装载器收到请求后(无论是直接请求装载还是下一级的ClassLoader上传的请求),它也会先把请求发送到上一级的标准扩展类装载器,这样一层一层上传,于是启动类装载器优先级最高,如果它按照自己的方式找到了java.lang.Integer,则下面的Cla