第一步、使用父类接收子类,类型转换失败,猜测是不是父子类不能接收,(应该不可能,但还是试下);
第二步、使用同一个类接收,还是类型转换失败;
com.book.common.result.RestResponse 不能转换为com.book.common.result.RestResponse 后面加了
org.springframework.boot.devtools.restart.classloader.RestartClassLoader
第三步、查看报错信息,同一个类型转换失败,但后面加了个RestartClassLoader,没有引入自己实现过这个ClassLoader啊,一般用的是
Bootstrap ClassLoader(引导类加载器):加载JDK核心类库,路径在 JAVA_HOME/lib下面的JAR,自己新加一个JAR放在这个目录下,它也不会加载,它只加载JDK核心类库,包必须是(sun、java、javax开头的类),用C/C++调用,JAVA打印是NULL
Extension ClassLoader(扩展类加载器):开发者可以直接使用扩展类加载器,该加载器负责加载java_home/lib/ext目录下类库;开发者也可以使用扩展类加载器加载-Djava.ext.dir指定目录下的类库。
App ClassLoader(应用类加载器):应用类加载器主要用于加载系统应用的class文件。它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过getSystemClassLoader()方法可以获取到该类加载器,Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式即把加载类的动作交由父类处理,是一种任务委派模式,我们代码里面自己写的类,一般用这个加载器。
User ClassLoader(自定义加载器):对某些特殊的类想要进行特殊的加载处理,可以使用自定义ClassLoader,自定义ClassLoader只需要继承抽象类java.lang.ClassLoader,之后重载方法 findClass(String name) 哪些类需要使用当前加载器,可以重载loadClass(String path)怎么加载类。
同一个对象,加载器不同,加载到内存中之后,也不属于一个对象,这里也涉及到了为什么使用双亲委派模式,这里暂时不展开写什么是双亲委派,可以查百度。
第四步、根据上面的代码,可以猜测,应该是使用AppClassLoader,怎么打印了一个RestartClassLoader的加载器,是不是因为ClassLoader不同造成了对象不能转换,之后断点到对象,打印对象 obj.getClass().getClassLoader(),Dubbo返回的对象却是是AppClassLoader,但直接在代码处new一个相同的对象,打印的ClassLoader却是RestartClassLoader,所以转换失败,原因已找到;
解决办法:
1、找到引入RestartClassLoader的地方,发现是spring-boot-devtools,Spring 提供的热部署工具引入的,移除热部署工具,但我用了热部署工具做初始化boot-starp.yml加载,移除之后,这个加载的配置也需要重新处理,很麻烦;
2、指定RestResponse用AppClassLoader加载;
源码解析:org.springframework.boot.devtools.settings.DevToolsSettings会加载配置
org.springframework.boot.devtools.restart.ChangeableUrls的构造方法会根据配置确定哪些jar和目录需要使用RestClassLoader,把指定的类排除,之后就可以了,但这个只能指定很细粒度的;
3、使用Bootstrap或Extension的ClassLoader做返回,这样也能正常返回,比如用Object接收,但对Dubbo返回的对象,使用强转,也存在加载器不同,不能强转的问题,之后就需要借助反射,反射针对的是属性和方法,不同的加载器也是可以处理的,这样就处理了一个,但每个地方都需要反射,就提炼出一个公共的类处理
这样也能正常使用,但对存在子对象的也需要特殊除了;
目前使用的是第三种方法,可以根据实际情况调整使用方法,问题暂时解决。