秒懂 Java 的三种代理模式

静态代理

静态代理需要先定义接口,被代理对象与代理对象一起实现相同的接口,然后通过调用相同的方法来调用目标对象的方法

image-20210726222112024

可以看见,代理类无非是在调用委托类方法的前后增加了一些操作。委托类的不同,也就导致代理类的不同。

某公司生产电视机,在当地销售需要找到一个代理销售商。那么客户需要购买电视机的时候,就直接通过代理商购买就可以。

代码示例:

电视机:

public class TV {

private String name;//名称

private String address;//生产地

public TV(String name, String address) {

this.name = name;

this.address = address;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

@Override

public String toString() {

return “TV{” +

“name='” + name + ‘’’ +

“, address='” + address + ‘’’ +

‘}’;

}

}

复制代码

创建公司接口:

public interface TVCompany {

/**

  • 生产电视机

  • @return 电视机

*/

public TV produceTV();

}

复制代码

公司的工厂生产电视机:

public class TVFactory implements TVCompany {

@Override

public TV produceTV() {

System.out.println(“TV factory produce TV…”);

return new TV(“小米电视机”,“合肥”);

}

}

复制代码

代理商去下单拿货(静态代理类):

public class TVProxy implements TVCompany{

private TVCompany tvCompany;

public TVProxy(){

}

@Override

public TV produceTV() {

System.out.println("TV proxy get order … ");

System.out.println("TV proxy start produce … ");

if(Objects.isNull(tvCompany)){

System.out.println("machine proxy find factory … ");

tvCompany = new TVFactory();

}

return tvCompany.produceTV();

}

}

复制代码

消费者通过代理商拿货(代理类的使用):

public class TVConsumer {

public static void main(String[] args) {

TVProxy tvProxy = new TVProxy();

TV tv = tvProxy.produceTV();

System.out.println(tv);

}

}

复制代码

输出结果:

TV proxy get order …

TV proxy start produce …

machine proxy find factory …

TV factory produce TV…

TV{name=‘小米电视机’, address=‘合肥’}

Process finished with exit code 0

复制代码

小结:

  • 优点:静态代理模式在不改变目标对象的前提下,实现了对目标对象的功能扩展。

  • 缺点:静态代理实现了目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。

如何解决静态代理中的缺点呢?答案是可以使用动态代理方式

动态代理

image-20210726224121229

动态代理具有如下特点:

  1. JDK动态代理对象不需要实现接口,只有目标对象需要实现接口。

  2. 实现基于接口的动态代理需要利用JDK中的API,在JVM内存中动态的构建Proxy对象

  3. 需要使用到 java.lang.reflect.Proxy,和其newProxyInstance方法,但是该方法需要接收三个参数。

image-20210724132028289

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  • ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的。

  • Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型。

  • InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。

有一天公司增加了业务,出售的商品越来越多,售后也需要更上。但是公司发现原来的代理商,还要再培训才能完成全部的业务,于是就找了另外的动态代理商B代理商B 承诺无缝对接公司所有的业务,不管新增什么业务,均不需要额外的培训即可完成。

代码示例:

公司增加了维修业务:

public interface TVCompany {

/**

  • 生产电视机

  • @return 电视机

*/

public TV produceTV();

/**

  • 维修电视机

  • @param tv 电视机

  • @return 电视机

*/

public TV repair(TV tv);

}

复制代码

工厂也得把维修业务搞起来:

public class TVFactory implements TVCompany {

@Override

public TV produceTV() {

System.out.println(“TV factory produce TV…”);

return new TV(“小米电视机”,“合肥”);

}

@Override

public TV repair(TV tv) {

System.out.println(“tv is repair finished…”);

return new TV(“小米电视机”,“合肥”);

}

}

复制代码

B代理商 全面代理公司所有的业务。使用Proxy.newProxyInstance方法生成代理对象,实现InvocationHandler中的 invoke方法,在invoke方法中通过反射调用代理类的方法,并提供增强方法。

public class TVProxyFactory {

private Object target;

public TVProxyFactory(Object o){

this.target = o;

}

public Object getProxy(){

return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),

new InvocationHandler() {

@Override

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

System.out.println("TV proxy find factory for tv… ");

Object invoke = method.invoke(target, args);

return invoke;

}

});

}

}

复制代码

购买、维修这两个业务 B代理就可以直接搞定了。后面公司再增加业务,B代理也可以一样搞定。

public class TVConsumer {

public static void main(String[] args) {

TVCompany target = new TVFactory();

TVCompany tvCompany = (TVCompany) new TVProxyFactory(target).getProxy();

TV tv = tvCompany.produceTV();

tvCompany.repair(tv);

}

}

复制代码

感受:

其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。

特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

拿到字节跳动offer后,简历被阿里捞了起来,二面迎来了P9"盘问"

TVCompany tvCompany = (TVCompany) new TVProxyFactory(target).getProxy();

TV tv = tvCompany.produceTV();

tvCompany.repair(tv);

}

}

复制代码

感受:

其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。

特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

[外链图片转存中…(img-0Je2dWF8-1720123557255)]

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值