学习Java代理模式,这一篇够用了

197 阅读5分钟

这篇代理模式的讲解,会通过其概念、特点,最后通过编码实现每个代理模式的使用场景。

什么是代理模式

代理模式是Java语言中一种编程的设计模式。包括两个重要角色:委托类和代理类。代理类可以在调用委托类同时,对委托类的现有功能的基础之上进行扩展。

如何理解这个模式呢?用日常生活中常见的事情通俗的表述就是:

房东把自己需要被出租的房子,交给房产中介,让其帮忙打理后续包括带租客看房、签合同等繁杂流程,房东只需最后通过房子获取利益就行。这个例子中,房东就是委托类,房产中介就是代理类。

在Java开发中,代理分为静态代理和动态代理。

动态代理又分为jdk动态代理和cglib动态代理。

静态代理、动态代理的特点

静态代理

优点:能做到在不修改委托类代码的前提下,还能通过代理类添加新的功能。

缺点:委托类和代理类都是实现了上级父类的接口,一旦父类接口代码变动,委托类和代理类都要维护代码。

动态代理

动态代理中的jdk代理可以解决静态代理中的那个缺点。jdk动态代理的代理类不需要实现父级的接口,但是委托类需要实现父级的接口。

它通过Java反射机制动态生成代理类,不仅简化了编程的工作,并且提高了软件系统的可扩展性。

上面的静态代理和jdk动态代理都必须让委托类实现父级的接口,假如委托类不去实现任何接口,它们两个代理模式就用不了,所以此时cglib动态代理就上位了。

编码实现静态代理

父类的接口

/**
 * 父类的接口
 * @author 杨33
 * @date 2020/6/18 19:48
 */
public interface ParentInterface {
    /**
     * 挣钱的方法
     */
    void money();
}

委托类:房东

/**
 * 委托类:房东
 * @author 杨33
 * @date 2020/6/18 19:20
 */
public class Owner implements ParentInterface {
    /**
     * 房东挣钱的方法
     */
    public void money() {
        System.out.println("房东:躺着挣钱就是舒服...");
    }
}

代理类:中介

/**
 * 代理类:中介
 * @author 杨33
 * @date 2020/6/18 19:45
 */
public class Medium implements ParentInterface {
    //引入房东对象,与中介联系在一起
    private Owner owner;
    public Medium(Owner owner) {
        this.owner = owner;
    }

    /**
     * 代理类在委托类现有功能的基础之上进行扩展
     */
    public void money() {
        System.out.println("中介:领着客户到处看房...");
        System.out.println("中介:坐下来与客户商量着签租房合同...");
        //告诉房东,收钱了
        owner.money();
    }
}

测试方法:

/**
 * @author 杨33
 * @date 2020/6/18 19:54
 */
public class Demo {
    public static void main(String[] args) {
        //委托类
        Owner owner = new Owner();
        //代理类
        Medium medium = new Medium(owner);
        medium.money();
    }
}

运行测试方法的打印结果:

中介:领着客户到处看房...
中介:坐下来与客户签租房合同...
房东:躺着挣钱就是舒服...

编码实现jdk动态代理

jdk动态代理必须使用到Java API中,java.lang.reflect 包中的Proxy类和InvocationHandler接口。

对上面的静态代理中的代理类修改如下:

import java.lang.reflect.Proxy;

/**
 * 代理类:中介
 * @author 杨33
 * @date 2020/6/18 19:45
 */
public class Medium {
    //引入委托类,与代理类联系在一起
    private Object object;
    public Medium(Object object) {
        this.object = object;
    }

    /**
     * 代理类在委托类现有功能的基础之上进行扩展
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("中介:领着客户到处看房...");
                        System.out.println("中介:坐下来与客户商量着签租房合同...");
                        //告诉房东,收钱了
                        Object invoke = method.invoke(object, args);
                        return invoke;
                    }
                }
        );
    }
}

测试方法:

/**
 * @author 杨33
 * @date 2020/6/18 19:54
 */
public class Demo {
    public static void main(String[] args) {
        //委托类
        ParentInterface owner = new Owner();
        //代理类
        ParentInterface proxyInstance = (ParentInterface)new Medium(owner).getProxyInstance();
        proxyInstance.money();
    }
}

运行测试方法的打印结果:

中介:领着客户到处看房...
中介:坐下来与客户签租房合同...
房东:躺着挣钱就是舒服...

编码实现cglib动态代理

使用cglib动态代理,需要在maven项目中引入依赖

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

委托类,不实现任何接口

/**
 * 委托类:房东
 * @author 杨33
 * @date 2020/6/18 19:20
 */
public class Owner {
    /**
     * 房东挣钱的方法
     */
    public void money() {
        System.out.println("房东:躺着挣钱就是舒服...");
    }
}

代理类:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * cglib的代理子类
 * @author 杨33
 * @date 2020/6/18 19:45
 */
public class Medium implements MethodInterceptor {
    //引入委托类,与代理类联系在一起
    private Object object;
    public Medium(Object object) {
        this.object = object;
    }

    /**
     * 创建代理类
     */
    public Object getProxyInstance() {
        Enhancer en = new Enhancer();
        //设置委托类的字节码
        en.setSuperclass(object.getClass());
        //设置回调函数
        en.setCallback(this);
        //创建代理类
        return en.create();
    }

    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("中介:领着客户到处看房...");
        System.out.println("中介:坐下来与客户商量着签租房合同...");
        //告诉房东,收钱了
        Object invoke = method.invoke(object, args);
        return invoke;
    }
}

测试方法:

/**
 * @author 杨33
 * @date 2020/6/18 19:54
 */
public class Demo {
    public static void main(String[] args) {
        //委托类
        Owner owner = new Owner();
        //代理类
        Owner proxyInstance = (Owner)new Medium(owner).getProxyInstance();
        proxyInstance.money();
    }
}

运行测试方法的打印结果:

中介:领着客户到处看房...
中介:坐下来与客户签租房合同...
房东:躺着挣钱就是舒服...

总结

静态代理需要委托类和代理类都去实现相同的接口或者继承相同的父类。

jdk动态代理委托类需要实现接口或者继承父类,而代理类都不需要。

cglib动态代理的委托类和代理类都不需要实现接口或者继承父类。