Java设计模式之代理模式

代理模式是指给某个对象提供一个代理对象,由代理对象控制对原对象的访问。代理对象可以在调用原对象方法的前后进行其他操作,是对原对象方法的一种增强实现。
为什么会存在代理模式?

  • 原对象无法直接引用,这时候需要代理对象起到中介作用,需要代理类和被代理类实现相同的接口
  • 想增强被代理类的功能且不修改委托类的代码,可以使用代理类,符合开闭原则

代理模式一般分为两种:静态代理和动态代理。在代理模式中,通常存在三个角色:

  • 被代理接口,定义了被代理类的方法。
  • 被代理类,定义了被代理接口方法的具体逻辑。
  • 代理类,实现了被代理接口,并在实现方法里对被代理类的方法进行了增强

接下来,我们以租房子为例,依次介绍静态代理、JDK动态代理、cglib动态代理

静态代理

首先,先介绍不通过中介租房的代码实例:
定义IRent接口,定义租房的行为:

public interface IRenter {

    void rentHouse();
}

实现IRent接口:

public class Renter implements IRenter {
    @Override
    public void rentHouse() {
        System.out.println("客户签订租房合同");
    }
}

创建业务类Test:

public class Test {
    public static void main(String[] args) {
        IRenter renter = new Renter();
        renter.rentHouse();
    }
}
客户签订租房合同

我们知道,靠自己找房子太困难了,我们需要找中介帮我找房子,签合同,那么在符合开闭原则的情况下,静态代理的代码实例如下:

public class RenterProxy implements IRenter {

    private IRenter iRenter;

    public RenterProxy(IRenter iRenter) {
        this.iRenter = iRenter;
    }

    @Override
    public void rentHouse() {
        System.out.println("中介帮忙寻找房子");
        iRenter.rentHouse();
        System.out.println("客户入住");
    }
}
中介帮忙寻找房子
客户签订租房合同
客户入住

代理类同样实现了IRent接口,并在实现方法里调用被代理类的方法且对该方法进行了增强,增加了中介行为的描述。

JDK动态代理

但是问题又来了,如果被代理接口改变了,或者接口里的方法改变了,代理类和业务类都要做出相应的改变,所以,静态代理模式不符合开闭原则,业务层和底层的耦合度很高。我们可以通过JDK动态代理来解决这个问题,代码如下:

public class RenterProxy implements InvocationHandler {

    //被代理的对象引用
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("中介帮忙寻找房子");
        Object object = method.invoke(target, args);
        System.out.println("客户入住");
        return object;
    }
}
public class Test {
    public static void main(String[] args) {
        IRenter renter = new Renter();
        Class clazz = renter.getClass();
        IRenter renterProxy = (IRenter) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new RenterProxy(renter));
        renterProxy.rentHouse();
    }
}

代理类实现了InvocationHandler 接口,重写了invoke方法,invoke方法中的method.invoke(target, args)就是调用原对象的rentHouse方法,最后通过Proxy类的静态方法newProxyInstance来返回一个代理对象。
当我们要更换接口或者修改接口方法的时候,我们只需要在业务类进行修改即可,符合开闭原则。JDK动态代理采用字节重组,重新生成对象代理原始对象,生成对象的步骤如下:

  1. 获取被代理对象的引用,并且获取它的所有接口,反射获取。
  2. JDK动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口。
  3. 动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用。
  4. 编译新生成的Java代码.class文件。
  5. 重新加载到JVM中运行

cglib动态代理

JDK动态代理只能代理实现了接口的类,那么没有接口的类怎么办呢?我们可以使用cglib动态代理,其代码实例如下:

创建没有实现接口的被代理类:

public class Renter {

    public void rentHouse() {
        System.out.println("客户签订租房合同");
    }

}

创建代理类:

public class RenterProxy implements MethodInterceptor {

    private Object target;

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

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("中介帮忙寻找房子");
        Object object = method.invoke(target, objects);
        System.out.println("客户入住");
        return object;
    }
}

创建业务类:

public class Test {
    public static void main(String[] args) {
        Renter renter = new Renter();
        Renter renterProxy = (Renter) Enhancer.create(renter.getClass(), new RenterProxy(renter));
        renterProxy.rentHouse();
    }
}

代理类实现了MethodInterceptor 接口并重写了intercept方法,方法体的代理与JDK动态代理相同,都是通过method.invoke(target, objects)来调用被代理类的方法,其中target是被代理对象,objects是方法参数。
然后在业务类中通过Enhancer.create方法来创建代理类。

JDK动态代理和cglib动态代理对比

JDK动态代理实现了被代理对象的接口,cglib动态代理继承了被代理对象

  • 二者都在运行期生成字节码,JDK动态代理直接写class字节码,cglib动态代理使用ASM框架写class字节码,生成代理类的过程更复杂,效率低。
  • JDK动态代理调用代理方法是通过反射机制调用的,cglib动态代理通过FastClass机制直接调用方法,其原理就是:为代理类和被代理类各生成一个类,这个类会为代理类和被代理类的方法分配一个index,FastClass就可以直接通过index定位要调用的方法,省去了反射调用,所以cglib动态代理的执行效率更高。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值