ClassLoader原理

最近本来打算研究分布式的东西的,看了一位大侠写的分布式系统,其中要实现把class传到客户端,然后在客户端实例化的工作,就去研究了一下ClassLoader的原理。

准备工作:http://download.java.net/openjdk/jdk6/

压缩包中含有 Windows, Linux, Solaris 平台 JRE 源代码、JVM 源代码,以及 JDK 中类库和工具的源代码。

 

有了源代码就可以查看源码分析了。

JVM自带的类加载器可以分为三类:Boot Start ClassLoader,Ext ClassLoader, App ClassLoader

1. Boot Start ClassLoader其实不是一个类,其实就是直接由JVM自身来加载,因此当类如果是Boot Start ClassLoader加载的话,就会返回null。

public static void getBootStartClass() {
		URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();

		for (int i = 0; i < urls.length; i++) {
			System.out.println(urls[i]);
		}
		 
}

 从运行结果可以看到加载的都是Java/jdk1.6.0_23/jre/lib/下面的jar包

2.Boot Start ClassLoader会加载sun.misc.Launcher类。其中Lancherer调用ExtClassLoader.getExtClassLoader();生成ExtClassLoader。可见下面源代码

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);

        // Finally, install a security manager if requested
        String s = System.getProperty("java.security.manager");
        if (s != null) {
            SecurityManager sm = null;
            if ("".equals(s) || "default".equals(s)) {
                sm = new java.lang.SecurityManager();
            } else {
                try {
                    sm = (SecurityManager)loader.loadClass(s).newInstance();
                } catch (IllegalAccessException e) {
                } catch (InstantiationException e) {
                } catch (ClassNotFoundException e) {
                } catch (ClassCastException e) {
                }
            }
            if (sm != null) {
                System.setSecurityManager(sm);
            } else {
                throw new InternalError(
                    "Could not create SecurityManager: " + s);
            }
        }
    }

 接下来我们看一下ExtClassLoader 类的源代码,可以看到继承了URLClassLoader类,加载的路径是系统属性java.ext.dirs。打印出来为:jdk1.6.0_23\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext

 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();
            }
        }

        void addExtURL(URL url) {
                super.addURL(url);
        }

        /*
         * Creates a new ExtClassLoader for the specified directories.
         */
        public ExtClassLoader(File[] dirs) throws IOException {
            super(getExtURLs(dirs), null, factory);
            this.dirs = dirs;
        }

        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;
        }
}

 再看看APP ClassLoader,也继承了URLClassLoader。加载的路径是系统属性java.ext.dirs

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);
                }
            });
        }
}

 既然有那么多加载器,那么它们的加载顺序是怎么样的呢?从下面的代码可以看到当加载类的时候,会先看一下当前类是不是已经被加载了,如果加载了就返回。如果没,就调用父类的加载器来加载类。如果父类的类加载器是null就调用bootsttap classloader来加载。这就是父类委托,因此在加载的时候是先从bootsttap 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;
    }

 看了那么多源代码那么怎么实现自己的classloader呢,有什么作用呢,对于一些我们不想被反编译的类,我们可以调用加密技术将类加密。在使用的时候只要用自定义的classloader反编译就好了。

我们可以通过两种方法来写自己的classloader,下面分别来介绍:

第一种是继承ClassLoader方法主要是用FileInputStream读取class,然后用defineClass方法返回class。注意在defineClass()方法的第一个参数必须设置成null。不然会抛出异常

 

package com.fnk.classloader;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class FileClassLoader extends ClassLoader {
	private String directory = "C:/"; // Default directory.
	private String type = ".class";

	public FileClassLoader(String directory) {
		this.directory = directory;
	}

	public Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] data;
		data = loadClassData(getFileName(name));
		return defineClass(name, data, 0, data.length);

	}
	
	private String getFileName(String className){
		int index = className.lastIndexOf(".");
		if(index != -1){
			return className.substring(index + 1);
		}else{
			return className;
		}
	}

	private byte[] loadClassData(String name) throws ClassNotFoundException {
		FileInputStream fis = null;
		ByteArrayOutputStream baos = null;

		try {
			fis = new FileInputStream(directory + name + type);
			baos = new ByteArrayOutputStream();
			int len = 0;
			while ((len = fis.read()) != -1) {
				baos.write(len);
			}
			byte[] data = baos.toByteArray();
			return data;
		} catch (Exception e) {
			e.printStackTrace();
			throw new ClassNotFoundException();
		} finally {
			if (fis != null)
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			if (baos != null)
				try {
					baos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

		}
	}
	
	public static void main(String[] args) {
		FileClassLoader classLoader1 = new FileClassLoader(
				"./");
		try {
			Class c1 = classLoader1.loadClass("com.fnk.classloader.TestClass");
			Testf tc1 = (Testf)c1.newInstance();
			tc1.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();
		}

	}
}

 

第二种是 URLClassLoader 加载jar文件,然后调用loaderclass查找类。

 

package com.fnk.classloader;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class MyURLClassLoader {
	public static Class loaderClass(String file, String className) {
		URL url;
		Class myClass = null;
		try {
			url = new URL("file:" + file);
			URLClassLoader myClassLoader = new URLClassLoader(
					new URL[] { url }, Thread.currentThread()
							.getContextClassLoader());

			myClass = myClassLoader.loadClass(className);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return myClass;
	}

	public static void main(String[] args) {
		try {
			Testf testf = (Testf)loaderClass("./TestClass.jar",
					"com.fnk.classloader.TestClass").newInstance();
			testf.test();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

 附件中是自定义的classloader

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值