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页面做个测试:...

Tool.java(得到类加载器等信息,并输出到jsp页面)
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;

public class Tool
{
    
public static final String nextLine = "<br>";
    
public static final String prefix = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
    
    
/**
     * 输出系统属性
     
*/
    
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();
    }
}

 

MyServlet.java(获取Servlet类加载器信息)
public class MyServlet extends HttpServlet
{
    
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);
    }
}

 

index.jsp(获取jsp的类加载器,并将各信息显示到页面)
    <body>
        
<%
            
// 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 class HelloWorld
{
    
public void hello(String username)
    {
        System.out.println(
"Hello, " + username + "!!!");
    }
}

 

 

Tool.java(加、解密的工具类)
public class Tool
{
    
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;
    }
}

 

MyClassLoader.java(继承了ClassLoader类,加载经Tool.java加密后的class文件)
public class MyClassLoader extends ClassLoader
{
    
/**
     * 加密后的.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;
    }
}

 

Test.java(测试类,先将HelloWorld.class文件加密,然后让自定义的ClassLoader去加载它)
public class Test
{
    @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文件

 

MyURLClassLoader.java(继承自URLClassLoader,加载指定路径下的.class、.jar文件)
public class MyURLClassLoader extends URLClassLoader
{
    
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自己的路径)

class A
{
    
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

 

posted on 2010-09-20 20:44  TroyZ  阅读(694)  评论(0编辑  收藏  举报