利用反射模拟Tomcat类加载器的toString方法

本文介绍了如何利用反射技术模拟Tomcat的WebappClassLoader类的toString方法,展示类加载器的父类及加载的资源。通过反编译Tomcat源码并调试,找到了URLClassLoader的ucp属性,通过反射获取并打印类路径,提供了DebugUtil工具类的代码示例,以帮助开发者进行程序调试。
摘要由CSDN通过智能技术生成

一直认为tomcat5的类加载器的toString方法很酷,为什么呢?因为它的toString方法列出了所有的父类加载器以及类加载器加载的资源(即classpath).可能有些朋友还没有注意过,如果想看看它到底有多酷的话可以随便找个jsp
<%System.out.println(com.syj.Test.class.getClassLoader());%>

WEB-INF/classes/com/syj/Test

启动tomcat访问jsp,看看控制台:

信息: Exposing service with name {http://ws.syj.com}Demo
2008-9-20 16:15:32 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on http-8080
2008-9-20 16:15:33 org.apache.jk.common.ChannelSocket init
信息: JK2: ajp13 listening on /0.0.0.0:8009
2008-9-20 16:15:33 org.apache.jk.server.JkMain start
信息: Jk running ID=0 time=15/78  config=D:/SYJ.WORK/SYJ.SERVER/tomcat5/conf/jk2.properties
2008-9-20 16:15:33 org.apache.catalina.startup.Catalina start
信息: Server startup in 6907 ms

转载请注明出处http://blog.csdn.net/sunyujia/
WebappClassLoader
  delegate: false
  repositories:
    /WEB-INF/classes/
----------> Parent Classloader:
StandardClassLoader
  delegate: true
  repositories:
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/shared/classes/
----------> Parent Classloader:
StandardClassLoader
  delegate: true
  repositories:
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/classes/
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/ant-launcher.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/ant.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/commons-collections-3.1.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/commons-dbcp-1.2.1.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/commons-el.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/commons-pool-1.2.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/jasper-compiler.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/jasper-runtime.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/jsp-api.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/mssql_All.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/naming-common.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/naming-factory.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/naming-java.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/naming-resources.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/servlet-api.jar
    file:D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/tools.jar
----------> Parent Classloader:

sun.misc.Launcher$AppClassLoader@18d107f

 

非常清晰的调试信息,classpath一目了然,在一些复杂的类加载场景非常有用,

反编译tomcat5/server/lib/catalina.jar/WebappClassLoader.class 类查看源码

  1.     public String toString()
  2.     {
  3.         StringBuffer sb = new StringBuffer("WebappClassLoader/r/n");
  4.         sb.append("  delegate: ");
  5.         sb.append(_flddelegate);
  6.         sb.append("/r/n");
  7.         sb.append("  repositories:/r/n");
  8.         if(repositories != null)
  9.         {
  10.             for(int i = 0; i < repositories.length; i++)
  11.             {
  12.                 sb.append("    ");
  13.                 sb.append(repositories[i]);
  14.                 sb.append("/r/n");
  15.             }
  16.         }
  17.         if(parent != null)
  18.         {
  19.             sb.append("----------> Parent Classloader:/r/n");
  20.             sb.append(parent.toString());
  21.             sb.append("/r/n");
  22.         }
  23.         return sb.toString();
  24.     }

 

so easy,于是就想写个Util类,搞个 public static String toString(ClassLoader classLoader) 方法,说干就干,不过repositories这个属性是tomcat自己搞的,在sun提供的类加载器中,哪个属性存储了classpath呢?我可没兴趣翻阅tomcat类加载过程的源码,于是在debug下,内存里面找了一通,发现原来是存储在URLClassLoader的URLClassPath ucp属性中,虽然不是public的,但是没有关系,因为可以靠反射取得,反射可是干这个的强项,呵呵,代码不多,贴出来和大家一起分享成果.

 

  1. import java.io.File;
  2. import java.lang.reflect.Field;
  3. import java.net.URL;
  4. import java.net.URLClassLoader;
  5. import java.util.List;
  6. /**
  7.  * <p>
  8.  * Title:调试辅助类
  9.  * </p>
  10.  *
  11.  * <p>
  12.  * Description:
  13.  * </p>
  14.  *
  15.  * <p>
  16.  * Copyright: 转载请注明出处http://blog.csdn.net/sunyujia/
  17.  * </p>
  18.  *
  19.  * @author 孙钰佳
  20.  * @main sunyujia@yahoo.cn
  21.  * @date Sep 20, 2008 3:27:23 PM
  22.  */
  23. public class DebugUtil {
  24.     /**
  25.      *
  26.      * Description:将类加载器转换为字符串,用于打印,调试程序使用.
  27.      * 转载请注明出处http://blog.csdn.net/sunyujia/
  28.      *
  29.      * @mail sunyujia@yahoo.cn
  30.      *
  31.      * @since:Sep 20, 2008 3:29:59 PM
  32.      */
  33.     public static String toString(ClassLoader classLoader) {
  34.         StringBuffer sb = new StringBuffer(classLoader.getClass().getName());
  35.         sb.append("/r/n");
  36.         try {
  37.             Field[] fields = URLClassLoader.class.getDeclaredFields();// 取出URLClassLoader类加载器的全部属性
  38.             for (int i = 0; i < fields.length; i++) {// 遍历属性
  39.                 Field field = fields[i];
  40.                 if (field.getName().equals("ucp")) {// 找到ucp属性Field对象
  41.                     field.setAccessible(true);// 打开访问权限
  42.                     final Object ucp = field.get(classLoader);// 取得类加载器中的ucp对象
  43.                     Field[] ucpFields = ucp.getClass().getDeclaredFields();// 取ucp对象的全部属性
  44.                     for (int k = 0; k < ucpFields.length; k++) {// 遍历ucp对象的属性
  45.                         ucpFields[k].setAccessible(true);// 打开访问权限
  46.                         if (ucpFields[k].getName().equals("path")) {// 取得ucp对象中的path属性Field对象
  47.                             List list = (List) ucpFields[k].get(ucp);// 取得ucp对象中的path对象
  48.                             sb.append("  repositories:/r/n");
  49.                             for (int j = 0; j < list.size(); j++) {
  50.                                 sb.append("    ");
  51.                                 sb.append(list.get(j));
  52.                                 sb.append("/r/n");
  53.                             }
  54.                         }
  55.                     }
  56.                 }
  57.             }
  58.             if (classLoader.getParent() != null) {
  59.                 sb.append("----------> Parent Classloader:/r/n");
  60.                 sb.append(toString(classLoader.getParent()));// 递归
  61.                 sb.append("/r/n");
  62.             }
  63.         } catch (Exception e) {
  64.             e.printStackTrace();
  65.         }
  66.         return sb.toString();
  67.     }
  68.     public static void main(String[] args) throws Exception {
  69.         File file1 = new File(
  70.                 "D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/ant.jar");
  71.         File file2 = new File(
  72.                 "D:/SYJ.WORK/SYJ.SERVER/tomcat5/webapps/WebServicesDemo/WEB-INF/classes");
  73.         URLClassLoader mcl = new URLClassLoader(new URL[] {
  74.                 file1.toURI().toURL(), file2.toURI().toURL() });
  75.         System.out.println("演示1:");
  76.         System.out
  77.                 .println(DebugUtil.toString(DebugUtil.class.getClassLoader()));
  78.         System.out.println("演示2:");
  79.         Class clazz = mcl.loadClass("com.syj.Test");
  80.         System.out.println(DebugUtil.toString(clazz.getClassLoader()));
  81.     }
  82. }

程序输出结果:

 

演示1:
sun.misc.Launcher$AppClassLoader
  repositories:
    file:/D:/SYJ.WORK/SYJ.WORKSPACE/ws1/MyblogTest/bin/
----------> Parent Classloader:
sun.misc.Launcher$ExtClassLoader
  repositories:
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/dnsns.jar
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/localedata.jar
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/sunjce_provider.jar
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/sunmscapi.jar
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/sunpkcs11.jar


演示2:
java.net.URLClassLoader
  repositories:
    file:/D:/SYJ.WORK/SYJ.SERVER/tomcat5/common/lib/ant.jar
    file:/D:/SYJ.WORK/SYJ.SERVER/tomcat5/webapps/WebServicesDemo/WEB-INF/classes/
----------> Parent Classloader:
sun.misc.Launcher$AppClassLoader
  repositories:
    file:/D:/SYJ.WORK/SYJ.WORKSPACE/ws1/MyblogTest/bin/
----------> Parent Classloader:
sun.misc.Launcher$ExtClassLoader
  repositories:
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/dnsns.jar
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/localedata.jar
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/sunjce_provider.jar
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/sunmscapi.jar
    file:/D:/SYJ.WORK/SYJ.ENV/jdk6.0/jre/lib/ext/sunpkcs11.jar

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值