`
wangyanlong0107
  • 浏览: 495105 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

【转】Java ClassLoader深进研究

 
阅读更多

参考文章:
http://blog.csdn.net/lovingprince/archive/2009/06/03/4238695.aspx 

http://www.yesky.com/243/1840743.shtml 
http://blog.csdn.net/gtuu0123/article/details/4493757


Java为了提供平台无关性,在操纵系统之上加进了一层JVM来隔离操纵系统特定实现,使所编写的java代码在任何平台都能运行,但是JVM是特定于某一操纵系统的





一、当JVM启动时,由三个类加载器对类进行加载:
1.bootstrap classloader 
2.extension classloader
3.system classloader 



(1)bootstrap classloader[ 引导类加载器] 是由JVM实现的,不是java.lang.ClassLoader的子类 ,它负责加载Java的核心类,其加载的类由 sun.boot.class.path指定,或者在 执行java命令时使用-Xbootclasspath选项或使用-D选项指定sun.boot.class.path系统属性值 



说明: 

-Dproperty_name=property_value  指定属性的值; 

-Xbootclasspath   改变虚拟机装载缺省系统运行包rt.jar,而从-Xbootclasspath中设定的搜索路径中装载系统运行类 



输出加载的核心类库: 

view plaincopy to clipboardprint?
01.public class TestBootstrapClassLoader {  
02.    public static void main(String[] args) {  
03.        URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();  
04.        for (int i = 0; i < urls.length; i++) {  
05.          System.out.println(urls[i].toExternalForm());  
06.        }  
07.    }  
08.}  
public class TestBootstrapClassLoader {
public static void main(String[] args) {
URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
  System.out.println(urls[i].toExternalForm());
}
}


输出结果: 

file:/E:/MyEclipse%205.1.1%20GA/jre/lib/rt.jar
file:/E:/MyEclipse%205.1.1%20GA/jre/lib/i18n.jar
file:/E:/MyEclipse%205.1.1%20GA/jre/lib/sunrsasign.jar
file:/E:/MyEclipse%205.1.1%20GA/jre/lib/jsse.jar
file:/E:/MyEclipse%205.1.1%20GA/jre/lib/jce.jar
file:/E:/MyEclipse%205.1.1%20GA/jre/lib/charsets.jar
file:/E:/MyEclipse%205.1.1%20GA/jre/classes 



(2) extension classloader[扩展类加载器] ,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的类包。这为引进除Java核心类以外的新功能提供了一个标准机制。在这个实例上调用方法getParent()总是返回空值null,由于引导加载器bootstrap classloader不是一个真正的ClassLoader实例。 

view plaincopy to clipboardprint?
01.class TestExtensionClassLoader {  
02.    public static void main(String[] args) {  
03.        System.out.println(System.getProperty("java.ext.dirs"));  
04.        ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();  
05.        System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());  
06.    }  
07.}  
class TestExtensionClassLoader {
public static void main(String[] args) {
System.out.println(System.getProperty("java.ext.dirs"));
ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());
}


输出结果: 

E:/MyEclipse 5.1.1 GA/jre/lib/ext
the parent of extension classloader : null 




以上这段代码也表明了下列父子关系或加载顺序: 

bootstrap classloader(因其不是ClassLoader的子类,null)-->extension classloader-->system classloader 

这也表明了jvm在加载类的顺序,当加载一个类时(假设其未加载),先找到最顶层的classloader,假如其可以加载这个类(或者已经加载了这个类),则返回这个类;假如其不能加载类(换个说法:在相应路径中搜索不到相应类),则用其子classloader加载,直到这个类被加载或者抛出相应的异常 

这个顺序保证了越重要的类,越先加载;由于一个类只被加载一次(cache),所以如java.lang.System这个类,不能被用户替换(由于是按照bootstrap-->extension-->system的顺序,当要加载java.lang.System类时,其首先从bootstrap的搜索路径中找类)





(3)system/application classloader [系统(应用)类加载器] ,加载来自-classpath或者java.class.path系统属性或者CLASSPATH操纵系统属性所指定的JAR类包和类路径。 

a.可以通过静态方法ClassLoader.getSystemClassLoader()找到该类加载器。 

b.假如没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。可以从源代码看出这一点: 

view plaincopy to clipboardprint?
01.protected ClassLoader() {  
02.    SecurityManager security = System.getSecurityManager();  
03.    if (security != null) {  
04.        security.checkCreateClassLoader();  
05.    }  
06.    this.parent = getSystemClassLoader();  
07.    initialized = true;  
08.    }  
protected ClassLoader() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
    security.checkCreateClassLoader();
}
this.parent = getSystemClassLoader();
initialized = true;
    }


classpath路径:


view plaincopy to clipboardprint?
01.class TestSystemClassLoader {  
02.    public static void main(String[] args) {  
03.        System.out.println(System.getProperty("java.class.path"));  
04.    }  
05.}  
class TestSystemClassLoader {
public static void main(String[] args) {
System.out.println(System.getProperty("java.class.path"));
}

结果为: 

E:/dev/java/eclipse/TestClassLoader/classes;E:/dev/java/lib/commons-logging-1.1.jar 



重要: 

classloader加载类用的是全盘负责委托机制。 

a.全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依靠的和引用的所有Class也由这个classloader负责载进,除非是显式的使用另外一个classloader载进 ; 

b.委托机制则是先让parent类加载器 (而不是super,它与parent classloader类不是继续关系)寻找,只有在parent找不到的时候才从自己的类路径中往寻找。此外类加载还采用了cache机制,也就是假如 cache中保存了这个Class就直接返回它,假如没有才从文件中读取和转换成Class,并存进cache,这就是为什么我们修改了Class但是必须重新启动JVM才能生效的原因。 

二、ClassLoader加载Class的过程 
1.检测此Class是否载进过(即在cache中是否有此Class),假如有到8,假如没有到2
2.假如parent classloader不存在(没有parent,那parent一定是bootstrap classloader了),到4
3.请求parent classloader载进,假如成功到8,不成功到5
4.请求jvm从bootstrap classloader中载进,假如成功到8
5.寻找Class文件(从与此classloader相关的类路径中寻找)。假如找不到则到7.
6.从文件中载进Class,到8.
7.抛出ClassNotFoundException.
8.返回Class. 


源代码如下: 

view plaincopy to clipboardprint?
01.ClassLoader类中  
02.protected synchronized Class<?> loadClass(String name, boolean resolve)  
03.    throws ClassNotFoundException  
04.    {  
05.    // First, check if the class has already been loaded   
06.    Class c = findLoadedClass(name);  
07.    if (c == null) {  
08.        try {  
09.        if (parent != null) {  
10.            c = parent.loadClass(name, false);  
11.        } else {  
12.            c = findBootstrapClass0(name);  
13.        }  
14.        } catch (ClassNotFoundException e) {  
15.            // If still not found, then invoke findClass in order   
16.            // to find the class.   
17.            c = findClass(name);  
18.        }  
19.    }  
20.    if (resolve) {  
21.        resolveClass(c);  
22.    }  
23.    return c;  
24.    }  
ClassLoader类中
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
    {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
    try {
if (parent != null) {
    c = parent.loadClass(name, false);
} else {
    c = findBootstrapClass0(name);
}
    } catch (ClassNotFoundException e) {
        // If still not found, then invoke findClass in order
        // to find the class.
        c = findClass(name);
    }
}
if (resolve) {
    resolveClass(c);
}
return c;
    } 

可以看到当在parent链classloader中和bootstrap classloader中都找不到相应的类时,会调用findClass方法,因此ClassLoader的子类可以重写这个方法,定义自己的找到类的方法 







以下是一些类的ClassLoader示例: 

view plaincopy to clipboardprint?
01.class TestClassLoader {  
02.    public static void main(String[] args) {  
03.        System.out.println(System.class.getClassLoader());  
04.        System.out.println(javax.swing.JButton.class.getClassLoader());  
05.        System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());  
06.        System.out.println(org.apache.commons.logging.Log.class.getClassLoader());  
07.    }  
08.}  
class TestClassLoader {
public static void main(String[] args) {
System.out.println(System.class.getClassLoader());
System.out.println(javax.swing.JButton.class.getClassLoader());
System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());
System.out.println(org.apache.commons.logging.Log.class.getClassLoader());
}


输出: 

null        其路径在sun.boot.class.path中,由bootstrap classloader加载,所以返回为null(因其不是ClassLoader的子类) 

null        其路径在sun.boot.class.path中,由bootstrap classloader加载 
sun.misc.Launcher$ExtClassLoader@7259da      路径在java.ext.dirs中,由extension classloader加载
sun.misc.Launcher$AppClassLoader@197d257   由classpath指定,由system classloader加载 



查看sun.misc.Launcher的源代码 

view plaincopy to clipboardprint?
01.public Launcher() {  
02.    // Create the extension class loader   
03.    ClassLoader extcl;  
04.    try {  
05.        extcl = ExtClassLoader.getExtClassLoader();  
06.    } catch (IOException e) {  
07.        throw new InternalError(  
08.        "Could not create extension class loader");  
09.    }  
10.    // Now create the class loader to use to launch the application   
11.    try {  
12.        loader = AppClassLoader.getAppClassLoader(extcl);  
13.    } catch (IOException e) {  
14.        throw new InternalError(  
15.        "Could not create application class loader");  
16.    }  
17.    // Also set the context class loader for the primordial thread.   
18.    Thread.currentThread().setContextClassLoader(loader);  
19.        // 其他   
20.}  
public Launcher() {
// Create the extension class loader
ClassLoader extcl;
try {
    extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
    throw new InternalError(
"Could not create extension class loader");
}
// Now create the class loader to use to launch the application
try {
    loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
    throw new InternalError(
"Could not create application class loader");
}
// Also set the context class loader for the primordial thread.
Thread.currentThread().setContextClassLoader(loader);
        // 其他


可以看到是由Launcher这个类初始化ExtClassLoader和AppClassLoader类的 

ExtClassLoader无parent,而AppClassLoader的parent为 ExtClassLoader 



Launcher的getClassLoader()方法


view plaincopy to clipboardprint?
01.public ClassLoader getClassLoader() {  
02.    return loader;  
03.    }  
04.只返回AppClassLoader  
public ClassLoader getClassLoader() {
return loader;
    }
只返回AppClassLoader 



ExtClassLoader 

view plaincopy to clipboardprint?
01.static class ExtClassLoader extends URLClassLoader {  
02.    private File[] dirs;  
03.    /** 
04.     * create an ExtClassLoader. The ExtClassLoader is created 
05.     * within a context that limits which files it can read 
06.     */  
07.    public static ExtClassLoader getExtClassLoader() throws IOException  
08.    {  
09.        final File[] dirs = getExtDirs();  
10.        try {  
11.        // Prior implementations of this doPrivileged() block supplied    
12.        // aa synthesized ACC via a call to the private method   
13.        // ExtClassLoader.getContext().   
14.        return (ExtClassLoader) AccessController.doPrivileged(  
15.             new PrivilegedExceptionAction() {  
16.            public Object run() throws IOException {  
17.                            int len = dirs.length;  
18.                            for (int i = 0; i < len; i++) {  
19.                                MetaIndex.registerDirectory(dirs[i]);  
20.                            }  
21.                            return new ExtClassLoader(dirs);  
22.            }  
23.            });  
24.        } catch (java.security.PrivilegedActionException e) {  
25.            throw (IOException) e.getException();  
26.        }  
27.    }  
28.......  
29.private static File[] getExtDirs() {  
30.        String s = System.getProperty("java.ext.dirs");  
31.        File[] dirs;  
32.        if (s != null) {  
33.        StringTokenizer st =   
34.            new StringTokenizer(s, File.pathSeparator);  
35.        int count = st.countTokens();  
36.        dirs = new File[count];  
37.        for (int i = 0; i < count; i++) {  
38.            dirs[i] = new File(st.nextToken());  
39.        }  
40.        } else {  
41.        dirs = new File[0];  
42.        }  
43.        return dirs;  
44.    }  
static class ExtClassLoader extends URLClassLoader {
private File[] dirs;
/**
* create an ExtClassLoader. The ExtClassLoader is created
* within a context that limits which files it can read
*/
public static ExtClassLoader getExtClassLoader() throws IOException
{
    final File[] dirs = getExtDirs();
    try {
// Prior implementations of this doPrivileged() block supplied 
// aa synthesized ACC via a call to the private method
// ExtClassLoader.getContext().
return (ExtClassLoader) AccessController.doPrivileged(
     new PrivilegedExceptionAction() {
public Object run() throws IOException {
                            int len = dirs.length;
                            for (int i = 0; i < len; i++) {
                                MetaIndex.registerDirectory(dirs[i]);
                            }
                            return new ExtClassLoader(dirs);
}
    });
    } catch (java.security.PrivilegedActionException e) {
    throw (IOException) e.getException();
    }
}
......
private static File[] getExtDirs() {
    String s = System.getProperty("java.ext.dirs");
    File[] dirs;
    if (s != null) {
StringTokenizer st = 
    new StringTokenizer(s, File.pathSeparator);
int count = st.countTokens();
dirs = new File[count];
for (int i = 0; i < count; i++) {
    dirs[i] = new File(st.nextToken());
}
    } else {
dirs = new File[0];
    }
    return dirs;




AppClassLoader 

view plaincopy to clipboardprint?
01.static class AppClassLoader extends URLClassLoader {  
02.    public static ClassLoader getAppClassLoader(final ClassLoader extcl)  
03.        throws IOException  
04.    {  
05.        final String s = System.getProperty("java.class.path");  
06.        final File[] path = (s == null) ? new File[0] : getClassPath(s);  
07.        // Note: on bugid 4256530   
08.        // Prior implementations of this doPrivileged() block supplied    
09.        // a rather restrictive ACC via a call to the private method   
10.        // AppClassLoader.getContext(). This proved overly restrictive   
11.        // when loading  classes. Specifically it prevent   
12.        // accessClassInPackage.sun.* grants from being honored.   
13.        //   
14.        return (AppClassLoader)   
15.        AccessController.doPrivileged(new PrivilegedAction() {  
16.        public Object run() {  
17.            URL[] urls =  
18.            (s == null) ? new URL[0] : pathToURLs(path);  
19.            return new AppClassLoader(urls, extcl);  
20.        }  
21.        });  
22.    }  
23.......  
static class AppClassLoader extends URLClassLoader {
public static ClassLoader getAppClassLoader(final ClassLoader extcl)
    throws IOException
{
    final String s = System.getProperty("java.class.path");
    final File[] path = (s == null) ? new File[0] : getClassPath(s);
    // Note: on bugid 4256530
    // Prior implementations of this doPrivileged() block supplied 
    // a rather restrictive ACC via a call to the private method
    // AppClassLoader.getContext(). This proved overly restrictive
    // when loading  classes. Specifically it prevent
    // accessClassInPackage.sun.* grants from being honored.
    //
    return (AppClassLoader) 
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
    URL[] urls =
(s == null) ? new URL[0] : pathToURLs(path);
    return new AppClassLoader(urls, extcl);
}
    });
}
......




Launcher类的getBootstrapClassPath()方法 

view plaincopy to clipboardprint?
01.public static URLClassPath getBootstrapClassPath() {  
02.    String prop = (String)AccessController.doPrivileged(new GetPropertyAction("sun.boot.class.path"));  
03.    URL[] urls;  
04.    if (prop != null) {  
05.        final String path = prop;  
06.        urls = (URL[])AccessController.doPrivileged(  
07.        new PrivilegedAction() {  
08.            public Object run() {  
09.                        File[] classPath = getClassPath(path);  
10.                        int len = classPath.length;  
11.                        Set seenDirs = new HashSet();  
12.                        for (int i = 0; i < len; i++) {  
13.                            File curEntry = classPath[i];  
14.                            // Negative test used to properly handle   
15.                            // nonexistent jars on boot class path   
16.                            if (!curEntry.isDirectory()) {  
17.                                curEntry = curEntry.getParentFile();  
18.                            }  
19.                            if (curEntry != null && seenDirs.add(curEntry)) {  
20.                                MetaIndex.registerDirectory(curEntry);  
21.                            }  
22.                        }  
23.                        return pathToURLs(classPath);  
24.            }  
25.        }  
26.        );  
27.    } else {  
28.        urls = new URL[0];  
29.    }  
30.    return new URLClassPath(urls, factory);  
31.    }  
public static URLClassPath getBootstrapClassPath() {
String prop = (String)AccessController.doPrivileged(new GetPropertyAction("sun.boot.class.path"));
URL[] urls;
if (prop != null) {
    final String path = prop;
    urls = (URL[])AccessController.doPrivileged(
new PrivilegedAction() {
    public Object run() {
                        File[] classPath = getClassPath(path);
                        int len = classPath.length;
                        Set seenDirs = new HashSet();
                        for (int i = 0; i < len; i++) {
                            File curEntry = classPath[i];
                            // Negative test used to properly handle
                            // nonexistent jars on boot class path
                            if (!curEntry.isDirectory()) {
                                curEntry = curEntry.getParentFile();
                            }
                            if (curEntry != null && seenDirs.add(curEntry)) {
                                MetaIndex.registerDirectory(curEntry);
                            }
                        }
                        return pathToURLs(classPath);
    }
}
    );
} else {
    urls = new URL[0];
}
return new URLClassPath(urls, factory);
    } 

从以上代码中可以看到bootstrap classloader使用sun.boot.class.path来加载类,extension classloader使用java.ext.dirs来加载类,而system classloader使用java.class.path来加载类 



运行下列程序: 

view plaincopy to clipboardprint?
01.class Test1 {  
02.    public static void main(String[] args) {  
03.        System.out.println(sun.misc.Launcher.getLauncher().getClass().getClassLoader());  
04.    }  
05.}  
class Test1 {
public static void main(String[] args) {
System.out.println(sun.misc.Launcher.getLauncher().getClass().getClassLoader());
}


结果为: 

null 

表明Launcher是由bootstrap classloader来加载的 



三、关于Context ClassLoader 
在上面Launcher的构造函数中有这么一句: Thread.currentThread().setContextClassLoader(loader); 这句是设置当前线程的classloader,默认是使用的AppClassLoader 

这个有什么作用呢? 

当线程需要用到某个类,contextClassLoader被请求来载进该类 

留意: 

(1)Class.forName(String name)载进的是在系统中已经加载进sun.boot.class.path、 java.ext.dirs、 java.class.path路径中的类,而在这几个路径中未加进的类不能载进(报异常) 

(2)Class.forName(String name, boolean initialize, ClassLoader loader)可以载进上述三个路径中没有的类,只要指定你的classloader即可


(3)利用ClassLoader可以载进在上述三个路径中没有的类 

示例: 

view plaincopy to clipboardprint?
01.自定义ClassLoader,从c根目录中读class  
02.package Test;  
03.public class TestClassLoader extends ClassLoader {  
04.        protected Map<String, Class> cache = new HashMap<String, Class>();  
05.      
06.    public TestClassLoader() {  
07.        super();  
08.    }  
09.      
10.    protected Class<?> findClass(String name) throws ClassNotFoundException {  
11.                if (cache.get(name) != null) {  
12.            return cache.get(name);  
13.        }  
14.        try {  
15.            String tname = name.replace('.', '/');  
16.            File file = new File("c://" + tname + ".class");  
17.            FileInputStream in = new FileInputStream(file);  
18.            BufferedInputStream bufIn = new BufferedInputStream(in);  
19.            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();  
20.            BufferedOutputStream bufOut = new BufferedOutputStream(byteOut);  
21.            byte[] buffer = new byte[4096];  
22.            int len = -1;  
23.            while((len = bufIn.read(buffer)) != -1) {  
24.                bufOut.write(buffer, 0, len);  
25.            }  
26.            bufOut.flush();  
27.            byteOut.flush();  
28.            byte[] data = byteOut.toByteArray();  
29.            Class cls = defineClass(name, data, 0, data.length);  
30.            cache.put(name, cls);  
31.            return cls;     } catch (IOException e) {  
32.            e.printStackTrace();  
33.        }  
34.        return null;  
35.    }  
36.}  
37.class TestLoader {  
38.    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {  
39.        ClassLoader cl = Thread.currentThread().getContextClassLoader();  
40.        System.out.println(Thread.currentThread().toString() + "  " + cl.toString());  
41.        new Thread() {  
42.            public void run() {  
43.                ClassLoader cl = Thread.currentThread().getContextClassLoader();  
44.                System.out.println(Thread.currentThread().toString() + "  " + cl.toString());  
45.                  
46.                TestClassLoader loader = new TestClassLoader();  
47.                Thread.currentThread().setContextClassLoader(loader);  
48.                new Thread() {  
49.                    public void run() {  
50.                        try {  
51.                            ClassLoader cl = Thread.currentThread().getContextClassLoader();  
52.                            System.out.println(Thread.currentThread().toString() + "  " + cl.toString());  
53.    //ClassLoader                          
54.          AbstractTestA test = (AbstractTestA) cl.loadClass("Test.TestAImp1").newInstance();  
55.        //Class.forName   
56.    //AbstractTestA test = (AbstractTestA) Class.forName("Test.TestAImp1", true, cl).newInstance();                     test.test();   
57.                        } catch (InstantiationException e) {  
58.                            // TODO Auto-generated catch block   
59.                            e.printStackTrace();  
60.                        } catch (IllegalAccessException e) {  
61.                            // TODO Auto-generated catch block   
62.                            e.printStackTrace();  
63.                        } catch (ClassNotFoundException e) {  
64.                            // TODO Auto-generated catch block   
65.                            e.printStackTrace();  
66.                        }  
67.                    }  
68.                }.start();  
69.            }  
70.        }.start();  
71.    }  
72.}  
73.//抽象类   
74.package Test;  
75.public abstract class AbstractTestA {  
76.    public String a;  
77.    public void print() {  
78.        System.out.println("a=" + a);  
79.    }  
80.      
81.    abstract public void test();  
82.}  
83.//编译后放进c根目录   
84.package Test;  
85.public class TestAImp1 extends AbstractTestA {  
86.    public void test() {  
87.        System.out.println(Thread.currentThread().toString());  
88.        System.out.println("Test");  
89.    }  
90.}  
自定义ClassLoader,从c根目录中读class
package Test;
public class TestClassLoader extends ClassLoader {
        protected Map<String, Class> cache = new HashMap<String, Class>();

public TestClassLoader() {
super();
}

protected Class<?> findClass(String name) throws ClassNotFoundException {
                if (cache.get(name) != null) {
return cache.get(name);
}
try {
String tname = name.replace('.', '/');
File file = new File("c://" + tname + ".class");
FileInputStream in = new FileInputStream(file);
BufferedInputStream bufIn = new BufferedInputStream(in);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
BufferedOutputStream bufOut = new BufferedOutputStream(byteOut);
byte[] buffer = new byte[4096];
int len = -1;
while((len = bufIn.read(buffer)) != -1) {
bufOut.write(buffer, 0, len);
}
bufOut.flush();
byteOut.flush();
byte[] data = byteOut.toByteArray();
Class cls = defineClass(name, data, 0, data.length);
cache.put(name, cls);
return cls; } catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
class TestLoader {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
System.out.println(Thread.currentThread().toString() + "  " + cl.toString());
new Thread() {
public void run() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
System.out.println(Thread.currentThread().toString() + "  " + cl.toString());

TestClassLoader loader = new TestClassLoader();
Thread.currentThread().setContextClassLoader(loader);
new Thread() {
public void run() {
try {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
System.out.println(Thread.currentThread().toString() + "  " + cl.toString());
//ClassLoader
          AbstractTestA test = (AbstractTestA) cl.loadClass("Test.TestAImp1").newInstance();
        //Class.forName
//AbstractTestA test = (AbstractTestA) Class.forName("Test.TestAImp1", true, cl).newInstance(); test.test();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
}.start();
}
}
//抽象类
package Test;
public abstract class AbstractTestA {
public String a;
public void print() {
System.out.println("a=" + a);
}

abstract public void test();
}
//编译后放进c根目录
package Test;
public class TestAImp1 extends AbstractTestA {
public void test() {
System.out.println(Thread.currentThread().toString());
System.out.println("Test");
}


运行结果为: 

Thread[main,5,main]  sun.misc.Launcher$AppClassLoader@197d257
Thread[Thread-0,5,main]  sun.misc.Launcher$AppClassLoader@197d257
Thread[Thread-1,5,main]  Test.TestClassLoader@1004901
Thread[Thread-1,5,main]
Test 

这也表明了线程间ContextClassLoader的继续性: 

(1)main线程默认的 ContextClassLoader为AppClassLoader 

(2)新启动线程从原线程处继续 ContextClassLoader 



四、关于ClassLoader和Package 
其中:String(int offset, int len, char[] arr)为包访问权限 

view plaincopy to clipboardprint?
01.package java.lang;  
02.public class TestPackage {  
03.    public static void main(String[] args) {  
04.        char[] c = "1234567890".toCharArray();  
05.        String s = new String(0, 10, c);  
06.    }  
07.}  
package java.lang;
public class TestPackage {
public static void main(String[] args) {
char[] c = "1234567890".toCharArray();
String s = new String(0, 10, c);
}


此代码可以编译通过,但是运行时出现下列错误:

java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$100(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)



这表明:

Java语言规定,在同一个包中的class,假如没有修饰符,默以为Package权限,包内的class都可以访问。但是这还不够正确。确切的说,只有由同一个ClassLoader装载的class才具有以上的Package权限。比如Bootstrap classloader装载了java.lang.String,AppClassLoader装载了我们自己写的java.lang.TestPackage,它们不能互相访问对方具有Package权限的方法。这样就阻止了恶意代码访问核心类的Package权限方法。 



五、关于两个ClassLoader载进同一个类 
留意:由两个不同的ClassLoader载进的同一个类,其是不同类型的,因此假如进行赋值会报ClassCastException 

示例: 

view plaincopy to clipboardprint?
01.package Test;  
02.import java.io.*;  
03.import java.util.*;  
04.public class TestClassLoader extends ClassLoader {  
05.      
06.    protected Map<String, Class> cache = new HashMap<String, Class>();  
07.      
08.    public TestClassLoader() {  
09.        super();  
10.    }  
11.      
12.    protected Class<?> findClass(String name) throws ClassNotFoundException {  
13.        if (cache.get(name) != null) {  
14.            return cache.get(name);  
15.        }  
16.        try {  
17.            String tname = name.replace('.', '/');  
18.            File file = new File("c://" + tname + ".class");  
19.            FileInputStream in = new FileInputStream(file);  
20.            BufferedInputStream bufIn = new BufferedInputStream(in);  
21.            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();  
22.            BufferedOutputStream bufOut = new BufferedOutputStream(byteOut);  
23.            byte[] buffer = new byte[4096];  
24.            int len = -1;  
25.            while((len = bufIn.read(buffer)) != -1) {  
26.                bufOut.write(buffer, 0, len);  
27.            }  
28.            bufOut.flush();  
29.            byteOut.flush();  
30.            byte[] data = byteOut.toByteArray();  
31.            Class cls = defineClass(name, data, 0, data.length);  
32.            cache.put(name, cls);  
33.            return cls;  
34.        } catch (IOException e) {  
35.            e.printStackTrace();  
36.        }  
37.        return null;  
38.    }  
39.}  
40.class TestLoaderA {  
41.    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {  
42.        TestClassLoader loader = new TestClassLoader();  
43.        System.out.println(TestAImp1.class.getClassLoader());  
44.        System.out.println(loader.findClass("Test.TestAImp1").getClassLoader());  
45.        TestAImp1 test = (TestAImp1)loader.findClass("Test.TestAImp1").newInstance();  
46.        test.test();  
47.          
48.    }  
49.}  
50.public class TestAImp1 extends AbstractTestA {  
51.    public void test() {  
52.        System.out.println(Thread.currentThread().toString());  
53.        System.out.println("Test");  
54.    }  
55.}  
package Test;
import java.io.*;
import java.util.*;
public class TestClassLoader extends ClassLoader {

protected Map<String, Class> cache = new HashMap<String, Class>();

public TestClassLoader() {
super();
}

protected Class<?> findClass(String name) throws ClassNotFoundException {
if (cache.get(name) != null) {
return cache.get(name);
}
try {
String tname = name.replace('.', '/');
File file = new File("c://" + tname + ".class");
FileInputStream in = new FileInputStream(file);
BufferedInputStream bufIn = new BufferedInputStream(in);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
BufferedOutputStream bufOut = new BufferedOutputStream(byteOut);
byte[] buffer = new byte[4096];
int len = -1;
while((len = bufIn.read(buffer)) != -1) {
bufOut.write(buffer, 0, len);
}
bufOut.flush();
byteOut.flush();
byte[] data = byteOut.toByteArray();
Class cls = defineClass(name, data, 0, data.length);
cache.put(name, cls);
return cls;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
class TestLoaderA {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
TestClassLoader loader = new TestClassLoader();
System.out.println(TestAImp1.class.getClassLoader());
System.out.println(loader.findClass("Test.TestAImp1").getClassLoader());
TestAImp1 test = (TestAImp1)loader.findClass("Test.TestAImp1").newInstance();
test.test();

}
}
public class TestAImp1 extends AbstractTestA {
public void test() {
System.out.println(Thread.currentThread().toString());
System.out.println("Test");
}


运行以上代码结果:

sun.misc.Launcher$AppClassLoader@197d257 
Test.TestClassLoader@1b90b39 
Exception in thread "main" java.lang.ClassCastException: Test.TestAImp1
at Test.TestLoaderA.main(TestClassLoader.java:84)

分享到:
评论

相关推荐

    Java类热替换(热部署)源码

    在Java开发过程中,为了提高开发效率和调试便捷性,我们经常需要实现类的热替换...这个压缩包中的源码可能就是展示如何实现这个功能的一个实例,你可以仔细研究这些代码,加深对Java类加载机制和热替换技术的理解。

    java深度历险

    Java的类加载机制分为三个主要的加载器:启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和应用类加载器(Application ClassLoader)。了解类加载器的工作原理有助于理解和解决类...

    java开发学习路线

    学习Java是一个循序渐进的过程。通过系统地掌握上述每个阶段的知识点,可以逐步建立起扎实的Java基础。同时,持续实践和探索新的技术也是提高编程技能不可或缺的一部分。希望这份学习路线能帮助你在Java开发之路上...

    JAVA工程师实习一周总结【推荐下载】.doc

    这一周的实习让我对JAVA开发有了更深入的理解,特别是对Java虚拟机(JVM)和类加载器(ClassLoader)机制有了初步的认识。 Java虚拟机是Java语言运行的基础,它负责管理程序的内存空间,包括堆内存、栈内存、方法区...

    深入java虚拟机第二版

    《深入Java虚拟机第二版》是一本专注于Java技术体系核心的权威著作,它详细解析了Java虚拟机(JVM)的工作原理,为开发者提供了深入理解Java程序运行机制的关键知识。这本书不仅包含了源码分析,还特别优化了PDF...

    Java虚拟机的系统优化研究.zip

    Java虚拟机(JVM)是Java程序运行的基础,它的系统优化对于提高应用程序的性能、减少资源消耗至关重要。本文将深入探讨Java虚拟机的系统优化,包括内存管理、垃圾收集、类加载机制、编译优化等多个方面,旨在帮助...

    Java核心API需要掌握的程度

    随着技术的深入,开发者还需要对Java的核心API有更深层次的理解: - **反射机制**:通过`Class`类学习如何动态获取类信息,并操作对象。 - **并发编程**:深入研究`Thread`类及其相关接口,如`Runnable`、`Callable...

    JAVA源码阅读的真实体会.pdf

    总的来说,Java源码阅读是一个渐进的过程,需要结合实际项目经验,逐步积累,才能真正从源码中获得深度理解和技能提升。在整个过程中,保持好奇心,坚持不懈,是成为一名出色程序员的必备品质。

    Java相关技术的源码学习心得

    1. **类加载机制**:Java的类加载器(ClassLoader)负责加载类文件到JVM中。理解双亲委派模型,如何加载类,以及如何自定义类加载器对于优化系统性能和实现特定功能至关重要。 2. **垃圾回收机制**:Java的内存管理...

    Java Source Learning

    在Java编程领域,深入理解源码是...通过深入研究源码,我们可以掌握更深层次的技术,更好地应对复杂的编程挑战。在实际开发中,结合阅读官方文档、参与开源项目,以及不断实践和反思,将是提升Java技术水平的有效途径。

    Java虚拟机介绍。不错的书啊

    类加载器分为两类:JVM自带的加载器(根类加载器、扩展类加载器、系统类加载器)和用户自定义的类加载器,后者是`java.lang.ClassLoader`的子类,允许用户定制类的加载方式。 JVM规范允许类加载器在预期类将被使用...

    java 反编译工具

    Java反编译工具是开发者和逆向工程人员用于查看Java字节码的工具,它们能够将已编译的.class文件转换回源代码形式。这些工具对于理解第三方库的工作原理、学习编程技巧、调试或者恢复丢失的源代码都极其有用。在Java...

    研究底层用src.zip

    4. **launcher**:这可能指的是Java应用程序的启动器,例如`java.lang.ClassLoader`和`java.lang.ProcessBuilder`等,它们负责加载类和执行程序。 5. **java**:这是Java平台的核心包,包含了基础类库,如集合框架...

    STRUTS2框架的getClassLoader漏洞利用1

    然而,随着Tomcat8.0的发布,这个问题变得更加严重,因为某些变化使得攻击者可以利用类加载器(ClassLoader)进行更深层次的攻击。 在Java中,每个类都有一个与之关联的ClassLoader,用于加载类的字节码。STRUTS2的...

    Java开发面试必备知识技能总结视频合集

    - **Netty线程模型**:深入研究Netty框架的线程模型,包括`NioEventLoopGroup`、`Selector`等组件的作用。 - **源码解析**:通过对Netty源码的详细解析,掌握其高效并发处理能力的关键技术点。 #### 7. 架构师不得...

    jvm解析编译过的class文件

    Java虚拟机(JVM)是Java程序运行的基础,它负责解析和执行编译后的.class文件。这个过程涉及多个阶段,包括加载、验证、准备、解析和初始化。在本篇文章中,我们将深入探讨JVM如何处理.class文件,以及相关工具如何...

    2022最全java面试题+心里辅导

    Java作为世界上最受欢迎的编程语言之一,其面试题的深度和广度都反映了开发者需要具备的扎实基础和技术深度。这份“2022最全Java面试题+心里辅导”旨在帮助求职者全面准备Java相关的面试,同时也为有经验的开发者...

    互联网大厂Java面试题合集

    ### 互联网大厂Java面试题合集解析 #### 京东篇 ##### 8.1.0 SQL注入检测方法与SQLMap工作原理 - **SQL注入检测**:SQL注入是一种常见的Web安全漏洞,攻击者可以通过篡改SQL语句来获取未经授权的数据访问。检测SQL...

    java_core:尝试看java源码-看

    4. **JVM内部机制**:虽然JVM是用C++编写的,但通过研究如ClassLoader、垃圾收集器等相关的Java代码,我们可以了解到类加载、内存管理、垃圾回收等JVM关键过程的工作原理。 5. **异常处理**:Java的异常处理机制是...

Global site tag (gtag.js) - Google Analytics