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,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...
随机推荐
- ng 图片的引用
对于图片的引用有两种类型 本地 业务逻辑中(使用url) 本地中图片需要存放在静态资源夹assets中下新建的文件夹images文件夹中 eg:images文件夹中有一张01.png 的图片 显示本地 ...
- Android自动跳过app开屏广告
跳过开屏广告,体验流畅人生 开屏广告 是应用启动时显示的广告,一般右下角(或右下角)有倒计时跳过,不主动点击就会 等待3到5秒 后再进入App 自动跳过 是跳过应用的开屏广告的App 一图胜千文,来我 ...
- python获取全国各个城市pm2.5、臭氧等空气质量
随着国家发展,中国很多城市的空气质量其实并不好,国家气象局会有实时统计,但是要去写爬虫爬取是十分麻烦的事情,并且官方网站也会做一些反爬虫措施,所以实现起来比较麻烦,最好的办法就是使用现成的免费接口,空 ...
- PCA 算法核心:高维度向量向低维度投影
Principal Component Analysis:主成分分析 步骤 5 步: 1.去平均值,也就是将向量中每一项都减去各自向量的平均值 2.计算矩阵的方差,协方差,特征值, 3,.把特征值从大 ...
- 【TencentOS tiny】深度源码分析(2)——调度器
温馨提示:本文不描述与浮点相关的寄存器的内容,如需了解自行查阅(毕竟我自己也不懂) 调度器的基本概念 TencentOS tiny中提供的任务调度器是基于优先级的全抢占式调度,在系统运行过程中,当有比 ...
- Spring Boot2 系列教程(十一)Spring Boot 中的静态资源配置
当我们使用 SpringMVC 框架时,静态资源会被拦截,需要添加额外配置,之前老有小伙伴在微信上问松哥 Spring Boot 中的静态资源加载问题:"松哥,我的 HTML 页面好像没有样 ...
- Java11月18日
动手动脑 实验一: 1. 下边的程序运行结果是什么? 2.你如何解释会得到这样的输出? 3.计算机是不会出错的,之所以得到这样的运行结果也是有原因的那么从这些运行结果中,你能总结出Java的哪些语法特 ...
- LeetCode_232-Implement Queue using Stacks
题意是使用栈实现队列:队列是先进先出,后进后出. class MyQueue { public: /** Initialize your data structure here. */ MyQueue ...
- CF991D Bishwock
CF991D Bishwock 题目描述 给一个$2\times n$的网格,上面一些位置以及被覆盖上了.现在你有一种形状为L的小块,每个由三个小格组成,构成L型 现在问你,当前的网格最多还能摆多少小 ...
- linux下 Mysql 安装使用
.安装mysql sudo apt-get install mysql-server mysql-client sudo service mysql restart 2.5.7版本以上修修改updat ...