Java中的静态代理与动态代理(Proxy.newProxyInstance)

代理模式是23种设计模式中比较常用的一种,属于结构型设计模式。代理的实现方式有三种:静态代理、动态代理以及使用 cglib 或 bytebuddy 开源库创建的代理,这里我们关注前两种常用的代理方式。

一、静态代理

静态代理中,代理对象要与目标对象实现相同的接口, 然后在相同的方法来调用目标对象的方法。

二、动态代理

这里介绍基于Proxy.newProxyInstance()方法的动态代理实现。动态代理主要用来做方法的增强,让开发者在不修改源代码的情况下,在方法执行前后做任何想做的事情(甚至不去执行这个方法),因为在InvocationHandler的invoke()方法中,你可以直接获取正在调用方法对应的Method对象,可以用来添加调用日志,做事务控制等。动态,指的是代理类是在程序运行时创建的,而不是在程序运行前手动编码来定义代理类的。这些动态代理类是在运行时候根据我们在JAVA代码中的"指示"动态生成的。动态代理的实现原理大概可以分为三步:1、为目标类创建代理类的字节码文件;2、使用ClassLoader将字节码文件加载到JVM;3、创建代理类实例对象,执行对象的目标方法;

1、定义目标接口

java 复制代码
// 目标接口(目标对象的类要实现的接口)
public interface GameService {
    String beginGame(String s);
    void playGame(String s);
}

2、定义目标对象的类

java 复制代码
// 目标对象的类(被代理的对象的类)
public class GameServiceImpl implements GameService {

    public String beginGame(String game) {
        System.out.println("玩家开始游戏:" + game);
        return game;
    }

    public void playGame(String game) {
        System.out.println("玩家进行游戏: " + game);
    }
}

3、实现InvocationHandler接口,重写invoke()方法,即定义处置器的类

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 调用处置器:1、承载目标对象;2、调用目标对象的方法
 */
public class GameInvocationHandler implements InvocationHandler {

    // 目标对象
    private final GameService target;

    public GameInvocationHandler(GameService target) {
        this.target = target;
    }

    /**
     * 调用目标对象的方法
     *
     * @param proxy  当前代理对象本身
     * @param method 被调用的方法对象
     * @param args: 调用方法时传递的参数数组
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 前置逻辑(前置增强)
        System.out.println("before--" + method.getName());

        // 调用目标对象的方法
        Object result = method.invoke(target, args);

        // 后置逻辑(后置增强)
        System.out.println("after--" + method.getName());

        return result;
    }
}

4、调用Proxy.newProxyInstance()方法生成和使用动态代理对象

java 复制代码
import java.lang.reflect.Proxy;

/**
 * 动态代理的主要优势在于其能够在不修改原有代码的基础上,对目标对象的功能进行增强或修改。
 * 但需要注意的是,JDK动态代理只能为实现接口的类创建代理实例,无法直接代理没有实现接口的类 !!!
 */
public class Main {

    public static void main(String[] args) {

        // 创建目标对象
        GameService gameServiceImpl = new GameServiceImpl();

        // 创建代理对象方法调用处理器
        GameInvocationHandler handler = new GameInvocationHandler(gameServiceImpl);

        // Proxy.newProxyInstance()是Java反射API中用于创建动态代理对象的核心方法(运行时动态生成代理类,无需预先编码)
        GameService proxy = (GameService) Proxy.newProxyInstance (
                // 指定一个类加载器,用于加载生成的代理类到JVM(TODO 不必是目标类型加载器,如可以使用当前类)
                /// gameServiceImpl.getClass().getClassLoader(),
                Main.class.getClassLoader(),
                // 指定代理类需要实现的一组接口和方法。动态代理只能基于接口工作,因此目标对象至少需要实现一个接口,
                // 代理类会实现这些接口,并且所有的接口方法调用都会被转发给InvocationHandler处理。
                gameServiceImpl.getClass().getInterfaces(),
                // 指定代理对象方法调用处置器(InvocationHandler接口的实现类),代理对象方法的调用会被转发到该处置器的invoke()方法。
                // 这是动态代理的核心部分,它允许我们在实际调用业务逻辑之前或之后插入额外的行为,如性能监控、日志记录等。
                handler
        );

        // 通过代理对象调用目标对象的方法
        proxy.beginGame("猫鼠游戏");
        proxy.playGame("猫鼠游戏");
    }
}
相关推荐
Wang's Blog7 小时前
Java框架 SpringCloud 快速入门: 服务拆分之服务远程调用
java·开发语言·spring cloud
努力努力再努力wz7 小时前
【CUDA入门系列】CUDA编程实战:设备选择、显存管理、Kernel、Stream 与 Event 全流程梳理
开发语言·c++
Wang's Blog7 小时前
Java框架 SpringCloud 快速入门: 路由断言工厂
java·spring·spring cloud
ym hyd 1117 小时前
门诊挂号系统源码 Java+SpringBoot+Vue3 前后分离
java·vue.js·spring boot·毕设
杨丰玮4187 小时前
C语言核心语法
java·c语言·开发语言·学习方法
ttwuai7 小时前
Go开源后台管理系统推荐:README、源码和License应该怎么验
开发语言·golang·开源
Java后端的Ai之路7 小时前
Python 进阶探索30 - Python中的装饰器
开发语言·人工智能·python·文件处理·装饰器模式
沫璃染墨7 小时前
《从零入门Linux系统篇(五十八):线程篇·十一——线程安全与死锁详解:从可重入到多锁管理》
linux·服务器·开发语言·c++·驱动开发·安全·架构
海宇AI7 小时前
Java数据工程:利用海宇婚恋风险报告优化高端婚恋实名与涉诉核验合规体验
java·人工智能
CEZ7 小时前
中小企业上 WMS 该先上哪几块:JeeWMS 开源 Java 仓库管理系统的分批上线清单
java·开源