最近这些天学习了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, null, null, null, null, null, null,
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