将自定义类加载器放入默认类中

一般来说能设置类加载器的地方只有Thread.currentThread().setContextClassLoader(classloader);如果想在默认类的类加载器中修改自定义的类加载器应该怎么做呢?

首先Class中没有能修改ClassLoader的地方,所以只能用其他方法修改

在Class的第694行发现有classloader,我们只需要获取并改写就行了

Field field = Class.class.getDeclaredField("classloader");
field.setAccessible(true);
System.out.println(field);

看起来应该没问题,但是运行后发现报错了,找不到这个字段,但是在Class很明显能看到这个字段是存在,看来这个字段的设定很特殊,不能获取,但是这时就放弃就没意义了,跟着getDeclaredField方法追踪代码时发现2574行的代码中有一个函数非常特别。

private Field[] privateGetDeclaredFields(boolean publicOnly) {
        checkInitted();
        Field[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.declaredPublicFields : rd.declaredFields;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        res = Reflection.filterFields(this, getDeclaredFields0(publicOnly));
        if (rd != null) {
            if (publicOnly) {
                rd.declaredPublicFields = res;
            } else {
                rd.declaredFields = res;
            }
        }
        return res;
    }

可以看到有一个方法是filterField,从名字就能看出是过滤字段,所以非常显然,这里有一个会过滤字段的存在,进入Reflection代码,可以看到两个非常显然的两个字段fieldFilterMap和methodFilterMap,见名知意,保存过滤的字段,可以试试把这两个字段取出来看看

Field field = Reflection.class.getDeclaredField("fieldFilterMap");
field.setAccessible(true);
System.out.println(field);

可惜,这个字段也是不能取的,原本想的是取出来清空就好了,也许还有其他方法

Class中的getDeclaredFields0会返回字段数组,传入的参数是publicOnly,有点类似选择是否是public的形式,可以用反射得到这个函数,然后运行试试

Method gdf = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
gdf.setAccessible(true);
//传入想要获取的类的class
Field[] fields= (Field[]) gdf.invoke(Class.class, false);
for (Field field : fields) {
	System.out.println(field);
}

从运行结果可以看到一个字段classloader,这就是我们想要修改的,既然得到了,我们就可以把这个字段单独取出,然后修改

URLClassLoader ucl = (URLClassLoader) TestRun.class.getClassLoader();
TestClassLoader tcl = new TestClassLoader(ucl.getURLs());
for (Field field : fs) {
	if(field.getName().equals("classLoader")) {
		field.setAccessible(true);
		field.set(TestRun.class, tcl);
		break;
    }
}

现在来做个测试

TestClassLoader:

import java.net.URL;
import java.net.URLClassLoader;

public class TestClassLoader extends URLClassLoader{

	public TestClassLoader(URL[] urls) {
		super(urls,null);
	}
	
	@Override
	public Class<?> findClass(String name) throws ClassNotFoundException {
		System.out.println("Get a Class:"+name);
		return super.findClass(name);
	}

	public void addURL(URL url) {
		super.addURL(url);
	}
	
}

TestRun:

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;

import sun.reflect.Reflection;

public class TestRun {
	
	public static void main(String[] args) {
		try {//没修改classloader
			Class<?> clazz = Class.forName("TestClass");
			System.out.println("find class");
		} catch (ClassNotFoundException e) {
			System.err.println("not find class");
		}
		URLClassLoader ucl = (URLClassLoader) TestRun.class.getClassLoader();
		TestClassLoader tcl = new TestClassLoader(ucl.getURLs());
		try {
			//Class类中的2583行代码的模仿
			Method gdf = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
			gdf.setAccessible(true);
			Field[] fs = (Field[]) gdf.invoke(Class.class, false);
			for (Field field : fs) {
				if(field.getName().equals("classLoader")) {
					field.setAccessible(true);
					field.set(TestRun.class, tcl);
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			tcl.addURL(new File("testclass").toURI().toURL());
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		}
		try {//修改类加载器后的结果
			Class<?> clazz = Class.forName("TestClass");
			System.out.println("find class");
		} catch (ClassNotFoundException e) {
			System.err.println("not find class");
		}
	}
}

 运行结果:

not find class
Get a Class:TestClass
find class

可以很明显的看到,类被发现了,而且是被Class加载的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值