Java中JDK动态代理详解
1.动态代理是什么?有什么用?
动态代理(Dynamic Proxy)是一种在【运行时】动态生成代理对象的技术。
它是一种设计模式,用于在不修改原始对象的情况下,通过代理对象来间接访问原始对象,并在访问前后执行额外的操作。
2.动态代理中的角色
-
目标对象【待增强的对象】
-
代理对象【增强过后的对象】(也就是我们会使用的对象)
3.代理模式
-
静态代理
在【编译期间】就确认好代理关系
-
动态代理
在【运行期间】确认好代理关系
-
jdk
动态代理(反射)-
目标接口
-
目标对象
implements
目标接口 -
代理对象
implements
目标接口
代理对象 和 目标对象 是平级关系
-
-
cglib
代理(继承)-
目标对象
-
代理对象
extends
目标对象
代理对象 和 目标对象 是父子关系
-
-
4.代理模式的应用
-
功能增强
-
控制访问
5.如何使用、实现?
1.目标接口
/**
* 目标接口
*/
interface Target{
void eat();
}
2.目标对象(原始对象)implements
目标接口
/**
?* 目标对象(原始对象)
?*/
?static class TargetDog implements Target{
? ? ?@Override
? ? ?public void eat() {
? ? System.out.println("狗在吃。。。。");
? ? }
?}
3.代理对象 implements
目标接口
Q:怎么实现功能增强
A:Java中提供了两个核心的类 API来实现。
代理类 proxy
invocationHandler类
来实现动态代理
?public static void main(String[] args) {
??
? ? ? ? ?Target proxy = (Target) Proxy.newProxyInstance(TestJdkAgent.class.getClassLoader(),
? ? ? ? ? ? ? ? ?TargetDog.class.getInterfaces(),
? ? ? ? ? ? ? ? ?new InvocationHandler() {
? ? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ? ?public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? ? ? ? ? ? ? ? ? ? ?// 1.前置增强
? ? ? ? ? ? ? ? ? ? ? ? ?System.out.println("before!!!");
? ? ? ? ? ? ? ? ? ? ? ? ?//2.目标对象的原始方法
? ? ? ? ? ? ? ? ? ? ? ? ?//现在是 方法.invoke(obj, arg) === 原来是 目标对象.方法
? ? ? ? ? ? ? ? ? ? ? ? ?Object invoke = method.invoke(new TargetDog(), args);
? ? ? ? ? ? ? ? ? ? ? ? ?return invoke;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? });
? ? ? ? ?proxy.eat();
? ? }
Proxy.newProxyInstance()
: 来实例化一个经过反射下的一个代理实例,需要要传递的3个参数,如下
-
ClassLoader loader
:类加载器 ,因为我们生成的这个代理对象是没有.java源文件的,是在运行时直接生成的一个.class的二进制字节码文件,他既然直接生成一个.class二进制文件的,那么它是需要去加载,被加载到Java的运行时数据区,所以他就需要一个类加载器PS:什么是类加载器?有什么作用?
在Java中,
.class
文件是Java编译器生成的二进制字节码文件,它包含了Java类的定义和方法。当我们运行一个Java程序时,Java虚拟机(JVM)需要加载这些.class
文件到其运行时数据区。类加载器(ClassLoader)的主要任务就是负责将
.class
文件中的二进制数据读入到内存中,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的Java类型。这个过程被称为类加载机制。类加载器还有另一个重要的功能,那就是隔离加载类的命名空间。不同的类加载器可以加载同名的类,但是由不同的类加载器加载的类属于不同的命名空间,互不干扰。
因此,类加载器在Java程序运行中起着至关重要的作用。它不仅负责加载类,还负责对类进行校验,解析和初始化,以及隔离加载类的命名空间。
-
@NotNull Class<?>[] interfaces
:接口类型,有两种方法:-
因为已经知道了接口的类型,直接
Target.class
-
new TargetDog().getClass().getInterfaces()
:通过目标对象去获得,因为目标对象实现了接口,先得到目标对象TargetDog().getClass()
,然后再.getInterfaces()
,会基于反射去拿到接口上的信息;(反射就是可以去拿到你的类的一些原始信息然后操作等)
-
-
@NotNull InvocationHandler h
:InvocationHandler接口 ,写代理的逻辑,直接new InvocationHandler()
接口,重写他的invoke
方法,就是在invoke
方法中写代理的逻辑是什么。invoke
有3个参数:Object proxy, Method method, Object[] args
:-
proxy
:就是生成的代理对象 -
method
:当前的方法中,我要增强的那个方法method.invoke(new TargetDog(),args)
== 原来的TargetDog().eat()
-
args
:参数就是method
方法中的参数
PS:因为代理对象?和?目标对象?是平级关系,所以可以直接进行强转
Target proxy = (Target) Proxy.newProxyInstance()
-
代理模式的应用体现
-
功能增强体现在:在原有的基础上,可以在
eat()
方法之前加上其他方法;?// 1.前置增强 ?System.out.println("before!!!"); ?//2.目标对象的原始方法 ?Object invoke = method.invoke(new TargetDog(), args); ?-------------------------------- ?before!!! ?狗在吃。。。。
-
控制访问体现在:可以直接不要原始的方法,只有新增的方法;
?// 1.前置增强 ?System.out.println("before!!!"); ?------------------------------------ ?before!!!
4.完整代码
?package org.example.testAgent;
??
?import java.lang.reflect.InvocationHandler;
?import java.lang.reflect.Method;
?import java.lang.reflect.Proxy;
??
?public class TestJdkAgent {
??
? ? ?public static void main(String[] args) {
??
? ? ? ? ?Target proxy = (Target) Proxy.newProxyInstance(TestJdkAgent.class.getClassLoader(),
? ? ? ? ? ? ? ? ?TargetDog.class.getInterfaces(),
? ? ? ? ? ? ? ? ?new InvocationHandler() {
? ? ? ? ? ? ? ? ? ? ?@Override
? ? ? ? ? ? ? ? ? ? ?public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? ? ? ? ? ? ? ? ? ? ?// 1.前置增强
? ? ? ? ? ? ? ? ? ? ? ? ?System.out.println("before!!!");
? ? ? ? ? ? ? ? ? ? ? ? ?//2.目标对象的原始方法
? ? ? ? ? ? ? ? ? ? ? ? ?//现在是 方法.invoke(obj, arg) === 原来是 目标对象.方法
? ? ? ? ? ? ? ? ? ? ? ? ?Object invoke = method.invoke(new TargetDog(), args);
? ? ? ? ? ? ? ? ? ? ? ? ?return invoke;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? });
? ? ? ? ?proxy.eat();
? ? }
? ? ?/**
? ? ? * 目标接口
? ? ? */
? ? ?interface Target{
? ? ? ? ?void eat();
? ? }
? ? ?/**
? ? ? * 目标对象(原始对象)
? ? ? */
? ? ?static class TargetDog implements Target{
??
? ? ? ? ?@Override
? ? ? ? ?public void eat() {
? ? ? ? ? ? ?System.out.println("狗在吃。。。。");
? ? ? ? }
? ? }
?}
??
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!