当前位置: 首页 > 后端技术 > Java

秒懂Java的三种代理模式

时间:2023-04-01 21:03:56 Java

前言代理(Proxy)模式是一种结构设计模式,提供另一种访问目标对象的方式;即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上增加额外的功能操作,即可以扩展目标对象的功能。这里用到编程中的一个思想:不要随意修改别人写好的代码或方法。如果需要修改,可以通过代理扩展方法。代理模式大致有三种作用:RealSubject:真正的类,即代理类和委托类。用于真正完成业务服务功能;Proxy:代理类,用RealSubject对应的功能实现自己的请求,代理类对象并没有真正实现其业务功能;Subject:定义RealSubject和Proxy角色都应该实现接口。代理模式分为三种,静态代理,动态代理(JDK代理,接口代理),Cglib代理(在内存中动态创建目标对象的子类)文本静态代理静态代理需要先定义接口,代理对象和代理对象一起实现同一个接口,然后通过调用同一个方法来调用目标对象的方法。可以看出,代理类无非就是在调用委托类方法前后添加一些操作。不同的委托类导致不同的代理类。某公司生产电视机,需要寻找本地销售代理。然后当客户需要购买电视时,他们可以直接通过代理商购买。代码示例:TV:publicclassTV{privateStringname;//nameprivateStringaddress;//productionplacepublicTV(Stringname,Stringaddress){this.name=name;this.address=地址;}publicStringgetName(){返回名称;}publicvoidsetName(Stringname){this.name=name;}publicStringgetAddress(){返回地址;}publicvoidsetAddress(Stringaddress){this.address=address;}@OverridepublicStringtoString(){return"TV{"+"name='"+name+'\''+",address='"+address+'\''+'}';}}创建公司接口:publicinterfaceTVCompany{/***ProduceTV*@returnTV*/publicTVproduceTV();}公司工厂生产电视:publicclassTVFactoryimplementsTVCompany{@OverridepublicTVproduceTV(){System.out.println("电视机厂生产电视机...");returnnewTV("小米电视","合肥");}}代理去下单取货(静态代理类):public类TVProxy实现TVCompany{privateTVCompanytvCompany;publicTVProxy(){}@OverridepublicTVproduceTV(){System.out.println("TVproxygetorder....");System.out.println("电视代理开始制作....");if(Objects.isNull(tvCompany)){System.out.println("机器代理找工厂....");tvCompany=newTVFactory();}返回tvCompany.produceTV();}}消费者通过代理获取商品(代理的使用):publicclassTVConsumer{publicstaticvoidmain(String[]args){TVProxytvProxy=newTVProxy();电视tv=tvProxy.produceTV();系统.out.println(电视);}}输出结果:TVproxygetorder....TVproxystartproduce....machineproxyfindfactory....TVfactoryproduceTV...TV{name='小米电视',address='合肥'}Processfinishedwithexitcode0总结:优点:静态代理方式实现了目标对象的功能扩展,不需要改变目标对象缺点:静态代理实现了目标对象方法的所有功能,一旦方法被添加到目标中接口,代理对象和目标对象必须做相应的修改,增加了维护成本。如何解决静态代理中的不足?答案是可以使用动态代理动态代理动态代理有以下特点:JDK动态代理对象不需要实现接口,只需要目标对象实现接口即可。实现基于接口的动态代理需要使用JDK中的API,在JVM内存中动态构建一个Proxy对象。您需要使用java.lang.reflect.Proxy及其newProxyInstance方法,但此方法需要接收三个参数。注意该方法是Proxy类中的静态方法,接收的三个参数为:ClassLoader加载器:指定当前目标对象使用类加载器,获取加载器的方法是固定的。Class[]interfaces:目标对象实现的接口类型,使用泛型方法确认类型。InvocationHandlerh:事件处理,当目标对象的方法执行时,会触发事件处理器的方法,并将当前正在执行的目标对象的方法作为参数传入。有一天公司业务量增加,产品越来越多,售后服务也有待提高。但是公司发现原来的agent需要重新训练才能完成所有的业务,所以又找了一个动态的agentB。agentB承诺无缝对接公司所有的业务,不管增加什么业务,都可以完成,不需要附加训练。代码示例:公司新增维修业务:publicinterfaceTVCompany{/***ProduceTVset*@returnTVset*/publicTVproduceTV();/***RepairTVset*@paramtvTVset*@returnTVsetMachine*/publicTVrepair(TVtv);}工厂还需要开始维修业务:publicclassTVFactoryimplementsTVCompany{@OverridepublicTVproduceTV(){System.out.println("电视工厂生产电视...");returnnewTV("小米电视","合肥");}@OverridepublicTVrepair(TVtv){System.out.println("tvisrepairfinished...");returnnewTV("小米电视","合肥");}}B代理全面代表公司所有业务。使用Proxy.newProxyInstance方法生成代理对象,在InvocationHandler中实现invoke方法,在invocation方法中通过反射调用代理类的方法,提供增强的方法。公共类TVProxyFactory{私有对象目标;publicTVProxyFactory(Objecto){this.target=o;}publicObjectgetProxy(){returnProxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("TVproxyfindfactoryfortv...");Objectinvoke=method.invoke(target,args);returninvoke;}});}}这两个业务B代理的购买和维护都可以直接完成。以后公司业务增加时,代理B也可以这样做。公共类TVConsumer{publicstaticvoidmain(String[]args){TVCompanytarget=newTVFactory();TVCompanytvCompany=(TVCompany)newTVProxyFactory(target).getProxy();电视tv=tvCompany.produceTV();电视公司。维修(电视);}}输出:电视代理查找电视工厂....电视工厂生产电视...电视代理查找电视工厂....电视已修复完成...进程已完成,退出代码为0摘要:代理对象执行不需要实现接口,但是目标对象必须实现接口,否则不能使用动态代理。在动态代理方法中,所有的函数调用最终都会被invoke函数转发,所以我们可以在这里做一些我们想做的操作,比如日志系统、事务、拦截器、权限控制等。JDK动态最致命的问题之一proxy就是只能代理实现了接口的实现类,代理类只能代理接口中实现的方法。如果实现类有自己的私有方法,但接口没有If,则无法通过代理调用该方法。如何解决这个问题呢?我们可以使用CGLIB动态代理机制。Cglib代理静态代理和JDK代理都需要一个对象来实现一个接口。有时代理对象只是一个单独的对象。在这种情况下,可以使用Cglib代理。Cglib代理可以称为子类代理,就是在内存中构造一个子类对象,从而实现对目标对象功能的扩展。C代理不仅要代理公司,还要代理多家工厂的产品。cglib通过Enhancer生成代理类,实现MethodInterceptor接口,实现拦截方法。在这个方法中,可以增加一个增强的方法,通过反射Method或者MethodProxy继承类来调用原来的方法。看到代理B承接了公司的各项业务(接口),那么代理C就从中发现了新的商机。B只能代理某公司的产品,而我不仅要代理公司的产品,还要对接不同的工厂,有更广泛的渠道拿货,更容易赚钱。所以使用了Cglib。代码示例:publicclassTVProxyCglibimplementsMethodInterceptor{//为目标对象创建代理对象publicObjectgetProxyInstance(Classc){//1.工具类Enhancerenhancer=newEnhancer();//2。设置父类enhancer.setSuperclass(c);//3。设置回调函数enhancer.setCallback(this);//4。创建子类(代理对象)returnenhancer.create();}@OverridepublicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{System.out.println("TVProxyFactoryenhancement.....");Objectobject=methodProxy.invokeSuper(o,objects);返回对象;}}newproxyBfactorypublicclassTVFactoryB{publicTVproduceTVB(){System.out.println("tvfactoryBproducingtv....");返回新电视(“华为电视”、“南京”);}publicTVrepairB(TVtv){System.out.println("tvB维修完成....");归还电视;}}AgentC可以直接和公司合作,也可以和工厂打交道。并可代理任何厂家的产品。公共类TVConsumer{publicstaticvoidmain(String[]args){TVCompanytvCompany=(TVCompany)newTVProxyCglib().getProxyInstance(TVFactory.class);电视tv=tvCompany.produceTV();tvCompany.repair(tv);System.out.println("==============================");TVFactoryBtvFactoryB=(TVFactoryB)newTVProxyCglib()。getProxyInstance(TVFactoryB.class);电视tv=tvFactoryB.produceTVB();tvFactoryB.repairB(电视);}}输出结果:TVProxyFactoryenhancement.....TVfactoryproduceTV...TVProxyFactoryenhancement.....tvisrepairfinished...==============================TVProxyFactoryenhancement.....tvfactoryBproducingtv....TVProxyFactoryenhancement.....tvBisrepairfinished....进程完成,退出代码0Spring中的AOP使用代理Spring中的AOP有两种实现,JDK和Cglib,如下图所示:如果目标对象需要实现接口,则使用JDK代理。如果目标对象不需要实现接口,则使用Cglib代理。总结静态代理:代理类和目标类都需要实现接口的方法,从而实现代理增强其功能。JDK动态代理:代理类需要实现一个接口,使用Proxy.newProxyInstance方法生成代理类,实现InvocationHandler中的invoke方法,实现增强功能。Cglib动态代理:不需要代理类实现接口,使用Cblib中的Enhancer生成代理对象子类,在MethodInterceptor中实现intercept方法,在该方法中可以实现增强功能。说到底,我还是一个还在奋力前行的码农。如果文章对你有帮助,记得点赞关注,谢谢!