当前位置: 首页 > news >正文

代理模式简述

目录

一、主要角色

二、类型划分

三、静态代理

示例

缺点

四、动态代理

JDK动态代理

示例

 缺点

CGLib动态代理

导入依赖

示例

五、Spring AOP


        代理模式是一种结构型设计模式,通过代理对象控制对目标对象的访问,可在不改变目标对象情况下增强其功能,隐藏实现细节。

一、主要角色

  • Subject(业务接口类):可以是抽象类或接口
  • RealSubject(业务实现类):具体的业务执行,即被代理的对象
  • Proxy(代理类):RealSubject的代理

二、类型划分

根据代理的创建时期,可分为静态代理和动态代理

  • 静态代理:由程序员创建或工具自动生成代理类源代码并编译,程序运行前代理类的字节码文件已经存在;
  • 动态代理:在程序运行时运用反射机制动态创建而成

为方便下面的讲解,我们先提前创建业务接口类和业务实现类

//业务接口类
public interface HouseSubject {
    void rentHouse();
}
//业务实现类(被代理类)
public class RealHouseSubject implements HouseSubject {
    @Override
    public void rentHouse() {
        System.out.println("我是房东,我出租房子");
    }
}

三、静态代理

示例
//代理类
public class HouseProxy implements HouseSubject{
    //中介出售房子前要先有房东的授权
    private HouseSubject houseSubject;

    public HouseProxy(HouseSubject houseSubject) {
        this.houseSubject = houseSubject;
    }

    @Override
    public void rentHouse() {
        System.out.println("我是中介,我开始代理房东出租房子");
        houseSubject.rentHouse();
        System.out.println("我是中介,代理结束");
    }
}

使用代理类执行

public class Main {
    public static void main(String[] args) {
        //创建代理对象
        HouseSubject subject=new HouseProxy(new RealHouseSubject());
        subject.rentHouse();
    }
}

缺点
  • 为每个被代理类编写代理类,代码冗余,随着业务的发展,类数量膨胀,项目管理难度增大
  • 被代理类接口变化时,所有相关代理类都需修改,易出错,可维护性差
  • 只能服务特定被代理类,难以应对新类和多变的额外逻辑需求,缺乏灵活性

四、动态代理

JDK动态代理
示例

自定义JDKInvocationHandler 并重写invoke(),在invoke()中会调用目标方法,并自定义一些处理逻辑

import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//实现 InvocationHandler接口可以被代理对象的方法进行功能增强
@Slf4j
public class JDKInvocationHandler implements InvocationHandler {

    //目标类
    private Object target;

    public JDKInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * 调用目标方法,并对方法进行增强
     * @param proxy 代理对象
     * @param method 代理对象需要实现的方法
     * @param args method方法所对应的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("JDK动态代理开始");
        //调用目标函数
        Object result=method.invoke(target,args);
        log.info("JDK动态代理结束");
        return null;
    }
}

创建代理对象,执行逻辑 

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        RealHouseSubject target=new RealHouseSubject();

        //通过被代理类、被代理类实现的接口、方法调用处理器来创建一个代理类
        /**
         *  ClassLoader loader 类加载器,用于加载代理对象
         *  Class<?>[] interfaces 被代理类实现的一些接口(jdk只能代理实现了接口的类)
         *  java.lang.reflect.InvocationHandler h 实现了InvocationHandler接口的对象
         */
        HouseSubject proxy=(HouseSubject) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            new Class[]{HouseSubject.class},
            new JDKInvocationHandler(target)
        );
        //使用代理类
        proxy.rentHouse();
    }
}

 缺点

只能代理实现了接口的类,不能代理普通类

CGLib动态代理

导入依赖
<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>3.3.0</version>
</dependency>
示例
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

@Slf4j
public class CGLibMethodInterceptor implements MethodInterceptor {
    private Object target;
    public CGLibMethodInterceptor(Object target) {
        this.target = target;
    }

    /**
     *
     * @param o 被代理的对象
     * @param method 目标方法
     * @param objects 方法入参
     * @param methodProxy 用于调用原始方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //代理增强内容
        System.out.println("CGLib代理开始");
        //通过反射调用被代理的方法
        Object retVal=methodProxy.invoke(target,objects);
        System.out.println("CGLib代理结束");
        return retVal;
    }
}
import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {
        HouseSubject target=new RealHouseSubject();
        HouseSubject proxy= (HouseSubject)Enhancer.create(target.getClass(),new CGLibMethodInterceptor(target));
        proxy.rentHouse();
    }
}

五、Spring AOP

Spring AOP是基于动态代理实现的,动态代理有JDK和CGLIB两种方式,运行时使用哪种方式与项目配置和代理的对象有关。

proxy-target-class="false"情况下,若代理对象实现接口,默认使用JDK动态代理;若未实现接口,则会用CGLIB动态代理。当然,即便对象实现接口,也能通过xml配置proxy-target-class="true"或在配置类上使用注解@EnableAspectJAutoProxy(proxyTargetClass = true) 强制使用CGLIB动态代理。

Spring Boot 2.X开始默认使用CGLIB代理,也就是proxy-target-class="true"。

二者各有优劣,需依项目实际情况抉择。JDK动态代理优势在于基于Java原生支持,无需额外依赖,性能较好,适用于对性能要求高且目标对象有接口的场景;但是只能代理实现了接口的对象。

CGLIB动态代理优势在于能代理无接口的普通类,功能更强大、灵活性更高;但是需额外注入依赖,且通过继承创建代理,若类被final修饰则无法代理,生成代理对象的性能开销相对较大。

相关文章:

  • 贪心算法(19)(java)重构字符串
  • 银河麒麟系统添加开机自启动
  • 【AI】使用Huggingface模型实现文本内容摘要器
  • DeepSeek 接入 Word 完整教程
  • shell 编程之循环语句
  • UNet深度学习实战遥感图像语义分割
  • 孟加拉slot游戏出海代投FB脸书广告策略
  • HTTP协议入门
  • c# 委托和事件的区别及联系,Action<T1,T2>与Func<T1,T2>的区别
  • RTX 5060 Ti 3DMark跑分首次流出:比RTX 4060 Ti快20%
  • JVM——运行时数据区
  • Linux内核中struct net_protocol的early_demux字段解析
  • 谷歌A2A与Anthropic MCP: AI 智能体互补双协议
  • 【MySQL】MVCC工作原理、事务隔离机制、undo log回滚日志、间隙锁
  • mac中的zip文件压缩与压缩文件中指定目录删除
  • 【话题讨论】Python + AI图像生成实战:AI图像生成——用代码点亮数字艺术
  • Uipath获取最新下载文件
  • Express学习笔记(六)——前后端的身份认证
  • 嵌入式基础(三)基础外设
  • 论文阅读笔记——Generating Long Sequences with Sparse Transformers
  • 从板凳席到指挥台,横扫广东男篮的少帅潘江究竟有何神奇
  • 女子伸腿阻止高铁关门被拘,央媒:严格依规公开处理以儆效尤
  • 建投读书会·东西汇流|西风东渐中的上海营造
  • 道客网络陈齐彦:技术无界化,开源让AI变成了“全民食堂”
  • 重庆警方通报“货车轮胎滚进服务区致人死亡”:正进一步调查
  • 抵制饭圈极端应援,发倡议书还不够