代理模式什么是代理模式? 1、代理就是帮别人做事情 如:工厂的中介,中介负责为工厂招收工人,那么中介就是工厂的代理;客户通过商家购买东西,商家向厂家购买货物,商家就是工厂的代理 2、在开发中存在a类需要调用c类的方法,完成某一个功能,但是c禁止a调用。 这时,可以在a和c之间创建一个b类代理,a类访问b类,b类访问c类。例如:登录的时候需要进行短信验证,这个时候代理就是中国移动的子公司来完成短信的发送功能 3、代理模式就是为其他对象提供一种代理来控制这个对象的访问,在某些情况下一个对象不适合或不能直接引用另一个对象,而代理对象可以在客户类和目标对象直接起到中介的作用使用代理模式的作用 代理模式能带给我们控制访问某个对象的能力,在某些情况下,一个对象的某些方法想要进行屏蔽或者某种逻辑的控制,则我们可以通过代理的方式进行。再此能力上,引申出来的作用,也是目前在开发中经常使用的一个作用,就是在不修改原对象代码的基础上,对原对象的功能进行修改或者增强。控制访问:代理类不让你访问目标,例如商家不让用户访问厂家 功能增强:其中目标对象实现真正的功能,但是代理对象可以对目标对象的功能做进一步的扩充 实现代理的方式 有两种模式:静态代理、动态代理静态代理静态代理:代理类是自己手动创建的,所需要代理的目标类是确定的,实现简单容易理解 例如:A类(用户)B类中介C类(目标) 具体实现的代码逻辑:创建一个接口,定义接口方法,表明B与C类的共同方法创建C类,实现接口方法创建B类,也就是代理,需要实现接口方法创建A类,调用B中介的方法动态代理动态代理(JDK代理,接口代理):利用的反射机制动态地生成代理的对象,我们不需要知道谁代理谁。代理类的那部分代码被固定下来了,不会因为业务的增加而逐渐庞大。在程序执行过程中,使用jdk的反射机制,创建代理类对象,并动态的指定要代理目标类(一种创建java对象的能力),不用自我创建B类,就可以自动创建代理类对象通过反射机制创建对象静态代理实现 实现步骤:创建一个接口,定义卖u盘的方法,表示你的厂家和商家做的事情。创建厂家类,实现1步骤的接口创建商家,就是代理,也需要实现1步骤中的接口。创建客户端类,调用商家的方法买一个u盘。 用户访问商家类,而用户不能直接访问厂家类接口类:表示功能的,厂家,商家都要完成的功能publicinterfaceUsbSell{定义方法参数amount:表示一次购买的数量,暂时不用返回值表示一个u盘的价格。floatsell(intamount);可以多个其它的方法voidprint();}C类(工厂)目标类的改写接口类方法如下:目标类:金士顿厂家,不接受用户的单独购买。publicclassUsbKingFactoryimplementsUsbSell{Overridepublicfloatsell(intamount){System。out。println(目标类中的方法调用,UsbKingFactory中的sell);一个128G的u盘是85元。后期根据amount,可以实现不同的价格,例如10000个,单击是80,50000个75return85。0f;}}B类(中介)改写接口类方法如下目标类C类中方法的调用功能增强 先访问C类,才能给A类taobao是一个商家,代理金士顿u盘的销售。publicclassTaoBaoimplementsUsbSell{声明商家代理的厂家具体是谁privateUsbKingFactoryfactorynewUsbKingFactory();Override实现销售u盘功能publicfloatsell(intamount){向厂家发送订单,告诉厂家,我买了u盘,厂家发货floatpricefactory。sell(amount);厂家的价格。商家需要加价,也就是代理要增加价格。priceprice25;增强功能,代理类在完成目标类方法调用后,增强了功能。在目标类的方法调用后,你做的其它功能,都是增强的意思。System。out。println(淘宝商家,给你返一个优惠券,或者红包);增加的价格returnprice;}}A类代理(中介)测试类:publicclassshopMain{publicstaticvoidmain(String〔〕args){创建代理的商家淘宝对象TaoBaotaoBaonewTaoBao();我只向淘宝买一件产品,得到报价floatpricetaoBao。sell(2);System。out。println(购买一件产品。淘宝的报价为:price);}} 输出:目标类中的方法调用,UsbKingFactory中的sell淘宝商家,给你返一个优惠券,或者红包110。0静态代理的优缺点 优点:实现简单容易理解 缺点:当目标类增多了,代理类也需要增加 例如:上例中创建了一个工厂类,那么该类只能代表一个工厂,当建立了其它品牌的工厂后,还需要为该工厂创建代理类当你的接口中功能增加了或者修改了,会影响众多的实现类。 比如:目标C类,代理B类都需要修改。不修改可不行,因为接口的实现类必须都要实现。动态代理实现 为了解决静态的缺点,产生了动态代理 当静态代理的目标类C很多的时候,可以使用动态代理 动态代理的实现方式有两种:一种是JDK动态代理,一种是CGLIB动态代理所谓JDK动态代理,是使用java反射包中的类和接口实现动态代理的功能。 反射包java。lang。reflect,里面有三个类:InvocationHandler,Method,Proxy所谓CGLIB动态代理。是通过继承目标类。 在子类中重写父类同名方法,实现功能修改(重写的方法不能是final)JDK动态代理机制 在Java动态代理机制中InvocationHandler接口和Proxy类是核心。 Proxy类中使用频率最高的方法是:newProxyInstance(),这个方法主要用来生成一个代理对象publicstaticObjectnewProxyInstance(ClassLoaderloader,Classlt;?〔〕interfaces,InvocationHandlerh)throwsIllegalArgumentException{。。。。。。} 这个方法一共有3个参数:loader:类加载器,用于加载代理对象。interfaces:被代理类实现的一些接口;h:实现了InvocationHandler接口的对象; 要实现动态代理的话,还必须需要实现InvocationHandler来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用。InvocationHandler,Method,Proxy类1InterfaceInvocationHandlerpublicinterfaceInvocationHandler{当你使用代理对象调用方法的时候实际会调用到这个方法publicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable;} invoke()方法有下面三个参数:proxy:动态生成的代理类method:与代理类对象调用的方法相对应args:当前method方法的参数 也就是说: 你通过Proxy类的newProxyInstance()创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler接口的类的invoke()方法。 你可以在invoke()方法中自定义处理逻辑,比如在方法执行前后做什么事情。代理类完成的功能1。调用目标方法,执行目标方法的功能2。功能增强,在目标方法调用时,增加功能2Method Method类:表示方法的,确切的说就是目标类中的方法。 作用: 通过Method可以执行某个目标类的方法,Method。invoke(); method。invoke(目标对象,方法的参数)objectretmethod。invoke(service22,李四) 说明: method。invoke()就是为了用来执行目标方法的,等同于静态代理中的向厂家发送订单,告诉厂家,我买了U盘,厂家发货发送给工厂,我需要的订单,返回报价floatpricefactory。sell(amount);3Proxy类 proxy类:核心的对象,创建代理对象。之前创建对象都是new(),现在是使用proxy类的方法,代替new的使用。 方法:静态方法newProxyInstance() 作用是:创建代理对象,等同于静态代理中的TaoBaotaoBaonewTaoBao() 我们来观察方法原型paramloader被代理类的类加载器,不用多说,没这个怎么反射去找被代理类的信息paraminterfaces被代理类实现的接口paramhInvocationHandler的实现类returnthrowsIllegalArgumentExceptionpublicstaticObjectnewProxyInstance(ClassLoaderloader,Classlt;?〔〕interfaces,InvocationHandlerh)throwsIllegalArgumentException 参数:ClassLoaderloader类加载器,负责向内存中加载对象的,使用反射机制获取对象的classLoaderClasslt;?〔〕interfaces:接口,目标对象实现的接口,也是反射获取的InvocationHandlerh:我们自己写的,代理类要完成的功能 返回值: 代理对象动态代理案例:创建接口,定义目标类要完成的功能创建目标类实现接口自定义InvocationHandler并重写invoke方法在invoke方法中调用原生方法(被代理类的方法)并自定义一些处理逻辑通过Proxy。newProxyInstance(ClassLoaderloader,Classlt;?〔〕interfaces,InvocationHandlerh)方法创建代理对象并把返回值转换成接口类型1。创建需要被代理的接口(必须要有)和实现类publicinterfaceservice{publicvoidreduceStock();}publicclassServiceImplimplementsservice{业务方法OverridepublicvoidreduceStock(){System。out。println(扣减库存开始);}}2。创建代理类 需要实现InvocationHandler接口,重写invoke方法,这里可以对方法进行增强。publicclassDynamicproxyimplementsInvocationHandler{privateObjecttargetObject;publicDynamicproxy(ObjecttargetObject){this。targetObjecttargetObject;}OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable{System。out。println(ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a开始);Objectinvokemethod。invoke(targetObject,args);System。out。println(ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a结束);returninvoke;}}3。创建动态代理对象publicclassTestApp{publicstaticvoidmain(String〔〕args)throwsNoSuchMethodException,InvocationTargetException,IllegalAccessException{创建代理对象1。创建目标对象》实现接口的业务类ServiceservicenewServiceImpl();2。创建代理类对象》InvocationHandler对象InvocationHandlerhandlernewDynamicproxy(service);3。创建代理对象Serviceo(Service)Proxy。newProxyInstance(ServiceImpl。class。getClassLoader(),service。getClass()。getInterfaces(),handler);4。通过代理执行方法o。reduceStock();使用匿名内部类的写法:Serviceo1(Service)Proxy。newProxyInstance(service。getClass()。getClassLoader(),service。getClass()。getInterfaces(),newInvocationHandler(){OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable{System。out。println(哈哈哈哈);Objectinvokemethod。invoke(service,args);System。out。println(嘿嘿嘿);returninvoke;}});o1。reduceStock();}}4。调用接口方法o。reduceStock(); 输出: Proxy类,实现动态代理的流程,使用返回指定接口的代理类实例 我们此时debug一下程序,在invok实现类中打一个断点 此时我们再观察,代理对象MyHandler里面的invoke方法的参数 CGLIB动态代理 使用步骤定义一个类;自定义MethodInterceptor并重写intercept方法,intercept用于拦截增强被代理类的方法,和JDK动态代理中的invoke方法类似;通过Enhancer类的create()创建代理类; 案例编写: 不同于JDK动态代理不需要额外的依赖。CGLIBopeninnewwindow(CodeGenerationLibrary)实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖。dependencygroupIdcglibgroupIdcglibartifactIdversion3。3。0versiondependency1。实现一个使用阿里云发送短信的类publicclassAliSmsService{publicStringsend(Stringmessage){System。out。println(sendmessage:message);returnmessage;}}2。自定义MethodInterceptor(方法拦截器)importnet。sf。cglib。proxy。MethodInterceptor;importnet。sf。cglib。proxy。MethodProxy;importjava。lang。reflect。Method;自定义MethodInterceptorpublicclassDebugMethodInterceptorimplementsMethodInterceptor{paramo被代理的对象(需要增强的对象)parammethod被拦截的方法(需要增强的方法)paramargs方法入参parammethodProxy用于调用原始方法OverridepublicObjectintercept(Objecto,Methodmethod,Object〔〕args,MethodProxymethodProxy)throwsThrowable{调用方法之前,我们可以添加自己的操作System。out。println(beforemethodmethod。getName());ObjectobjectmethodProxy。invokeSuper(o,args);调用方法之后,我们同样可以添加自己的操作System。out。println(aftermethodmethod。getName());returnobject;}}3。获取代理类importnet。sf。cglib。proxy。Enhancer;publicclassCglibProxyFactory{publicstaticObjectgetProxy(Classlt;?clazz){创建动态代理增强类EnhancerenhancernewEnhancer();设置类加载器enhancer。setClassLoader(clazz。getClassLoader());设置被代理类enhancer。setSuperclass(clazz);设置方法拦截器enhancer。setCallback(newDebugMethodInterceptor());创建代理类returnenhancer。create();}}4。实际使用AliSmsServicealiSmsService(AliSmsService)CglibProxyFactory。getProxy(AliSmsService。class);aliSmsService。send(java); 运行上述代码之后,控制台打印出:beforemethodsendsendmessage:javaaftermethodsendJDK动态代理和CGLIB动态代理对比JDK动态代理只能代理实现了接口的类或者直接代理接口,而CGLIB可以代理未实现任何接口的类。CGLIB动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为final类型的类和方法。就二者的效率来说,大部分情况都是JDK动态代理更优秀,随着JDK版本的升级,这个优势更加明显。静态代理和动态代理的对比灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!JVM层面:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的class文件。而动态代理是在运行时动态生成类字节码,并加载到JVM中的