【JavaWeb笔记】静态代理、动态代理还有class jdk.proxy1.$Proxy0 cannot be cast to class……

本文详细介绍了动态代理与静态代理的概念,通过实例说明动态代理如何基于接口为任何实现了Eat接口的类提供代理服务,同时揭示了动态代理的实现原理尚未深入讨论。

明白了一点反射+一点静态代理

网上看了几个动态代理的视频,感觉讲得都不是很清楚

实践出真知还是自己手搓了一些,结果就好多坑。

假如一个大老板想吃饭,但又觉得煮饭洗碗太麻烦了,就把煮饭希望这些事都扔给代理类【仆人】处理,自己实现吃的功能即可

按照上图分别定义三个类

public interface Eat {
    // Eat接口类
    void eat();
}
public class Master implements Eat{

    @Override
    public void eat(){
        System.out.println("主人:干饭!");
    }
}

这里一定要把Eat写成一个接口类!因为动态代理只能基于接口!!!

如果按照静态代理的写法,【仆人(Servant)】类可以简单写成

public class StaticServant implements Eat{
    Master targetMaster; // 代表被代理的对象,本例即仆人伺候的主人
    StaticServant(Master targetObj){
        this.targetMaster = targetObj; // 仆人必须有个伺候的主人吧,所以构造时传个主人
    }

    // 这里就是代理的主体部分了,仆人帮主人煮饭洗碗...
    @Override
    public void eat(){
        System.out.println("仆人:买菜煮饭……");
        this.targetMaster.eat();
        System.out.println("仆人:收拾饭桌洗碗……");
    }
}

此时再main方法中让仆人代理主人,再让仆人代理主人执行一下eat方法

public static void main(String[] args) {
        Master m1 = new Master();
        StaticServant ss1 = new StaticServant(m1);
        ss1.eat();
}

就可以得到想要的输出了

不过这种方式,假如仆人想给【公司老总类】洗碗做饭,就又得给自己加个【公司老总类】的成员变量,假如他又想给【富二代少爷类】洗碗做饭,就又得给自己加个【富二代少爷类】的成员变量……

这样肯定是不行的,必须有什么不这么笨的方法

于是动态代理应运而生

不管被代理类具体是什么类,只要这个类实现了Eat接口,那么动态代理就都能以相同方式给它代理了。(为什么是“只要这个类实现了Eat接口”呢,因为动态代理是基于接口的!!!)

实现动态代理的话,接口和被代理类(主人类)都不需要动,只需要修改一下代理。

public class DynamicServant implements InvocationHandler {
    // 被代理的对象(这里就不是Master了,因为要换成富二代少爷也能代理……)
    Object targetObj; 

    DynamicServant(Object targetObj){
        // 这个构造函数不用多讲了吧
        this.targetObj = targetObj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 仆人在主人吃饭前做的动作放这
        System.out.println("仆人:买菜煮饭……"); 
        // 这是被代理类要做的事(即吃饭)
        Object invoke = method.invoke(this.targetObj, args);
        // 仆人在主人吃饭后做的动作
        System.out.println("仆人:收拾饭桌洗碗……");
        return invoke;
    }
}

你没猜错,这里的invoke就是来实现代理功能的。(那要怎么调用这个代理呢?这个先不急后面再讲)

上面invoke方法,除了仆人要做的两件事(吃饭前和吃饭后做的事)你可以按照自己需求去更改,其它地方都可以直接照抄(假如你不想懂的话……然而其实懂了也是照抄),包括invoke这个方法名,和里面的所有参数。

接着讲怎么调用这个代理呢。

在main函数中,按照原先的逻辑,应该是先new一个master,然后再new一个servant,然后servant调用eat方法,结束!

于是我们写下如下代码(这个代码有坑,大家别先急着运行)。

public static void main(String[] args) {
    // 首先new一个master
    Master m1 = new Master();
    // 接着new一个servant
    DynamicServant s1 = (DynamicServant) Proxy.newProxyInstance(
        m1.getClass().getClassLoader(), 
        m1.getClass().getInterfaces(), 
        new DynamicServant(m1)
    );
    // 最后用new出来的servant调用eat方法
    s1.eat();
}

这里的servant的创建方法有点奇怪,因为是动态代理。

我们需要用Proxy.newProxyInstance来创建一个servant实例,

第一个参数填m1(即被代理的对象),后面的.getClass().getClassLoader()直接照抄(如果看方法名,可以知道这是要获得m1的类加载器),

第二个参数同理,m1后面的直接照抄(同理可知此参数要的是m1的接口——在这里是Eat接口),

第三个参数类型要new一个servant。(看第三个参数的形参名是InvocationHandler,是不是很眼熟,就是DynamicServant实现的那个接口(看DynamicServant代码的第一行),所以第三个参数要填DynamicServant的一个实例)

因为Proxy.newProxyInstance返回的是Object类型,需要手动强转一下变成想要的Servant。

最后用生成的servant调用eat方法。

然后成功发现,报错了。

我们发现s1里根本没有eat方法。

先退一步,删除s1.eat()这一行。

编译器没有报错,但是一运行还是出现了报错信息

