Spring 动态代理 之 but was actually of type 'com.sun.proxy.$Proxy14 Exception
今天在写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();
后来google了一下发现把代理方式改成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});
} catch (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 动态代理 之 but was actually of type 'com.sun.proxy.$Proxy14 Exception的更多相关文章
- spring 动态代理
突然想到AOP,就简单回忆一下动态代理.1.什么是动态代理? 假如有个用户有增删该查4个方法,如果要对用户操作后进行日志记录,可能会有人说直接在增删改查后做日志记录就行. 一旦我想在用户操作之前加一个 ...
- Java的三种代理模式(Spring动态代理对象)
Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...
- Spring动态代理及Spring Bean的生命周期
数组添加值 public class DiTest { /** * 数组 */ private String [] arrays; /** * List:集合 */ private List<I ...
- Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?
前言 在研究 『 Spring 是如何解决循环依赖的 』 的时候,了解到 Spring 是借助三级缓存来解决循环依赖的. 同样在上一节留下了疑问: 循环依赖为什么要使用三级缓存?而不是使用二级缓存? ...
- Spring动态代理的生成-如何判断是使用JDK动态代理还是CGlib代理
前言 在上一篇文章中讲到了Spring是如何获取对应的Bean的增强,然后本次主要讲解一下Spring如何在获取到增强后创建Spring代理的. 在步入正题之前先给大家看一下Spring创建代理的大致 ...
- spring动态代理
接下来我们来体会下动态代理带给我们的便利 package aop006; public interface Girl { public void KFC(String datetime); publi ...
- Spring AOP动态代理原理与实现方式
AOP:面向切面.面向方面.面向接口是一种横切技术横切技术运用:1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物 2.日志处理:3.安全验证 ...
- 【spring基础】AOP概念与动态代理详解
一.代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...
- spring基础概念AOP与动态代理理解
一.代理模式 代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...
随机推荐
- 05-pandas索引切片读取数据缺失数据处理
引入 numpy已经能够帮助我们处理数据,能够结合matplotlib解决我们数据分析的问题,那么pandas学习的目的在什么地方呢? numpy能够帮我们处理处理数值型数据,但是这还不够 很多时候, ...
- 地图的标注Marker
(1)在point处添加标注:var marker = new BMap.Marker(point); (2)添加覆盖物:map.addOverlay(marker); (3)激活标注的拖拽功能:ma ...
- Java基础学习(八) - 多线程
理解线程 进程是指一个内存中运行的应用程序,系统运行一个程序即是一个进程从创建,运行,结束的过程. 线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程. 多线程的特点是并发 ...
- 基于微信小程序云开发实现的婚礼邀请函模板,可自行定制开发
这个婚礼邀请函是之前帮别人做的,现在人家婚礼举办过了也没啥关系了,想着就修改成通用模板进行开源,方便大家尤其是小白们克隆项目之后稍微修改就能够直接使用. 当时自己开始这个项目时候也是查阅了很多教程文章 ...
- Vue入门教程 第一篇 (概念及初始化)
注:为了本教程的准确性,部分描述引用了官网及网络内容. 安装Vue 1.使用npm安装vue: npm install vue 2.下载使用js文件: https://vuejs.org/js/vue ...
- Java集合总结—再也不怕面试问到集合了
Java集合总结 1.常见的集合 Map接口和Collection接口是所有集合框架的父接口: Collection接口的子接口包括:Set接口和List接口 Map接口的实现类主要有:HashMap ...
- java23种设计模式(一)工厂方法模式
在说工厂方法模式之前,先了解一下简单工厂模式.工厂方法模式其实是在简单工厂上面做了一些增强. 简单工厂模式:有一个专门的类来生产其他类的实例,生产的这些实例有一个共同父类.这个跟我们的多态有一点像. ...
- GUI tkinter (Menu)菜单项篇
"""添加顶层菜单:1.我们可以使用Menu类来新建一个菜单,Menu和其他的组件一样,第一个是parent,这里通常可以为窗口2.然后我们可以用add_command方 ...
- DG常用运维命令及常见问题解决
DG常见运维命令及常见问题解决方法 l> DG库启动.关闭标准操作Dataguard关闭1).先取消日志应用alter database recover managed standby data ...
- VUE--插值的操作
一.vue常见的指令 v-once:保留第一次渲染结果 v-html :把html代码解析,只显示内容 v-pre :原样输出 v-cloak :解决文本闪烁问题 v-text :显示文本 二.v-b ...