实现自定义的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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaClassLoader是一个关键组件,它负责将Java加载到JVMJavaClassLoader可以分为三个层次:Bootstrap ClassLoader、Extension ClassLoaderApplication ClassLoader自定义ClassLoader可以使得我们更好地控制Java类的加载过程,例如可以从特定的路径或者网络加载类。 下面是一个简单的自定义ClassLoader的示例代码: ```java public class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] data = loadClassData(name); return defineClass(name, data, 0, data.length); } private byte[] loadClassData(String name) { String fileName = classPath + File.separatorChar + name.replace('.', File.separatorChar) + ".class"; try { FileInputStream fis = new FileInputStream(fileName); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len; byte[] buffer = new byte[1024]; while ((len = fis.read(buffer)) != -1) { baos.write(buffer, 0, len); } return baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; } } ``` 上述代码,我们继承了ClassLoader类,并实现了findClass方法,在该方法,我们可以根据自己的需求去加载Java类。在示例,我们从指定的路径加载类的字节码文件,并将其转换为字节数组,最后调用defineClass方法生成Class对象。注意,这里的路径需要与ClassLoader所在的类路径相对应。 我们可以通过以下代码来使用自定义ClassLoader: ```java MyClassLoader myClassLoader = new MyClassLoader("/path/to/class/files"); Class<?> clazz = myClassLoader.loadClass("com.example.Test"); Object obj = clazz.newInstance(); Method method = clazz.getMethod("hello"); method.invoke(obj); ``` 上述代码,我们通过自定义ClassLoader加载了Test类,并调用了hello方法。 需要注意的是,JavaClassLoader是一个层级结构,类的加载过程会从上至下依次进行,因此我们需要根据具体的需求来选择ClassLoader的层次。在自定义ClassLoader时,我们需要保证其所在的类路径与被加载的类所在的类路径相对应,否则就会出现ClassNotFoundException。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值