代理模式

131 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情


哈喽,大家好,我是一条。

今天和大家聊一下代理模式,面试只要问到设计模式,代理模式是必问的。

废话不多说,还是之前的项目结构,一个maven工程,并安装lombok依赖和插件。

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.10</version>
    </dependency>

定义

官方

代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用,对象结构型模式。

说人话

插播一句广告:“瓜子二手车,无中间商赚差价。”相信大家都听过,这里的中间商不难理解就是代理,中介同理。

我们租房子时都是和中介联系,并不需要接触房东,那中介类就是房东的一个代理类。

除了租房子相关信息,我们还需要付一定额度的中介费,这是不是就很像之前讲过的装饰器模式

更准确的说,装饰器模式和静态代理是一样的

所以代理模式包含如下角色:

  • Subject: 抽象主体角色(抽象类或接口)
  • Proxy: 代理主体角色(代理对象类)
  • RealSubject: 真实主体角色(被代理对象类)

Coding

代理模式分为静态代理和动态代理。

区别在于如果代理类是写死的,称为静态代理。代理类是根据被代理类动态创建的,称为动态代理。

而动态代理又分为jdkcglib两种。

下面就开始编码。

情景再现

假设业务场景如下:

有一个直播接口Live,我直播的类叫做LiLive,也就是被代理类。我们要做的就是创建代理类直播。

基础类

public interface Live {
    void play();
}
public class LiLive implements Live{
    @Override
    public void play() {
        System.out.println("开始直播");
    }
}

静态代理

/**
 * 代理类和被代理类实现同一抽象接口
 */
@Data
@AllArgsConstructor
public class LiLiveProxy implements Live {
    // 那如何知道代理谁呢?这里不用继承,而是组合的方式
    private LiLive liLive;
​
    @Override
    public void play() {
        // 此处增强功能 即 装饰器模式
        System.out.println("代理");
        liLive.play();
    }
}

测试类

public class MainTest {
    public static void main(String[] args) {
        LiLiveProxy liveProxy = new LiLiveProxy(new LiLive());
        liveProxy.play();
    }
}

输出

动态代理

JDk

所谓动态代理,好比有一个万能的代理类生成器,无论我传入任何对象,都能创建出代理类。

总体就两步,

  • 创建类
  • 执行代理类的目标方法

创建类new是肯定的不行了,只能用反射,即通过classLoader

执行目标其实就是将被代理类的所有方法都做一个拦截,转而由代理类实现或增强。

万能生成器

public class JdkProxy {
    public static <T> Object getProxy(T t) {
        return Proxy.newProxyInstance(t.getClass().getClassLoader(),
                t.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("代理");
                    return method.invoke(t, args);
                });
    }
}
​
// lambda 简化写法
return Proxy.newProxyInstance(t.getClass().getClassLoader(),
                t.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    System.out.println("代理");
                    return method.invoke(t, args);
                });

测试类

public class MainTest {
    public static void main(String[] args) {
        // 传入被代理类,由jdk动态的生成代理类
        Live proxy = (Live) JdkProxy.getProxy(new LiLive());
        proxy.play();
    }
}

总结

t.getClass().getInterfaces(),这里我们可以得出一个结论,jdk的动态代理,要求被代理类必须实现一个接口。正因为有这个限制,才诞生了 cglib 动态代理。

cglib

创建一个无接口的类

public class LiSell {
​
    public void sell() {
        System.out.println("开始卖货");
    }
​
}

生成代理类

public class CglibProxy  {
    public static<T> Object getProxy(T t) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(t.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("代理");
                return methodProxy.invokeSuper(o,objects);
            }
        });
        return enhancer.create();
    }
​
}
​
​
// lambda 简化写法
 enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("代理");
            return methodProxy.invokeSuper(o,objects);
        });

测试

打完收工

ok,代理模式就和大家聊完了。