持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
哈喽,大家好,我是一条。
今天和大家聊一下代理模式,面试只要问到设计模式,代理模式是必问的。
废话不多说,还是之前的项目结构,一个maven
工程,并安装lombok
依赖和插件。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
定义
官方
代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用,对象结构型模式。
说人话
插播一句广告:“瓜子二手车,无中间商赚差价。”相信大家都听过,这里的中间商不难理解就是代理,中介同理。
我们租房子时都是和中介联系,并不需要接触房东,那中介类就是房东的一个代理类。
除了租房子相关信息,我们还需要付一定额度的中介费,这是不是就很像之前讲过的装饰器模式
。
更准确的说,装饰器模式和静态代理是一样的。
所以代理模式包含如下角色:
- Subject: 抽象主体角色(抽象类或接口)
- Proxy: 代理主体角色(代理对象类)
- RealSubject: 真实主体角色(被代理对象类)
Coding
代理模式分为静态代理和动态代理。
区别在于如果代理类是写死的,称为静态代理。代理类是根据被代理类动态创建的,称为动态代理。
而动态代理又分为jdk
和cglib
两种。
下面就开始编码。
情景再现
假设业务场景如下:
有一个直播接口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,代理模式就和大家聊完了。