Spring:代理模式之静态代理&动态代理

前言

其实之前写过类似一篇了,重新具体的总结一下

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP 和 SpringMVC】面试必定

代理模式的分欸:

  • 静态代理
  • 动态代理

代理的原型:

静态代理

角色分析:

  • 抽象角色:一般会用接口或者抽象类来解决(这里指的是外卖平台和餐厅的共同目标"赚钱")
  • 真实角色:被代理的角色(厨师、餐厅)
  • 代理角色:代理真实角色,代理真实角色后,通常会进行一些附属操作...(外卖平台)
  • 客户:访问代理对象的人!(这里指的是要吃饭的人)

代理模式的好处:

  • 可以使真实角色的操作更加纯粹!,不用去关注一些公共的业务
  • 公共业务就交给了代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!

缺点:

  • 一个真实角色就会产生一个代理角色,代码量翻倍~开发效率会变低
  • 缺点解决的办法就是动态代理

代码步骤

  1. 接口
java 复制代码
// 卖吃的(赚钱)
public interface Sell {
    public  void sell();
}
  1. 真实角色
java 复制代码
// 厨师:真实角色
public class Chef implements Sell{


    @Override
    public void sell() {
        System.out.println("cike_y真实厨师:快来我这里点吃的吧");
    }
}
  1. 代理角色
java 复制代码
// 少用继承(局限性很大)、多用组合的方式
public class proxy implements Sell{
    @Override
    public void sell() {
        System.out.println("外卖平台开始代理厨师的话:");
        chef.sell();//帮真实角色(厨师)代理他说的话
        // 下面是外卖平台(中介代理)的附属操作
        System.out.println("外卖平台(中介代理)开始附属以下操作了:\n");
        SeeRider();
        SendOrder();
        GiveMoney();
        TakeCommission();
        TakeRiderFee();
    }


    // 组合
    private Chef chef;

    public proxy() {
    }

    public proxy(Chef chef) {
        this.chef = chef;
    }

    // setProxy(){..} 没必要set注入

    // 代理可以做一些附属的事情
    public void SeeRider(){// 找骑手
        System.out.println("帮你寻找骑手中......");
    }

    // 收取佣金
    public void TakeCommission(){
        System.out.println("外卖平台收取佣金");
    }

    // 骑手费用
    public void TakeRiderFee(){
        System.out.println("给骑手小费");
    }

    // 发送客户订单
    public void SendOrder(){
        System.out.println("发送客户订单");
    }

    // 给餐厅钱
    public void GiveMoney(){
        System.out.println("给餐厅钱");
    }
}
  1. 客户端访问代理角色
java 复制代码
public class Client {
    public static void main(String[] args) {
        // 想点这家餐厅做的菜
        Chef chef = new Chef();
        // 外卖平台,帮你通知厨师做菜,但是外卖平台(代理)会做一些附属操作
        proxy proxy = new proxy(chef);

        // 你不用面对厨师、直接找外卖平台即可!
        proxy.sell();
    }
}

加深理解

在一个增删改查的业务

  1. 接口类(公共调用的方法)
java 复制代码
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
  1. 真实角色(业务实现类)
java 复制代码
public class UserServiceImpl {
    public void add() {
        System.out.println("添加用户");
    }
    public void delete() {
        System.out.println("删除用户");
    }
    public void update() {
        System.out.println("修改用户");
    }
    public void query() {
        System.out.println("查询用户");
    }
}
  1. 代理角色(代理真实角色的类&横向扩展附属操作)
java 复制代码
public class UserServiceProxy implements UserService{
    public UserServiceProxy(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }

    public void setUserServiceImpl(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }

    private UserServiceImpl userServiceImpl;
    @Override
    public void add() {
        debug("add");
        userServiceImpl.add();
    }

    @Override
    public void delete() {
        debug("delete");
        userServiceImpl.delete();
    }

    @Override
    public void update() {
        debug("update");
        userServiceImpl.update();
    }

    @Override
    public void query() {
        debug("query");
        userServiceImpl.query();
    }

    // 日志方法:横向扩展增增删改查业务
    public void debug(String msg){
        System.out.println("debug:"+msg+"方法执行了");
    }
}
  1. 客户端访问代理角色(客户端想实现真实角色的类,只能通过访问代理角色)
java 复制代码
public class Client {
    public static void main(String[] args) {
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy(userServiceImpl);
        userServiceProxy.add();
        userServiceProxy.delete();
        userServiceProxy.update();
        userServiceProxy.query();
    }
}

聊聊AOP:

动态代理

动态代理的底层都是反射

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口的---JDK的动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javasist

需要了解的两个类:Proxy:代理,InvocationHandler:调用处理程序

Proxy

  • Proxy提供了创建动态代理类和实例的静态方法。

InvocationHandler

  • 代理实例的_调用处理程序_实现的_接口_

代理类写法

  1. 继承InvocationHandler接口
java 复制代码
public class ProxyInvocationHandler implements InvocationHandler
  1. 引用真实角色的接口
java 复制代码
private Sell sell;
  1. 获取真实角色的共同继承的接口
