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,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...
随机推荐
- C#的FTP服务器源代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Bugku练习
首先我们拿到了一个admin.exe的文件,根据惯例我先用查一下有没有加壳: ???这是怎么回事??? 我于是用了winhex打开看了一下,发现 这不就是data协议吗~~~,把后面的base64解密 ...
- idea 新建项目 coding上新建项目 idea推送到coding
1. 注册coding a. 首先在(https://coding.net)上创建项目 ps:跳过注册 ASP.NET Core EF创建模型(包含属性和排除属性、主键、生成的值)
1.什么是Fluent API? EF中内嵌的约定将POCO类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体映射到约定指示外的其他对象,所以Fluent API和注解都是一种方法,这两种方法 ...
- AppBoxFuture: 服务模型的在线调试
框架内的服务模型(ServiceModel)用于处理各类业务逻辑(如最简单的CRUD操作),在设计时以类似于伪代码的形式存在,发布时后端会通过Roslyn转换并编译为运行时代码.为了方便开发者更简 ...
- [UWP]使用CompositionLinearGradientBrush实现渐变画笔并制作动画
1. 什么是 CompositionBrush CompositionBrush(合成画笔)是操作可视化层时用于绘制 SpriteVisual 区域的画笔. 使UWP 应用时可以选择使用 XAML 画 ...
- 【python数据分析实战】电影票房数据分析(二)数据可视化
目录 图1 每年的月票房走势图 图2 年票房总值.上映影片总数及观影人次 图3 单片总票房及日均票房 图4 单片票房及上映月份关系图 在上一部分<[python数据分析实战]电影票房数据分析(一 ...