Exception in thread "main" java.lang.ClassCastException: class jdk.proxy1.$Proxy0 cannot be cast to class com.test.proxyTest.DynamicServant (jdk.proxy1.$Proxy0 is in module jdk.proxy1 of loader 'app'; com.test.proxyTest.DynamicServant is in unnamed module of loader 'app')
	at com.test.proxyTest.Master.main(Master.java:13)

这就是为什么我前面强调了两次“动态代理只能基于接口!!!”(参考动态代理之: com.sun.proxy.$Proxy0 cannot be cast to 问题_com.sun.proxy。$proxy0 cannot be cast to_codingCoge的博客-优快云博客

可能以静态代理的思维我们确实是要“创建静态代理类、使用静态代理类实例调用eat方法”,然而在动态代理中,Proxy.newProxyInstance返回的不是代理类实例(仆人),而是返回 【被代理的接口】(Eat实例),在本例中,Proxy.newProxyInstance返回的实例应该用Eat来接收。即

public static void main(String[] args) {
    // 首先new一个master
    Master m1 = new Master();
    // 接着new一个Eat
    Eat e1 = (Eat) Proxy.newProxyInstance(
        m1.getClass().getClassLoader(), 
        m1.getClass().getInterfaces(), 
        new DynamicServant(m1)
    );
    // 最后用new出来的Eat调用eat方法
    e1.eat();
}

运行!

终于得到想要的结果

而利用动态代理,无论吃饭的是公司老总还是老总他儿子,只要是实现了Eat接口的类,都可以用此方法代理了。

=============================分割线==================================

上面都只说了怎么实现动态代理,没有提及实现原理。

下面来推敲一下实现原理

🤣原理我还没弄懂,看官们想看的再等等,弄懂了再来吹吹

. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.5.2.RELEASE) 2025-06-19 17:11:36.275 INFO 147976 --- [ main] com.score.boot.StartApplication : Starting StartApplication on LAPTOP-HIH2EG0I with PID 147976 (started by asus in D:\Javaweb-学生成绩管理系统\2-源码资料\2-代码\StudentScore) 2025-06-19 17:11:36.276 INFO 147976 --- [ main] com.score.boot.StartApplication : No active profile set, falling back to default profiles: default 2025-06-19 17:11:36.317 INFO 147976 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@740773a3: startup date [Thu Jun 19 17:11:36 CST 2025]; root of context hierarchy 2025-06-19 17:11:36.743 ERROR 147976 --- [ main] o.s.boot.SpringApplication : Application startup failed java.lang.IllegalStateException: Cannot load configuration class: com.score.boot.StartApplication at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:403) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:249) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:281) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:125) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:686) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:524) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at com.score.boot.StartApplication.main(StartApplication.java:14) ~[classes/:na] Caused by: java.lang.ExceptionInInitializerError: null at org.springframework.context.annotation.ConfigurationClassEnhancer.newEnhancer(ConfigurationClassEnhancer.java:122) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:110) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:393) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] ... 12 common frames omitted Caused by: java.lang.IllegalStateException: Unable to load cache item at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:79) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.KeyFactory$Generator.create(KeyFactory.java:221) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.KeyFactory.create(KeyFactory.java:174) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.KeyFactory.create(KeyFactory.java:153) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.proxy.Enhancer.<clinit>(Enhancer.java:73) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] ... 15 common frames omitted Caused by: java.lang.ExceptionInInitializerError: null at org.springframework.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:243) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:329) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:93) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na] at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] ... 22 common frames omitted Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @569cfc36 at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:388) ~[na:na] at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:364) ~[na:na] at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:312) ~[na:na] at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:203) ~[na:na] at java.base/java.lang.reflect.Method.setAccessible(Method.java:197) ~[na:na] at org.springframework.cglib.core.ReflectUtils$1.run(ReflectUtils.java:54) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] at java.base/java.security.AccessController.doPrivileged(AccessController.java:319) ~[na:na] at org.springframework.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:44) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE] ... 30 common frames omitted 2025-06-19 17:11:36.745 INFO 147976 --- [ main] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@740773a3: startup date [Thu Jun 19 17:11:36 CST 2025]; root of context hierarchy 2025-06-19 17:11:36.745 WARN 147976 --- [ main] ationConfigEmbeddedWebApplicationContext : Exception thrown from LifecycleProcessor on context close java.lang.IllegalStateException: LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@740773a3: startup date [Thu Jun 19 17:11:36 CST 2025]; root of context hierarchy at org.springframework.context.support.AbstractApplicationContext.getLifecycleProcessor(AbstractApplicationContext.java:417) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1002) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:961) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:794) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:325) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at com.score.boot.StartApplication.main(StartApplication.java:14) ~[classes/:na] 2025-06-19 17:11:36.745 ERROR 147976 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Destroy method on bean with name 'org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory' threw an exception java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@740773a3: startup date [Thu Jun 19 17:11:36 CST 2025]; root of context hierarchy at org.springframework.context.support.AbstractApplicationContext.getApplicationEventMulticaster(AbstractApplicationContext.java:404) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.ApplicationListenerDetector.postProcessBeforeDestruction(ApplicationListenerDetector.java:97) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:253) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:578) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:554) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:961) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:523) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:968) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1033) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1009) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:961) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE] at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:794) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:325) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE] at com.score.boot.StartApplication.main(StartApplication.java:14) ~[classes/:na] 进程已结束,退出代码为 1
最新发布
06-20
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值