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

原文地址:https://www.cnblogs.com/dengchengchao/p/11823687.html

时间: 2024-10-02 23:01:41

Spring 动态代理 之 but was actually of type 'com.sun.proxy.$Proxy14 Exception的相关文章

快速学会Spring动态代理原理

本文主要是讲述快速学会Spring动态代理原理,更多Java技术知识,请登陆疯狂软件教育官网. 一.为什么要使用动态代理 当一个对象或多个对象实现了N中方法的时候,由于业务需求需要把这个对象和多个对象的N个方法加入一个共同的方法,比如把所有对象的所有方法加入事务这个时候有三种方法: 方法一:一个一个对象一个一个方法去加,很显然这个方法是一个比较笨的方法. 方法二:加一个静态代理对象将这个静态代理对象实现要加事务对象的接口.然后在静态代理对象里面每个方法里面加上事务. 方法三:使用动态代理对象,进

spring动态代理的cglib方法

1.被代理类Person.java 1 package com.xiaostudy; 2 3 /** 4 * @desc 被代理类 5 * 6 * @author xiaostudy 7 * 8 */ 9 public class Person { 10 11 public void add() { 12 System.out.println("add()>>>>>>>>"); 13 } 14 15 public void update(

Spring 动态代理基础知识

Spring AOP 使用动态代理技术在运行期织入增强的代码,为了揭示 Spring AOP 底层的工作机理,有必要学习涉及的 Java 知识.Spring AOP 使用了两种代理机制:一种是基于 JDK 的动态代理:另一种是基于 CGLib 的动态代理.之所以需要两种代理机制,很大程度上是因为 JDK 本身只提供接口的代理,而不支持类的代理. 1.带有横切逻辑的实例 下面通过具体化代码实现一个性能监视横切逻辑,并通过动态代理技术对此进行改造.在调用每一个目标类方法时启动方法的性能监视,在目标类

spring动态代理

接下来我们来体会下动态代理带给我们的便利 package aop006; public interface Girl { public void KFC(String datetime); public void meet(String datetime); } package aop006; /* * */ public class Girl1 implements Girl{ public void KFC(String datetime){ System.out.println("[核心业

关于Spring动态代理

默认情况下,spring会按照如下规则生成代理: 当类有实现接口,spring会生成JdkDynamicAopProxy代理 当类没有实现接口,spring会生成CglibAopProxy代理 如果想强制spring生成CglibAopProxy代理,可以<aop:config proxy-target-class="true"> </aop:config> 当需要使用CGLIB代理和@AspectJ自动代理支持,请按照如下的方式设置 <aop:aspec

Spring框架_代理模式(静态代理,动态代理,cglib代理)

共性问题: 1. 服务器启动报错,什么原因? * jar包缺少.jar包冲突 1) 先检查项目中是否缺少jar包引用 2) 服务器: 检查jar包有没有发布到服务器下:                                      用户库jar包,需要手动发布到tomcat. (每次新建项目) 3) 重新发布项目 * 配置文件错误 (web.xml / struts.xml /bean.xml /hibernate.xml / *.hbm.xml) 明确的提示 * 端口占用 * we

spring aop 动态代理批量调用方法实例

今天项目经理发下任务,需要测试 20 个接口,看看推送和接收数据是否正常.因为对接传输的数据是 xml 格式的字符串,所以我拿现成的数据,先生成推送过去的数据并存储到文本,以便验证数据是否正确,这时候要批量调用这些同名方法,我觉得这里可以发展成有潜力的代码. 推送比较好做数据,队友们都写好代码,但是有个问题,方法要的值都大致相同,封装的方式不一致,多人开发,有的封装对象里面,有的直接使用 Map.get(),唉,一千个人一千个哈姆雷特嘛,只好利用反射和动态代理节省自己的代码量,而且这种方式练练手

CgLib动态代理学习【Spring AOP基础之一】

如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发现其参数类型是ClassLoader classLoader, Class<?>[] interface, InvocationHandler handler, 只支持根据接口实现代理类,如果所有代码都是自己掌控,当然没有问题.所有的业务逻辑均抽象出接口,然后所有的业务类实现接口,这样所有的业务类

WebServcie结合Spring结合动态代理进行抽象封装以及性能优化

webService抽象客户端封装.动态代理提升使用性能 1. 什么是webService webService就是在web上提供非相关系统与系统之间进行数据交互的一种服务.通过实现定义好的wsdl借口配置文件,进行约定以及调用. 在企业的内部系统中很常见,尤其是比较大型的企业,有数十种内部使用的系统,之间的通信基本上都是使用webService. 通俗点说就是:你要调用别人的服务,你就通过wsdl生成客户端代码,方便进行直接调用. 你需要被别人调用,你就通过wsdl生成服务端代码,方便对方系统