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,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...
随机推荐
- Bumblebee服务网关之统一请求验证
对于微服务网关来说,统一请求验证是一个比较重要和常用的功能,通过网关验证后台服务就无须关注请求验证:对于多语言平台的服务而言制定验证方式和变更验证配置都是一件比较繁琐和工作量大的事情.Bumblebe ...
- Mongoose: aggregate聚合 $group使用说明
aggregate聚合是通过管道操作实现的.聚合管道里的每一步输出,都会作为下一步的输入,每一步在输入文档执行完操作后生成输出文档. 聚合管道: $project 修改输入文档的结构.可以用来重命名 ...
- Java通信——获取自己IP
获取自己的IP地址 import java.net.InetAddress; import java.net.UnknownHostException; public class getip { pu ...
- Ubuntu16.04安装java6(jdk 1.6)
目录 下载安装包 安装 移动到指定位置并设置版本 设置环境变量 切换java版本 下载安装包 先到官网下载安装包. 安装 输入命令 chmod 777 jdk-6u45-linux-x64.bin s ...
- 最新打赏正版V15微信视频打赏源码 带(百倍)暗雷 N秒试看 自动切换域名 自动防封
免签支付域名防封随机跳转盒子推广设置试看N秒百倍 平台搭建:乌龟-源码科技QQ:64430146 全新版本 V15打赏版本功能介绍: 特别注意: 新增加功能!!!! 1.包括V14所有功能(除个别因优 ...
- 算法学习之剑指offer(八)
题目一 题目描述 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100.但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数).没 ...
- 解决vue组件内前置路由守卫beforeRouteEnter无法获取上下文this
问题描述 vue框架,只有在报名页面报名成功,然后自动跳转到订单详情,才弹出一个引流弹窗,其他情况均不弹出,我就想到使用vue 的组件内前置守卫beforeRouteEnter来实现.beforeRo ...
- 细谈Mysql事务
文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. 上一篇着重谈到了MySQL锁的概念,里面谈到了事务的概念,其实大部分开发者对于事务肯定不陌生,事务的概念其实就 ...
- 算法---区间K大数查找 Java 蓝桥杯ALGO-1
import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(Strin ...
- 你不知道的Canvas(一)
Canvas基础 一.Canvas是什么 Canvas是一个可以使用脚本(通常为JavaScript来绘制图形的HTML) 元素.例如,它可以用于绘制图表.制作图片构图或者制作简单的动画,主要用来绘制 ...