Spring 动态代理 之 but was actually of type 'com.sun.proxy.$Proxy14 Exception
2019-11-08 22:31
2336 查看
今天在写
Spring的引介代理的时候,报了一个错:
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'inter1' is expected to be of type 'com.dengchengchao.springtest.intertest.Inter1Impl' but was actually of type 'com.sun.proxy.$Proxy14'
大概的意思是类型转换错误。
源代码如下:
ApplicationContext ctx = new AnnotationConfigApplicationContext(Conf.class); Inter1 inter1 = ctx.getBean("inter1", Inter1Impl.class); inter1.say1(); Inter2 inter2=(Inter2) inter1; inter2.say2();
后来
CGLIB就行。
我们都知道
JDK只能代理接口,对于非接口的类的代理,应该使用
CGLIB。
因为
CGLIB是通过继承代理类实现,而
JDK是通过实现接口实现。
但是我这里
Inter1分明就是一个接口。后来仔细检查了代码,发现其实使用
Java代理也行,只要改如下一行代码即可:
Inter1 inter1 = ctx.getBean("inter1", Inter1.class);
也就是说,需要转换成类型应该是
Inter1.class而不能是具体的类
Inter1Impl。
为什么
Java代理只支持接口代理,这里我们来深扒一下:
首先定义一个接口:
public interface People { void eat(); }
然后定义一个实现类:
public class Student implements People{ @Override public void eat() { System.out.println("用手吃"); } }
接着定义一个代理类:
public class StudentInvokeHandler implements InvocationHandler { private Object target; public StudentInvokeHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("饭前洗手"); Object retVal = method.invoke(target, args); System.out.println("饭后吃水果"); return retVal; } }
接下来,通过代理来调用
Student
public static void main(String[] args) { //初始化Student Student student = new Student(); //初始化Student代理类 StudentInvokeHandler studentInvokeHandler = new StudentInvokeHandler(student); //通过代理获取代理独享 People studentProxy = (People) Proxy.newProxyInstance(StudentInvokeHandler.class.getClassLoader(), new Class[]{People.class}, studentInvokeHandler); //通过代理对象调用eat方法 studentProxy.eat(); }
可以看见,
Java的代理非常简单,但是底层是如何实现的呢?
参照细说JDK动态代理的实现原理,我们在
main中设置一下
JVM属性
public static void main(String[] args) { //将生成的代理类文件保存 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); Student student = new Student(); StudentInvokeHandler studentInvokeHandler = new StudentInvokeHandler(student); People studentProxy = (People) Proxy.newProxyInstance(StudentInvokeHandler.class.getClassLoader(), new Class[]{People.class}, studentInvokeHandler); studentProxy.eat(); }
运行之后,可以在项目根目录中找到
com/sun/proxy/$Proxy0.class文件,这个文件便是代理
Student生成的对象的
.class文件:
public final class $Proxy0 extends Proxy implements People { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } c 15d8 atch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void eat() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.dengchengchao.springtest.proxy.People").getMethod("eat"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
通过以上文件我们可以发现:
生成的代理类继承了
Proxy
,实现了People
接口这也就是为什么
JDK
代理只能代理接口,不能代理具体的类,因为Java
不能多继承,因此只能实现接口由于实现的是接口,因此对于生成的代理对象
proxy
proxy instanceof People //true proxy instanceof Student //false
这便是开始我们所遇到的问题的根源所在,
proxy仅仅是实现了
People接口,却不是继承自
Student类,因此无法将
proxy对象转换为
Student类型,所以才报的错。
明白了这个问题,以后使用底层为
JDK代理的类,就不会再出错了。
如果觉得写得不错,欢迎扫描下面二维码关注微信公众号:逸游Java ,每天不定时发布一些有关Java进阶的文章,感谢关注
相关文章推荐
- 解决spring使用动态代理类型转换失败的问题--java.lang.ClassCastException: com.sun.proxy.$Proxy$ cannot be cast to ...
- 解决spring使用动态代理类型转换失败的问题--java.lang.ClassCastException: com.sun.proxy.$Proxy$ cannot be cast to ...
- 解决spring使用动态代理类型转换失败的问题--java.lang.ClassCastException: com.sun.proxy.$Proxy$ cannot be cast to ...今天
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 用Java的动态代理模拟spring的AOP
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
- 学习Java框架的笔记(Spring AOP)简介、动态代理、基于代理类的AOP实现、AspectJ开发
- Spring AOP中的JDK和CGLib动态代理哪个效率更高?
- spring中AOP动态代理基本原理
- Spring 动态代理(四)- 动态代理核心类 - ProxyCreatorSupport
- jdk动态代理引起的spring事务不起作用
- 详解Spring的两种代理方式:JDK动态代理和CGLIB动态代理
- Spring中的AOP操作(动态代理)
- spring中JDK动态代理
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- Java的动态代理机制及Spring的实现方式
- Java动态代理+注解体现Spring AOP思想
- spring->动态代理
- Spring中JDK的动态代理和CGLIB代理的区别
- JDK动态代理给Spring事务埋下的坑