posts - 28, comments - 37, trackbacks - 0, articles - 0
     最近这些天学习了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

Feedback

# re: 实现自定义的classLoader加载classpath中的class[未登录]  回复  更多评论   

2015-01-07 14:03 by 阿帕奇
挺不错的。、

只有注册用户登录后才能发表评论。


网站导航: