ClassLoader学习笔记
bootstrap classloader (由JVM用C代码实现的加载器,加载jre/lib下的jar包)
|
extension classloader (sun.misc.Launcher$ExtClassLoader,系统属性java.ext.dirs指定,加载 jre/lib/ext目录下的jar包)
|
system classloader (sun.misc.Launcher$AppClassLoader,系统属性java.class.path指定,加载 项目 classes 目录、lib目录下的.jar包 )
1. 加载.class时,使用委托机制,即加载.class时,如果有父类加载器,则先在父类加载器管理的目录下查找.class,如果没有,则从子加载器的目录中加载。
用这种委托方式会比较安全,我们自己写一个java.lang.String,就不会被加载,因为它是被bootstrap加载器加载的。
2. 我们用tomcat的jsp页面做个测试:...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import java.net.URL;
import java.net.URLClassLoader;
public class Tool
{
public static final String nextLine = "<br>";
public static final String prefix = " ";
/**
* 输出系统属性
*/
public static void outputSysProp(Writer out)
{
try
{
out.write(nextLine);
out.write(nextLine);
out.write("<font color='red'>系统属性信息 start</font>" + nextLine);
String extDir = System.getProperty("java.ext.dirs");
String classPathDir = System.getProperty("java.class.path");
out.write(prefix + "1>. java.ext.dirs : " + extDir + nextLine);
out.write(prefix + "2>. java.class.path : " + classPathDir + nextLine);
out.write("<font color='red'>系统属性信息 end</font>" + nextLine);
out.flush();
}
catch(Exception e)
{
e.printStackTrace();
}
}
/**
* 输出boot strap加载器信息
*/
public static void outputBootStrap(Writer out)
{
try
{
out.write(nextLine);
out.write(nextLine);
out.write("<font color='red'>Boot Strap 加载器信息 start</font>" + nextLine);
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
out.write(urlToString(urls));
out.write("<font color='red'>Boot Strap 加载器信息 end</font>" + nextLine);
out.flush();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 输出loader的名称、加载的路径,即其父加载器的名称、加载路径
* @param out 输出
* @param loader 类加载器
*/
public static void output(Writer out, ClassLoader loader)
{
try
{
if(loader == null || out == null)
{
return;
}
int count = 0;
while(loader != null)
{
count++;
out.write(count + ". 加载器名称 : " + loader.getClass().getName() + nextLine);
if(loader instanceof URLClassLoader)
{
URLClassLoader _loader = (URLClassLoader)loader;
URL[] urls = _loader.getURLs();
out.write(urlToString(urls));
}
loader = loader.getParent();
}
out.flush();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 输出URL信息
*/
public static String urlToString(URL[] urls)
{
if(urls == null || urls.length <= 0)
return null;
StringBuffer buf = new StringBuffer(200);
for(int i = 0; i < urls.length; i++)
{
URL url = urls[i];
buf.append(prefix).append((i + 1) + ">. 加载路径 : ").append(url.getPath()).append(nextLine);
}
return buf.toString();
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
PrintWriter out = response.getWriter();
out.print("<br><br>");
out.print("<font color='red'>Servlet加载器信息 start</font><br>");
Tool.output(out, getClass().getClassLoader());
out.print("<font color='red'>Servlet加载器信息 end</font><br>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
doGet(request, response);
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%
// 0. 输出系统属性
Tool.outputSysProp(out);
// 1. 输出 boot strap 加载器信息
Tool.outputBootStrap(out);
// 2. 输出 servlet 加载器信息
%>
<jsp:include page="/MyServlet" />
<%
// 3. 输出 jsp 加载器信息
outputJSP(out);
%>
<%!
/**
* 输出jsp 加载器信息
*/
private void outputJSP(Writer out)
{
try
{
out.write("<br><br>");
out.write("<font color='red'>Jsp 加载器信息 start</font><br>");
Tool.output(out, getClass().getClassLoader());
out.write("<font color='red'>Jsp 加载器信息 end</font><br>");
out.flush();
}
catch(Exception e)
{
e.printStackTrace();
}
}
%>
</body>
页面输出结果:
系统属性信息 start
1>. java.ext.dirs : E:\Java\baseTool\jdk1.6.0_11\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
2>. java.class.path : E:\Java\javaDevelop\apache\tomcat-6.0.18/bin/bootstrap.jar;E:\Java\baseTool\jdk1.6.0_11/lib/tools.jar
系统属性信息 end
Boot Strap 加载器信息 start
1>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/lib/resources.jar
2>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/lib/rt.jar
3>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/lib/sunrsasign.jar
4>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/lib/jsse.jar
5>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/lib/jce.jar
6>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/lib/charsets.jar
7>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/classes
Boot Strap 加载器信息 end
Servlet加载器信息 start
1. 加载器名称 : org.apache.catalina.loader.WebappClassLoader
1>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/webapps/NamiWeb/WEB-INF/classes/
2>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/webapps/NamiWeb/WEB-INF/lib/jsf-api.jar
…
2. 加载器名称 : org.apache.catalina.loader.StandardClassLoader
1>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/lib/
2>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/lib/annotations-api.jar
…
3. 加载器名称 : sun.misc.Launcher$AppClassLoader
1>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/bin/bootstrap.jar
2>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/lib/tools.jar
4. 加载器名称 : sun.misc.Launcher$ExtClassLoader
1>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/lib/ext/dnsns.jar
…
Servlet加载器信息 end
Jsp 加载器信息 start
1. 加载器名称 : org.apache.jasper.servlet.JasperLoader
1>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/work/Catalina/localhost/NamiWeb/
2. 加载器名称 : org.apache.catalina.loader.WebappClassLoader
1>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/webapps/NamiWeb/WEB-INF/classes/
2>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/webapps/NamiWeb/WEB-INF/lib/jsf-api.jar
…
3. 加载器名称 : org.apache.catalina.loader.StandardClassLoader
1>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/lib/
2>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/lib/annotations-api.jar
…
4. 加载器名称 : sun.misc.Launcher$AppClassLoader
1>. 加载路径 : /E:/Java/javaDevelop/apache/tomcat-6.0.18/bin/bootstrap.jar
2>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/lib/tools.jar
5. 加载器名称 : sun.misc.Launcher$ExtClassLoader
1>. 加载路径 : /E:/Java/baseTool/jdk1.6.0_11/jre/lib/ext/dnsns.jar
…
Jsp 加载器信息 end
3. 可以看到,Tomcat有自己的类加载器,
(1)JasperLoader 负责加载:
${TOMCAT_HOME}/work/Catalina/localhost/${项目}/ 目录下编译后的jsp对应的.class文件
(2)WebappClassLoader 负责加载 :
${项目}/WEB-INF/classes/ 目录下的*.class 文件
${项目}/WEB-INF/lib/ 目录下的*.jar 文件
(3)StandardClassLoader 负责加载:
${TOMCAT_HOME}/lib/ 目录下的*.jar 文件
sun自己的加载器:
(4)AppClassLoader 负责加载:
系统属性 java.class.path 中的目录
(5)ExtClassLoader 负责加载:
系统属性 java.ext.dirs 中的目录
4. 通过ClassLoader,我们可以自定义自己的ClassLoader,用自己的方式加载.class文件,加载系统运行后才知道的某个目录下的jar或者.class文件。
(这样可以:在jar包中放加密后的.class文件,然后运行时,用自定义的ClassLoader中解密并加载到内存中)
5. 用ClassLoader加载处理后的.class文件
// 普通的java类,(注:也可以让java类实现一个接口,然后在自定义ClassLoader中实例化接口的实例,这样就可以不用反射来调用方法了)
// 加密类,将字节反转,所以加密、解密方法是同一个
// 自定义ClassLoader,将.class文件解密后并加载到内存
{
public void hello(String username)
{
System.out.println("Hello, " + username + "!!!");
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
private Tool(){};
/**
* 实现将一个文件的内容反转
*/
public static void reverseFile(String srcFile, String distFile)
{
try
{
File file = new File(srcFile);
FileInputStream fis = new FileInputStream(file);
byte[] b = new byte[fis.available()];
fis.read(b);
byte[] _b = reverse(b);
FileOutputStream fos = new FileOutputStream(distFile);
fos.write(_b);
fos.flush();
fos.close();
fis.close();
// 删除javac编译后的.class文件
file.delete();
}
catch (Exception e)
{
e.printStackTrace();
}
}
/**
* 将字节数组反转
*/
public static byte[] reverse(byte[] b)
{
byte[] _b = new byte[b.length];
for(int i = 0; i < b.length; i++)
{
_b[i] = b[b.length - i - 1];
}
return _b;
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
/**
* 加密后的.class文件存放的路径
*/
private String classFilePath;
public MyClassLoader(String classFilePath)
{
classFilePath = classFilePath.replace("\\", "/");
if(!classFilePath.endsWith("/"))
{
classFilePath = classFilePath + "/";
}
this.classFilePath = classFilePath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
try
{
// 1.找到加密后的.class文件
FileInputStream classFile = new FileInputStream(classFilePath + name.substring(name.lastIndexOf(".") + 1) + ".class");
byte[] b = new byte[classFile.available()];
classFile.read(b);
// 2.解密后的字节码
byte[] _b = Tool.reverse(b);
return defineClass(null, _b, 0, _b.length);
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception
{
// javac编译后的.class文件
String srcFile = "E:/Java/javaDevelop/eclipse3.3/workspace/NamiWeb/WebRoot/WEB-INF/classes/cn/nami/demo/HelloWorld.class";
// 加密后保存的位置
String distFile = "C:/HelloWorld.class";
// 1.加密
Tool.reverseFile(srcFile, distFile);
// 2.使用自定义文件加载加密后的.class文件
MyClassLoader loader = new MyClassLoader("C:/");
// 加载HelloWorld.class
Class clazz = loader.loadClass("cn.nami.demo.HelloWorld");
// 这里也可以让HelloWorld实现一个接口,然后就不需要用反射调用hello()方法了。
clazz.getMethod("hello", String.class).invoke(clazz.newInstance(), "nami");
}
}
6. 用URLClassLoader加载指定目录下的.jar、.class文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{
public MyURLClassLoader(URL[] urls)
{
// MyURLClassLoader 加载urls 目录下的.class、.jar文件
super(urls);
}
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception
{
String dir = "C:/";
File file = new File(dir);
URL[] urls = new URL[]{file.toURI().toURL()};
MyURLClassLoader loader = new MyURLClassLoader(urls);
// 加载HelloWorld.class
Class clazz = loader.loadClass("cn.nami.demo.HelloWorld");
// 调用hello()方法
clazz.getMethod("hello", String.class).invoke(clazz.newInstance(), "nami");
}
}
7. 当类A依赖类B时,如果类A是就类加载器C来加载的,则类B必定也是由类加载器C按照其搜索路径来加载的(先递归搜索类加载器C的parent路径,然后再搜索C自己的路径)
{
public void hello()
{
B b = new B();
}
}
如果A是由ExtClassLoader加载的,则B必定会由ExtClassLoader或者BootStrap来加载,而不会由AppClassLoader去加载。
8. 参考文章 : http://www.blogjava.net/lhulcn618/archive/2006/05/25/48230.html