记一次JDK类加载强转错误

背景

业务需要在运行时动态加载class文件,并注册到spring bean中。由于JDK的类加载会拿到Object实例,所以要用到强转类型。

加载例子

一般见网上的例子即可,本例使用a.class,定义一个cn.demo.MyDemo类,继承于IDemo接口。

public class MyDemo implements IDemo {
	@Overided
	public String show() {
		System.out.println("YES");
	}
}

在Main方法中,强转后使用show方法,无异常。

public class MyClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) {
        String myPath = "file:///D:/a.class";
        System.out.println(myPath);
        byte[] cLassBytes = null;
        Path path = null;
        try {
            path = Paths.get(new URI(myPath));
            cLassBytes = Files.readAllBytes(path);
        } catch (IOException | URISyntaxException e) {
            e.printStackTrace();
        }
        Class clazz = defineClass(name, cLassBytes, 0, cLassBytes.length);
        return clazz;
    }
	public static void main(String[] args) throws ClassNotFoundException {
        MyClassLoader loader = new MyClassLoader();
        Class<?> aClass = loader.findClass("cn.demo.MyDemo");
        try {
            Object obj = aClass.newInstance();
            IDemo demo = (IDemo) obj;
            demo.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在Spring 的Service层中,却提示ClassCastException。

...
MyClassLoader loader = new MyClassLoader();
Class<?> aClass = loader.findClass("cn.demo.MyDemo");

Object obj = aClass.newInstance();
// 此处报错
IDemo demo = (IDemo) obj;

// 注册到Bean中
...

排错过程

使用instanceof检测是否同一个类

boolean flag = obj instanceof IDemo //显示false

结果显示Spring下为false,main下为true

检测是否使用同一个类加载器

ClassLoader l1 = obj.getClassLoader();
ClassLoader l2 = IDemo.getClassLoader();

Spring下:

  • l1为org.springframework.boot.devtools.restart.classloader.RestartClassLoader
  • l2为sun.misc.Launcher$AppClassLoader

Main下:

  • 都为sun.misc.Launcher$AppClassLoader

好了,总算知道问题在哪了,Spring的热部署惹的祸,把热部署去掉就能强转了。

要点

同一个类型判断:

  • 必须包路径及名称一致
  • 必须属于同一类加载器加载出来的,否则也是不同的

后记

若在Spring Boot中打包成Jar使用,类加载器会变成LaunchedURLClassLoader,这时更多会导致运行环境中出异常而开发环境无异常。

这时需要将自定义类加载器继承调整一下:

public class MyClassLoader extends URLClassLoader {
    public MyClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }
...

解释:使用URLClassLoader是因为它属于两种情况下共同的父类,而且借助构造函数传父类加载器,可以委托类加载器进行加载。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值