java 复制代码
public Object getProxy(){
    // 获取被代理接口的所有方法
    return Proxy.newProxyInstance(this.getClass().getClassLoader(),
            sell.getClass().getInterfaces(), this);
}
  1. 处理代理实例,并且执行方法返回结果
java 复制代码
    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(sell, args); // 执行一个方法,执行完毕之后会获取一个结果
        return result;
    }

代码步骤

真实角色(厨师)

java 复制代码
// 厨师:真实角色
public class Chef implements Sell {


    @Override
    public void sell() {
        System.out.println("cike_y真实厨师:这里是Demo04案例,快来我这里点吃的吧");
    }
}

外卖平台和厨师都要赚钱(共同接口)

java 复制代码
public interface Sell {
    public  void sell();
}

外卖平台(动态代理类)

java 复制代码
//等我们会用这个类自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler{
    // 被代理的接口,解耦性强
    private Sell sell;

    public void setSell(Sell sell) {
        this.sell = sell;
    }

    //    Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
//            new Class<?>[] { Foo.class },
//            handler);

    // 获取真实角色和代理角色公共的接口
    public Object getProxy(){
        // 获取被代理接口的所有方法
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                sell.getClass().getInterfaces(), this);
    }

    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在执行目标对象的方法之前,可以进行一些自定义操作
        Start();
        Object result = method.invoke(sell, args); // 执行一个方法,执行完毕之后会获取一个结果
        // 在执行目标对象的方法之后,可以进行一些自定义操作
        SeeRider();
        SendOrder();
        GiveMoney();
        TakeCommission();
        TakeRiderFee();
        return result;
    }

// 代理的附属操作
    public void Start(){
        System.out.println("这里是外卖平台,开始代理:");
    }

    // 代理可以做一些附属的事情
    public void SeeRider(){// 找骑手
        System.out.println("帮你寻找骑手中......");
    }

    // 收取佣金
    public void TakeCommission(){
        System.out.println("外卖平台收取佣金");
    }

    // 骑手费用
    public void TakeRiderFee(){
        System.out.println("给骑手小费");
    }

    // 发送客户订单
    public void SendOrder(){
        System.out.println("发送客户订单");
    }

    // 给餐厅钱
    public void GiveMoney(){
        System.out.println("给餐厅钱");
    }

}

用户(要吃饭的人:面对外卖平台)

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 真实角色
        Chef chef = new Chef();

        // 代理角色
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();

        // 要代理的对象:指的是真实角色,设置代理的真实角色对象
        proxyInvocationHandler.setTarget(chef);

        // 代理角色(已经代理了真实角色了)获取接口
        Sell proxy =  (Sell) proxyInvocationHandler.getProxy();

        // 直接面对外卖平台,不用找厨师
        proxy.sell();
    }

万能的写法

只需要改变动态代理的对象为Object对象就可以了,因为Java 中Object 类是所有类的父类,也就是说Java 的所有类都继承了Object,子类可以使用Object 的所有方法。

java 复制代码
public class ProxyInvocationHandler implements InvocationHandler {
    //  要代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

//  官方写法:
//    Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
//            new Class<?>[] { Foo.class },
//            handler);

// 获取真实角色的公共接口
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);

    }

    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args); // 执行方法,返回结果
        return result;
    }
}

其余的:真实角色(厨师)、接口(外卖平台和厨师的共同目标)、用户(吃饭的人)写法都不需要变。

动态代理的好处:

  • 可以使真实角色的操作更加纯粹!,不用去关注一些公共的业务
  • 公共业务就交给了代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

参考文档:

plain 复制代码
JDK-api-1.8_google
相关推荐
程序猿乐锅13 分钟前
万字详解Single-flight(单飞模式)
java·开发语言·分布式·后端·学习·single-flight
Java后端的Ai之路24 分钟前
【AIAgent开发】-Agent工具封装成CLI(纯干货!!!)
java·javascript·人工智能·agent·cli
Flynn Liang40 分钟前
Spring AI 上生产:限流熔断、密钥与成本治理、私有化/混合部署、降级与 Prompt 灰度架构全解
人工智能·spring·架构·高可用·springai·成本治理·生产架构
AntsData1 小时前
用 12K+ 条亚马逊评论信号,定位机械键盘下一版该改什么
java·大数据·计算机外设
code2cat1 小时前
【随笔】连续批处理:长短请求怎样在迭代之间补位
java·人工智能·后端·大语言模型·vllm
最强小杰1 小时前
DeepSeek V4 Flash + Codex CLI 接入教程:原生 Responses API 适配,从配置到跑通收藏这篇就够了(2026)
java·前端·python·ai
Flynn Liang1 小时前
Spring AI 2.0 深度解析:ChatClient、Advisor 链与 MCP——Java 工程师的 AI 落地最优解
java·人工智能·spring·大模型·架构设计·spring ai
灿飞1 小时前
Kotlin与Java虚拟机六:基础与类型系统:一切皆对象,但对象有代价
java·kotlin
计算机毕设定制辅导-无忧学长2 小时前
《基于微信小程序健康习惯激励系统设计与实现》
java·spring boot·mysql·微信小程序·uniapp·健康习惯激励系统
掘金者阿豪2 小时前
SQL Server数据迁移完成后,凌晨的数据库告警该怎么查?
前端·后端