Java代理模式

本文介绍了Java代理模式,包括静态代理的实现方式(如定义接口、创建委托类和代理类,以及在方法前后添加额外操作),动态代理(如JDK动态代理机制和CGLIB库)的应用和区别,以及其在RMI、事务管理等场景中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java代理模式

代理模式就是使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能

三种角色

  • 真实类:就是被代理类,委托类,用来真正完成业务服务功能
  • 代理类:将自身的请求用真实类对应的功能来实现,代理类对象并不真正实现业务功能
  • Subject:定义真实类和代理类都应该实现的接口

简单的来说,代理模式的主要作用就是扩展目标对象的功能,比如在目标对象的某个方法执行前后你可以增加一些额外的操作,并且不用修改方法的源代码

通过代理类这个中间层,很好地隐藏和保护委托类的对象,能有效屏蔽外界对委托类的直接访问,也可以在委托类加上额外的操作

静态代理

 Java代理模式是一种设计模式,它为其他对象提供一个代理来控制对这个对象的访问,代理模式在Java开发中广泛使用,尤其是远程方法调用RMI,事务管理,延迟初始化等场景,代理模式主要分为静态代理和动态代理两种形式

  • 静态代理在编译时就已经确定了代理类和目标对象的关系。
  • 使用静态代理,需要为每一个被代理的类手动创建一个代理类,代理类需要实现与目标对象相同的接口,并在内部维护一个目标对象的引用,通过代理类对目标对象方法进行增强,首先额外的功能
  • 步骤:
    • 定义接口:确定目标对象和代理对象共同遵循的接口
    • 实现接口:目标对象实现接口的方法
    • 创建代理类:代理类同样实现该接口,并在内部维护一个对象实际目标对象的引用,通过构造方法传入目标对象,代理类在实现接口方法的时候,可以在调用目标对象的方法前后添加额外的处理逻辑

静态代理的实现方式

1)定义一个接口(Subject)

2)创建一个委托类(Real Subject)实现这个接口

3)创建一个代理类(Proxy)同样实现这个接口

  1. 将委托类注入进代理类Proxy,在代理类的方法中调用Real Subject 中的对应方法,这样的话就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做自己想做的事情
  • 静态代理中,我们对目标对象的每一个方法的增强都是手动完成的,不够灵活
  • 从JVM层面来说,静态代理在编译的时候,将接口,委托类代理类这些文件都变成字节码文件

静态代理实例

  • interface
public interface SmsService {
    String send(String message);
}

  • 委托类 实现该接口

public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
  • 创建一个代理类Proxy 同样实现该接口
  • 将委托类Real Subject 注入进入代理类Proxy,在代理类的方法中调用Real Subject 中的对应方法 ,这样就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情,
public class SmsProxy implements SmsService {
    
    // 将委托类注入进代理类
    private final SmsService smsService;public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }@Override
    public String send(String message) {
        // 调用委托类方法之前,我们可以添加自己的操作
        System.out.println("before method send()");
        // 调用委托类方法
        smsService.send(message); 
        // 调用委托类方法之后,我们同样可以添加自己的操作
        System.out.println("after method send()");
        return null;
    }
}

  • 如何使用被增强的send方法: 首先初始化SmsServiceImpl委托类对象 然后初始化注入代理类对象中,之后调用方法
 public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("Java");
    }
}

  • 弊端:
    • 新增一个委托类,实现了SmsService接口,如果我们想要对这个委托类进行增强,就需要重写一个代理类,然后注入这个新的委托类,不够灵活

动态代理

概述

静态代理中,委托类不同,导致代理类不同,那么将委托类的方法抽取出来,封装成一个通用的处理类

那么在代理类和委托类之间就多了一个处理类的角色,这个角色是对代理类调用委托类方法的这个动作进行统一的调用,也就是通过InvocationHandler来统一处理代理类调用委托类方法这个操作

从JVM角度来说,动态代理是在运行的时候生成.class字节码文件,并且加载到JVM中

JDK动态代理机制

  • 定义一个接口Subject

  • 创建一个委托类: Real Subject 实现这个接口

  • 创建一个处理类并且实现InvocationHandler 接口 重写invoke方法 利用反射机制调用委托类的方法 并且自定义一些处理逻辑,并将委托类注入处理类

  • Subject 接口


public interface SmsService {
    String send(String message);
}
  • 委托类 Real Subject

public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
  • 创建一个处理类并且实现InvocationHandler接口 重写其invoke方法 (在invoke方法使用反射机制调用委托类的方法 自定义一些处理逻辑) 将委托类注入处理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class DebugInvocationHandler implements InvocationHandler {
    
    // 将委托类注入处理类(这里我们用 Object 代替,方便扩展)
    private final Object target;public DebugInvocationHandler(Object target) {
        this.target = target;
    }
    
    // 重写 invoke 方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return result;
    }
}
  • 创建一个代理对象Proxy的工厂类:通过Proxy.newProxyInstance() 创建委托类对象的代理对象
  • 第一个参数是 类加载器 用于定义动态生成的代理类,通常是目标对象(被代理的实例)的累加载器,类加载器的作用是将.class文件加载到JVM中,转换为Class对象
  • 第二个参数接口数组: 代理类需要实现的接口列表,通过getInterfaces获取目标对象实现的接口
  • DebugInvocationHandler是自定义的调用处理程序,拦截对代理对象的所有方法调用 转发给目标对象 同时允许在调用目标方法前后执行自定义逻辑
 public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new DebugInvocationHandler(target)
        );
    }
}

  • 实际使用
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("Java");

  • 与静态代理,动态代理在运行的时候动态生成代理类和对象
  • Java提供了两种动态代理:基于接口的动态代理和基于类的动态代理
  • 基于接口的动态代理:
    • 创建一个实现了InvocationHandler接口的处理器类:该处理器类关联了真实对象,实现了invoke方法,invoke方法中封装了对真实对象方法的调用
    • 通过Proxy.newProxyInstance方法动态创建代理对象:这个方法需要加载真实对象的类加载器、真实对象实现的接口及上一步创建的处理器对象,返回一个实现了指定接口的代理实例
  • 基于类的动态代理:
    • CGLIB(Code Generation Library)是一个第三方代码生成库,通过继承的方式实现动态代理,因此不需要接口。它在运行时动态生成被代理对象的子类,并在子类中重写父类方法,通过方法拦截技术插入增强代码

静态代理的步骤

  • 定义一个接口:确定代理和真实对象的共同行为
  • 实现该接口创建真实对象:具体实现接口的功能
  • 创建一个代理类也实现这个接口:用来包装真实对象,同时在调用真实对象的方法前后,可以添加自己的操作
  • 在代理类中维护一个真实对象的引用,通过构造方法传入真实对象
  • 调用接口的方法,实际上执行的是代理类的方法

动态代理的步骤

  • 定义一个或者多个接口以及实现类
  • 创建一个实现InvocationHandler接口的类,他必须实现invoke方法
  • 通过调用Proxy.newProxyInstance 方法 创建动态代理对象:
    • 类加载器(可以从已经加载的对象中获取)
    • 代理需要实现的接口列表(至少需要实现一个接口)
    • InvocationHandler 实例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少写代码少看论文多多睡觉

求打赏,求关注,求点赞

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值