实现自定义的classLoader加载classpath中的class

转载:http://www.blogjava.net/shenh062326/archive/2012/05/20/378623.html

 

最近这些天学习了classLoader的原理, 原因是因为服务器上的一个java进程启动时加载两个不同版本的jar包, 含有相同名字的类, 而且服务端的jar包排在前面, 我上传的jar包排在后面, 于是每次都使用服务端的jar包, 我的jar包便无法生效, 因此希望修改classLader, 让它按相反的顺序加载jar包.
     网上查阅了classLoader的原理, 分析jvm默认使用AppClassLoader加载classpath中的类, 因此目标很明确, 替换它就行了, 找到一个参数

java.system.class.loader, 只需要在启动java进程的时候把它设置为自己的类就行. 
     开始写自己的classLoader, 参考URLClassLoader与网上的介绍, 写了一个简单的,上代码:
  1  package org.taobao.yuling.testClassLoader;
  2 
  3  import java.io.*;
  4  import java.lang.reflect.*;
  5  import java.net.MalformedURLException;
  6  import java.net.URL;
  7  import java.net.URLClassLoader;
  8  import java.nio.ByteBuffer;
  9  import java.security.AccessControlContext;
 10  import java.security.AccessController;
 11  import java.security.CodeSigner;
 12  import java.security.CodeSource;
 13  import java.security.PermissionCollection;
 14  import java.security.PrivilegedAction;
 15  import java.security.SecureClassLoader;
 16  import java.util.Arrays;
 17  import java.util.HashMap;
 18  import java.util.Map;
 19  import java.util.jar.Attributes;
 20  import java.util.jar.Manifest;
 21 
 22 
 23  import sun.misc.JavaNetAccess;
 24  import sun.misc.Resource;
 25  import sun.misc.SharedSecrets;
 26  import sun.misc.URLClassPath;
 27 
 28  /**
 29   * The class loader used for loading from java.class.path.
 30   * runs in a restricted security context.
 31    */
 32  public  class MyClassLoader  extends URLClassLoader {
 33      public URLClassPath ucp;
 34        private Map<String, Class<?>> cache =  new HashMap();
 35        private  static  final Method defineClassNoVerifyMethod;
 36       
 37        static String[] paths = System.getProperty("java.class.path").split(";");
 38       
 39        static URL[] urls =  new URL[paths.length];
 40 
 41        static{
 42           System.out.println(Arrays.toString(paths));
 43           System.out.println(System.getProperty("java.class.path"));
 44            for( int i=0; i<urls.length; i++){
 45                try {
 46                   
 47                 urls[i] =  new URL("file:"+paths[paths.length-1-i]);
 48             }  catch (MalformedURLException e) {
 49                 e.printStackTrace();
 50             }
 51           }
 52           System.out.println(Arrays.toString(urls));
 53         SharedSecrets.setJavaNetAccess( new JavaNetAccess() {
 54            public URLClassPath getURLClassPath(URLClassLoader u) {
 55              return ((MyClassLoader)u).ucp;
 56           } } );
 57         Method m;
 58          try {
 59           m = SecureClassLoader. class.getDeclaredMethod(
 60             "defineClassNoVerify",  new Class[] { String. class
 61             ByteBuffer. class, CodeSource. class });
 62           m.setAccessible( true);
 63         }  catch (NoSuchMethodException nsme) {
 64           m =  null;
 65         }
 66         defineClassNoVerifyMethod = m;
 67       }
 68       
 69        public MyClassLoader(URL[] urls) {
 70              super(MyClassLoader.urls);
 71              this.ucp =  new URLClassPath(MyClassLoader.urls);
 72         }
 73       
 74        public MyClassLoader(ClassLoader parent) {
 75              super(MyClassLoader.urls, parent);
 76              this.ucp =  new URLClassPath(MyClassLoader.urls);
 77         }
 78 
 79        public Class<?> loadClass(String name)
 80          throws ClassNotFoundException{
 81         Class c =  null;
 82         
 83          if (name.contains("hadoop")) {
 84           c = (Class) this.cache.get(name);
 85            if (c ==  null) {
 86             c = findClass(name);
 87              this.cache.put(name, c);
 88           }
 89         }  else {
 90           c = loadClass(name,  false);
 91         }
 92          return c;
 93       }
 94 
 95        protected Class<?> findClass(String name)
 96          throws ClassNotFoundException
 97       {
 98         String path = name.replace('.', '/').concat(".class");
 99         Resource res =  this.ucp.getResource(path);
100          if (res !=  null) {
101            try {
102              return defineClass(name, res,  true);
103           }  catch (IOException e) {
104              throw  new ClassNotFoundException(name, e);
105           }
106         }
107          throw  new ClassNotFoundException(name);
108       }
109 
110        private Class<?> defineClass(String name, Resource res,  boolean verify)  throws IOException
111       {
112          int i = name.lastIndexOf('.');
113         URL url = res.getCodeSourceURL();
114          if (i != -1) {
115           String pkgname = name.substring(0, i);
116 
117           Package pkg = getPackage(pkgname);
118           Manifest man = res.getManifest();
119            if (pkg !=  null)
120           {
121              if (pkg.isSealed())
122             {
123                if (!pkg.isSealed(url)) {
124                  throw  new SecurityException(
125                   "sealing violation: package " + pkgname + 
126                   " is sealed");
127               }
128 
129             }
130              else  if ((man !=  null) && (isSealed(pkgname, man))) {
131                throw  new SecurityException(
132                 "sealing violation: can't seal package " + 
133                 pkgname + ": already loaded");
134             }
135 
136           }
137            else  if (man !=  null)
138             definePackage(pkgname, man, url);
139            else {
140             definePackage(pkgname,  nullnullnullnullnullnull
141                null);
142           }
143 
144         }
145 
146         ByteBuffer bb = res.getByteBuffer();
147          byte[] bytes = bb ==  null ? res.getBytes() :  null;
148 
149         CodeSigner[] signers = res.getCodeSigners();
150         CodeSource cs =  new CodeSource(url, signers);
151 
152          if (!verify)
153         {
154           Object[] args = { name, bb ==  null ? ByteBuffer.wrap(bytes) : bb, 
155             cs };
156            try {
157              return (Class)defineClassNoVerifyMethod.invoke( this, args);
158           }
159            catch (IllegalAccessException localIllegalAccessException) {
160           }
161            catch (InvocationTargetException ite) {
162             Throwable te = ite.getTargetException();
163              if ((te  instanceof LinkageError))
164                throw ((LinkageError)te);
165              if ((te  instanceof RuntimeException)) {
166                throw ((RuntimeException)te);
167             }
168              throw  new RuntimeException("Error defining class " + name, 
169               te);
170           }
171 
172         }
173          return defineClass(name, bytes, 0, bytes.length, cs);
174       }
175 
176        private  boolean isSealed(String name, Manifest man) {
177         String path = name.replace('.', '/').concat("/");
178         Attributes attr = man.getAttributes(path);
179         String sealed =  null;
180          if (attr !=  null) {
181           sealed = attr.getValue(Attributes.Name.SEALED);
182         }
183          if ((sealed ==  null) && 
184           ((attr = man.getMainAttributes()) !=  null)) {
185           sealed = attr.getValue(Attributes.Name.SEALED);
186         }
187 
188          return "true".equalsIgnoreCase(sealed);
189       }
190 }
191 
注: isSealed(), defineClass(), findClass(), 都是直接从URLClassLoader复制过来.
代码较粗糙, 不过意思很简单, 就是通过把java.class.path的jar包反顺序, 然后定义了自己的
Class<?> loadClass(String name) 方法, 在加载我需要的类时在已被反序的的jar包中查找, 这样就能首先加载我修改的类了. 
运行方法:
java -Djava.system.class.loader=org.taobao.yuling.testClassLoader.MyClassLoader -classpath ...  MyTestClass

 

你可能感兴趣的:(ClassLoader)