`
bingobird
  • 浏览: 44866 次
社区版块
存档分类
最新评论

动态修改当前ClassLoader

阅读更多

自定义ClassLoader相信很多人都用过,网上文章也有很多。但如何使用自定义的ClassLoader有时确实比较头痛。

如果启动入口自己可以控制还好说,大不了通过自定义ClassLoader加载所有类就可以了,但如果控制不了,比如说是通过TOMCAT或脚本来启动的,但又要用自定义的ClassLoader来加载外部类,那就郁闷了。


我碰到的情形就是如此,其中的ClassLoaderC是tomcat的类加载器,而classLoaderD是自定义的类加载器。通常来说,我们只能选择访问C或D其中一个下面的类。有没办法能同时访问它们下两个的类呢?

 

其中一种办法是Thread.currentThread().setContextClassLoader。相对比较方便,但这在多线程环境下很容易产生问题。

还有一种办法是通过反射调用,修改ClassLoaderC的parent为ClassLoaderD。我们知道ClassLoader的委托机制是先让parent(父)类加载器寻找,只有在parent找不到的时候才从自己的类路径中去寻找。这样我们通过修改parent就能达到同时访问的目的。当然,由于parent是私有的,而且没有提供写方法,所以还需要用反射来设置。

 

之前还尝试了另一种方法,即ClassLoader.addClass,但发现类是进去了,但package里没有,还是会加载不到类。

 

这种方法目前还在试用,大家觉得有什么问题欢迎提出来:)

 

 

public class ContainerClassLoader extends ClassLoader {

    private Map<String, Class<?>> loadedClasses = new HashMap<String, Class<?>>();

    private static ContainerClassLoader INSTANCE;

    private ContainerClassLoader() {
        super(ContainerClassLoader.class.getClassLoader().getParent());
    }

    /**
     * 初始化
     */
    public static void init() {
        INSTANCE = new ContainerClassLoader();
        try {
            INSTANCE.addThisToParentClassLoader(ContainerClassLoader.class
                    .getClassLoader());
        } catch (Exception e) {
            System.err.println("设置classloader到容器中时出现错误!");
        }
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        if (loadedClasses.containsKey(name)) {
            return loadedClasses.get(name);
        }
        return super.loadClass(name, resolve);
    }

    /**
     * 将this替换为指定classLoader的parent ClassLoader
     * 
     * @param classLoader
     */
    private void addThisToParentClassLoader(ClassLoader classLoader) throws Exception {
        Field field = ClassLoader.class.getDeclaredField("parent");
        field.setAccessible(true);
        field.set(classLoader, this);
    }

}

 

  • 大小: 10.5 KB
3
0
分享到:
评论
4 楼 jsufly 2015-05-27  
bingobird 写道
另外,在过程中还碰到spring加载时,classloader还没修改的问题。后来通过在web.xml中增加listener-class来实现。


“在web.xml中增加listener-class ”是在listener中直接使用ContainerClassLoader吗?
3 楼 LinApex 2014-12-09  
bingobird 写道
另外,在过程中还碰到spring加载时,classloader还没修改的问题。后来通过在web.xml中增加listener-class来实现。

还有更神奇的类型匹配错误:
java.lang.ClassCastException: org.eclipse.core.runtime.internal.adaptor.ContextFinder cannot be cast to java.net.URLClassLoader

调试发现,原来是通过调第三方OSGI模块加载完Bundle,当前线程的ClassLoader已经变成了ContextFinder。改回来就OK了。

这次调试过程中,很多东西通过GOOGLE很难搜到答案,反编译源码来对照调试确实很有帮助。


兄弟,这个图帮了我很大的忙,我就直接 添加 自定义Classloader 到 当前 ContentClassloader 上。实现动态MVC。
2 楼 bingobird 2010-03-04  
另外,在过程中还碰到spring加载时,classloader还没修改的问题。后来通过在web.xml中增加listener-class来实现。

还有更神奇的类型匹配错误:
java.lang.ClassCastException: org.eclipse.core.runtime.internal.adaptor.ContextFinder cannot be cast to java.net.URLClassLoader

调试发现,原来是通过调第三方OSGI模块加载完Bundle,当前线程的ClassLoader已经变成了ContextFinder。改回来就OK了。

这次调试过程中,很多东西通过GOOGLE很难搜到答案,反编译源码来对照调试确实很有帮助。
1 楼 bingobird 2010-03-04  
后续补充:
后面还碰到了问题,如果将TOMCAT的当前ClassLoader的parent修改了,还是会碰到类无法识别的问题。
原因是WebappClassLoader类也有个私有属性,与基类ClassLoader名称一样,而且是在WebappClassLoader类构造时赋值。
处理方法:通过反射将WebappClassLoader对象的两个私有属性都修改。
如下:
 private void addThisToParentClassLoader(ClassLoader classLoader) throws Exception {
        Field field;
        try {
            //将当前ClassLoader的parent属性修改为本对象(适用于WebClassLoader)
            field = classLoader.getClass().getDeclaredField("parent");
            field.setAccessible(true);
            field.set(classLoader, this);
        } catch (Exception e) {
        }
        //将当前ClassLoader的parent ClassLoader修改为本对象
        field = ClassLoader.class.getDeclaredField("parent");
        field.setAccessible(true);
        field.set(classLoader, this);
}

相关推荐

    ClassLoader 案例

    自定义ClassLoader允许开发者根据特定需求加载类,比如动态加载或更新类文件,这在某些高级应用场景中非常有用,如插件系统、热部署等。本案例将深入探讨如何创建一个自定义的ClassLoader,利用Java反射和注解技术...

    ClassLoader 详解.doc

    在类加载过程中,遵循“双亲委派模型”:当一个类加载器需要加载某个类时,它首先会委托它的父加载器尝试加载,只有当父加载器无法加载时,当前加载器才会尝试自己加载。这种机制避免了类的重复加载,并确保了核心...

    JVM.zip_JVM代理_classloader

    JVM代理和类加载器是Java开发中的高级话题,它们允许我们实现诸如动态字节码修改、代码加密、插件系统等复杂功能。通过自定义类加载器,我们可以定制加载流程,如在加载过程中进行加密操作,增强代码的安全性。而JVM...

    使用自定义ClassLoader解决反序列化serialVesionUID不一致问题 _ 回忆飘如雪1

    - **取消双亲委派模型**:改为当前`ClassLoader`优先加载,这样可以避免不一致的JAR被父`ClassLoader`加载,从而实现隔离。 - **便捷地共享依赖**:允许共享那些没有`serialVersionUID`冲突的类库,提高效率。 - **...

    classloader 热部署

    总之,Classloader热部署是Java开发中的高级技巧,它通过巧妙地管理和操作类加载器,实现了代码的动态更新,极大地提高了开发效率。理解并掌握这些技术,对于提升Java应用的开发和维护能力至关重要。

    Java动态加载与插件开发研究.pdf

    ClassLoader提供了多种途径来获取ClassLoader,例如使用系统类加载器、当前类加载器或当前线程类加载器。例如: ```java ClassLoader classLoader = ClassLoader.getSystemClassLoader(); Class cls = classLoader....

    插件化开发实现动态换肤

    在Android应用开发中,动态换肤是一项非常吸引用户的功能,它允许用户根据个人喜好更改应用程序的外观和风格。实现这一功能通常需要借助插件化开发技术。本文将深入探讨插件化开发的基本原理以及如何利用它来实现...

    Java中的动态代理

    这种模式通常用于在不修改目标对象的情况下,为对象添加额外的功能或者行为。动态代理分为两类:基于接口的代理(Java动态代理)和基于具体类的代理(CGLIB)。本文主要介绍基于接口的Java动态代理。 #### 二、Java...

    Android动态加载资源示例代码

    通常,我们会将主题定义为不同的样式文件(style.xml),然后在运行时动态替换掉当前的主题。通过改变主题,用户可以根据自己的喜好调整应用的外观,提升使用体验。 实现主题切换通常有以下步骤: 1. 定义不同主题...

    关于java热部署知识.doc

    当一个类需要加载时,它会首先尝试由父ClassLoader加载,只有当父ClassLoader无法找到该类时,才会由当前ClassLoader加载。这种机制防止了类的冲突,并确保了系统类如`String`只能由JRE的ClassLoader加载。 在JVM中...

    java动态代理

    通过动态代理,我们可以在不修改原有代码的情况下增加新的功能,比如日志记录、事务管理等。 #### 二、Java动态代理的基本原理 Java动态代理的核心是`java.lang.reflect.Proxy`类和`java.lang.reflect....

    Android一二三代壳加固原理分析,代码实现ART下抽取壳。

    在Android中,类加载器(ClassLoader)遵循双亲委派模型,即首先由父加载器尝试加载,如果父加载器无法加载,再由当前加载器尝试。默认情况下,Android使用`PathClassLoader`加载应用的.dex文件。加固过程中,通常...

    Java Instrumentation Framework.rar

    反射提供的功能,能在运行时(动态)的 ...Instrumentation是Java提供的一个来自JVM的接口,该接口提供了一系列查看和操作Java类定义的方法,例如修改类的字节码、向classLoader的classpath下加入jar文件等。

    java 动态加载的实现代码

    在描述中提到的Java动态加载类的主要目的是为了不改变主程序代码,仅通过修改配置文件就能实现对不同对象的操作和功能的变更。这种方式特别适合于需要频繁更新功能的系统,因为它大大降低了修改程序和重新部署的...

    安卓逆向学习笔记之ART定制方案比较和流程.docx

    因此,分析当前加载的.so文件所在的ClassLoader是非常重要的。 ```java // 示例代码,用于获取当前加载的.so文件路径 String path = System.getProperty("java.library.path"); Log.i("Xposed", "Library path: " +...

    扫描Class文件的方法

    3. **Javassist**:除了字节码操作,Javassist还提供了扫描类的能力,可以用于动态修改类。 五、实战示例 以下是一个简单的文件系统扫描示例: ```java import java.io.File; import java.io.FilenameFilter; ...

    java注解_反射_字节码_类加载机制.zip

    字节码也可以被反编译,用于分析代码逻辑或进行动态代码修改,如Java代理。 **类加载机制(Class Loading)**: 类加载是JVM启动时或运行过程中动态加载类的过程。这个过程包括加载、验证、准备、解析和初始化五个阶段...

    day020-继承加强和设计模式代码和笔记.rar

    装饰者模式指的是在不必改变原类(Input)文件和使用继承的情况下,动态地扩展一个对象的功能。 它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 实现步骤 :通过对原类文件继承,对原有...

    JAVA文件中调用已编译的.CLASS的类.doc

    总结来说,Java程序调用已编译的`.class`文件主要是通过自定义类加载器或内置的`ClassLoader`实现,这样可以实现动态加载和执行外部代码,尤其适用于插件系统、模块化应用等场景。在实际开发中,理解类加载机制以及...

Global site tag (gtag.js) - Google Analytics