Java代理模式

本文深入探讨了Java中的动态代理,包括JDK动态代理和CGLib动态代理的实现原理及区别。通过示例代码展示了如何创建代理类并执行方法,强调了两者在代理方式、效率和应用场景上的不同。JDK动态代理基于接口实现,而CGLib则是通过字节码技术创建子类。总结了JDK动态代理的简洁性和CGLib的高效性。

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

静态代理

定义:创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class文件就已经存在了。

应用实例:

1.买火车票不一定在火车站买,也可以去代售点。

2.一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。

3.SpringAOP,使用代理类,然后通过调用相同的方法来调用目标对象的方法。前提是代理目标类和代理类都实现同一接口或者继承同一父类。可在代理方法前后执行一些额外的操作,实现增强方法的目的。

代理角色介绍

在这里插入图片描述

Real Subject:真实类,也就是被代理类、委托类。用来真正完成业务服务功能;
Proxy:代理类,将自身的请求用 Real Subject 对应的功能来实现,代理类对象并不真正地去实现其业务功能
Subject:定义 RealSubject 和 Proxy 角色都应该实现的接口。

以火车站买票为例

Subject类

public interface TicketPlatform {

    void saleTicket();

    void cancelTicket();
}

Real Subject:真实类

public class TrainStation implements TicketPlatform{

    @Override
    public void saleTicket() {
        System.out.println("卖票。。");
    }

    @Override
    public void cancelTicket() {
        System.out.println("退票。。");
    }
}

Proxy:代理类

public class TrainStationProxy implements TicketPlatform{

    private TrainStation trainStation;

    public TrainStationProxy(TrainStation station){
        this.trainStation=station;
    }

    @Override
    public void saleTicket() {
        System.out.println("代理平台");
        trainStation.saleTicket();
    }

    @Override
    public void cancelTicket() {
        System.out.println("代理平台");
        trainStation.cancelTicket();
    }
}

运行结果:
在这里插入图片描述

总结:

优点:代理类可以在卖票前后作单独的处理又不影响其原逻辑,具有高扩展性。

缺陷:如果每个类都需要自己提前写好代理类,维护会很困难。

动态代理

定义:在程序运行时,动态创建而成。

一、 jdk动态代理

双方都
在这里插入图片描述
执行结果:

saleTicket方法开始执行...
卖票。。
saleTicket方法执行结束...
cancelTicket方法开始执行...
退票。。
cancelTicket方法执行结束...

在main方法的第一行代码添加以下代码,保存生成的字节码文件:

System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

在这里插入图片描述

生成的代理类
完整代码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.sun.proxy;

import com.xy.designmodel.proxy.TicketPlatform;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements TicketPlatform {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void saleTicket() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void cancelTicket() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.xy.designmodel.proxy.TicketPlatform").getMethod("saleTicket");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.xy.designmodel.proxy.TicketPlatform").getMethod("cancelTicket");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

小结:

1、代理类继承了Proxy类并且实现了要代理的接口,由于java不支持多继承,所以JDK动态代理不能代理类。

2、重写了equals、hashCode、toString。

3、有一个静态代码块,通过反射或者代理类的所有方法。

动态代理实现步骤:

创建一个实现接口InvocationHandler的类,它必须实现invoke方法。
通过Proxy的静态方法newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
通过代理调用方法。

二、cglib动态代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

spring的包已经包含了cglib的代码,所以这里无需再次引包
在这里插入图片描述

 /**
     * cglib获取真实对象的代理实例
     * @param target
     * @return
     */
    private static Object getCglibProxy(final Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println(method.getName() + "cglib方法开始执行...");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println(method.getName() + "cglib方法执行结束...");
                return result;
            }
        });
        return enhancer.create();
    }

执行结果
在这里插入图片描述

saleTicketcglib方法开始执行...
卖票。。
saleTicketcglib方法执行结束...
cancelTicketcglib方法开始执行...
退票。。
cancelTicketcglib方法执行结束...

这里cglib生成了三个class文件,有一个输入代理类,继承了代理对象类,另外两个为所谓Fast类。

在这里插入图片描述
查阅相关资料后了解到cglib的FastClass机制就是对一个类的方法建立索引,调用方法时根据方法的签名来计算索引,通过索引来直接调用相应的方法,所以cglib采用了FastClass的机制来实现对被拦截方法的调用。

这篇文章解释的很详细:CGLIB生成的代理类详解

总结:
1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

代码已上传到github: https://github.com/xxxxyy1998/design-model

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值