详解代理模式

代理模式

1、代理模式

代理模式是指,为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。

**换句话说,使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑。客户类真正的想要访问的对象是目标对象,但客户类真正可以访问的对象是代理对象。**客户类对目标对象的访问是通过访问代理对象来实现的。当然,代理类与目标类要实现同一个接口。

例如:

原来的访问关系:
在这里插入图片描述

通过代理的访问关系:
在这里插入图片描述

有 A,B,C 三个类, A 原来可以调用 C 类的方法, 现在因为某种原因 C 类不允许A 类调用其方法,但 B 类可以调用 C 类的方法。A 类通过 B 类调用 C 类的方法。这里 B 是 C的代理。 A 通过代理 B 访问 C。

1.1、代理模式的作用

1.1.1、控制访问

代理类不让客户类访问目标类。例如商家(代理类)不让用户(客户类)访问厂家(目标类)。

1.1.2、增强功能

在原有功能的基础上,增加额外的功能。 新增加的功能,叫做增强功能。

1.2、代理模式的分类

可以将代理模式分为静态代理和动态代理。

2、静态代理

静态代理是指,代理类在程序运行前就已经定义好.java 源文件,其与目标类的关系在程序运行前就已经确立。在程序运行前代理类已经编译为.class 文件。

2.1、静态代理的使用

(1)定义目标接口
package com.scut.service;
// 表示功能的,厂家,商家都要完成的功能
public interface UsbSell {
    //定义方法 参数 amount:表示一次购买的数量,暂时不用
    //返回值表示一个u盘的价格。
    float sell(int amount);
}

(2)定义目标类
package com.scut.factory;
import com.scut.service.UsbSell;
//目标类: 闪迪厂家, 不接受用户的单独购买。
public class UsbSanFactory implements UsbSell {
    @Override
    public float sell(int amount) {
        System.out.println("闪迪 目标类中的方法调用 , UsbSanFactory 中的sell ");
        //一个128G的u盘是 85元。
        //后期根据amount ,可以实现不同的价格,例如10000个,单击是80, 50000个75
        return 85.0f;
    }
}

(3)定义代理类
package com.scut.shangjian;

import com.scut.factory.UsbKingFactory;
import com.scut.factory.UsbSanFactory;
import com.scut.service.UsbSell;

//taobao是一个商家
public class TaoBaoSanDi implements UsbSell {

    //声明商家代理的厂家具体是谁
    private UsbSanFactory factory = new UsbSanFactory();

    @Override
    //实现销售u盘功能
    public float sell(int amount) {
        //向厂家发送订单,告诉厂家,我买了u盘,厂家发货
        float price = factory.sell(amount); 
        price = price + 25; //增强功能,代理类在完成目标类方法调用后,增强了功能。
        //在目标类的方法调用后
        System.out.println("淘宝商家,给你返一个优惠券,或者红包");

        //增加的价格
        return price;
    }
}

(4)定义调用类
package com.scut;
import com.scut.shangjian.TaoBao;
import com.scut.shangjian.WeiShang;

public class ShopMain {

    public static void main(String[] args) {
        //创建代理类对象
        TaoBaoSanDi taoBao = new TaoBaoSanDi();
        //调用代理类对象的方法
        float price = taoBao.sell(1);
        System.out.println("购买u盘单价:"+price);
    }
}

2.2、静态代理的缺点

(1)代码复杂,难于管理

代理类和目标类实现了相同的接口,每个代理都需要实现目标类的方法,这样就出现了大量的代码重复。如果接口增加一个方法,除了所有目标类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

(2)代理类依赖目标类,代理类过多

代理类只服务于一种类型的目标类,如果要服务多个类型。势必要为每一种目标类都进行代理,静态代理在程序规模稍大时就无法胜任了,代理类数量过多。

3、动态代理

动态代理是指代理类对象在程序运行时由 JVM 根据反射机制动态生成的。动态代理不需要定义代理类的.java 源文件。动态代理其实就是 jdk 运行期间,动态创建 class 字节码并加载到 JVM。动态代理的实现方式常用的有两种:使用 JDK 代理代理,与通过 CGLIB 动态代理。

3.1、JDK动态代理

JDK动态代理是基于 Java的反射机制实现的。使用JDK中接口和类实现代理对象的动态创建。JDK 的动态要求目标对象必须实现接口,这是 Java设计上的要求。 从 JDK1.3 以来,java 语言通过 java.lang.reflect 包提供三个类支持代理模式 Proxy, Method和 InovcationHandler

3.1.1、JDK动态代理的使用
(1)定义目标接口
package com.scut.service;

//目标接口
public interface UsbSell {

    float sell(int amount);

    void print();
}

(2)定义目标类
package com.scut.factory;

import com.scut.service.UsbSell;

//目标类
public class UsbKingFactory implements UsbSell {
    @Override
    public float sell(int amount) {
        //目标方法
        System.out.println("目标类中,执行sell目标方法");
        return 85.0f;
    }

    @Override
    public void print() {

    }
}
(3)定义InvocationHandler接口实现类
package com.scut.handler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//必须实现InvocationHandler接口,完成代理类要做的功能(1.调用目标方法,2.功能增强)
public class MySellHandler implements InvocationHandler {

    private Object target = null;

    //动态代理:目标对象是活动的,不是固定的,需要传入进来。
    //传入是谁,就给谁创建代理。
    public MySellHandler(Object target) {
        //给目标对象赋值
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object res  = null;
        //向厂家发送订单,告诉厂家,我买了u盘,厂家发货
        //float price = factory.sell(amount); //厂家的价格。
        res =  method.invoke(target,args); //执行目标方法



        //商家 需要加价, 也就是代理要增加价格。
        //price = price + 25; //增强功能,代理类在完成目标类方法调用后,增强了功能。
        if( res != null ){
            Float price = (Float)res;
            price = price + 25;
            res = price;
        }

        //在目标类的方法调用后,你做的其它功能,都是增强的意思。
        System.out.println("淘宝商家,给你返一个优惠券,或者红包");
        //记录数据库

        //增加的价格
        return res;

    }
}

(4)创建动态代理对象
package com.scut;

import com.scut.factory.UsbKingFactory;
import com.scut.handler.MySellHandler;
import com.scut.service.UsbSell;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class MainShop {

    public static void main(String[] args) {
        //创建代理对象,使用Proxy
        //1. 创建目标对象
        // UsbKingFacotry  factory = new UsbKingFactory();
        UsbSell factory = new UsbKingFactory();
        //2.创建InvocationHandler调用处理器对象
        InvocationHandler handler = new MySellHandler(factory);

        //3.创建代理对象
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(),
                handler);

        //com.sun.proxy.$Proxy0 : 这是jdk动态代理创建的对象类型。
        System.out.println("proxy:"+proxy.getClass().getName());
        //4.通过代理执行方法
        float price = proxy.sell(1);
        System.out.println("通过动态代理对象,调用方法:"+price);
    }
}

3.2、CDLIB动态代理

CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类与实现 Java 接口。它广泛的被许多 AOP 的框架使用,例如 Spring AOP。

使用 JDK 的 Proxy 实现代理,要求目标类与代理类实现相同的接口。若目标类不存在接口,则无法使用该方式实现。但对于无接口的类,要为其创建动态代理,就要使用 CGLIB 来实现。

CGLIB 代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。所以,使用CGLIB 生成动态代理,要求目标类必须能够被继承,即不能是 final 的类。

CGLIB 经常被应用在框架中,例如 Spring ,Hibernate 等。CGLIB 的代理效率高于 JDK。对于CGLIB 一般的开发中并不使用。简单了解即可。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

草莓森明

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值