动态代理(Dynamic Proxy)是一种在【运行时】动态生成代理对象的技术。
它是一种设计模式,用于在不修改原始对象的情况下,通过代理对象来间接访问原始对象,并在访问前后执行额外的操作。
目标对象【待增强的对象】
代理对象【增强过后的对象】(也就是我们会使用的对象)
静态代理
在【编译期间】就确认好代理关系
动态代理
在【运行期间】确认好代理关系
jdk
动态代理(反射)
目标接口
目标对象 implements
目标接口
代理对象 implements
目标接口
代理对象 和 目标对象 是平级关系
cglib
代理(继承)
目标对象
代理对象 extends
目标对象
代理对象 和 目标对象 是父子关系
功能增强
控制访问
/**
* 目标接口
*/
interface Target{
void eat();
}
implements
目标接口/**
?* 目标对象(原始对象)
?*/
?static class TargetDog implements Target{
? ? ?@Override
? ? ?public void eat() {
? ? System.out.println("狗在吃。。。。");
? ? }
?}
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!!!
?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("狗在吃。。。。");
? ? ? ? }
? ? }
?}
??