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

设计模式-代理模式

时间:2023-04-01 19:07:23 Java

1.1基本介绍代理模式:为一个对象提供一个替身来控制对该对象的访问,即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上增加额外的功能操作,即可以扩展目标对象的功能。代理对象可以是远程对象、创建成本高昂的对象或需要安全控制的对象。有不同形式的代理模式。主要分为三种:静态代理、动态代理(JDK代理、接口代理)和Cglib代理(无需实现接口即可在内存中动态创建对象,属于动态代理的范畴)。代理模式示意图1.2静态代理1.2.1基本介绍使用静态代理时,需要定义一个接口或父类,代理对象(即目标对象)与代理对象实现相同的接口或继承相同的接口父类。1.2.2应用实例思维分析图(类图)代码实现createITeachServiceinterfacepublicinterfaceITeachService{/***teachingmethod*/publicvoidteach();}createTeachServiceimplementationclasspublicclassTeachServiceimplementsITeachService{@Overridepublicvoidteach(){System.out.println("开始上课...");}}创建TeachServiceProxy代理类publicclassTeachServiceProxyimplementsITeachService{privateTeachServiceteachService;publicTeachServiceProxy(TeachServiceteachService){this.teachService=teachService;}@Overridepublicvoidteach(){System.out.println("准备上课...");teachService.teach();System.out.println("下课...");}}创建客户端调用publicclassClient{publicstaticvoidmain(String[]args){TeachServiceProxyteachServiceProxy=newTeachServiceProxy(newTeachService());teachServiceProxy.teach();//输出//准备上课...//开始上课...//结束课...}}1.2.3静态代理优缺点优点:无需修改目标对象的功能,可以通过代理对象扩展目标功能缺点:由于代理对象需要实现与目标对象相同的接口,因此代理类会很多。将方法添加到接口后,必须维护目标对象和代理对象。1.3动态代理1.3.1代理对象基本介绍,不需要实现接口,但目标对象必须实现接口,否则不能使用动态代理。代理对象的生成是利用JDK的API在内存中动态构建代理对象。动态代理又称:JDK代理、接口代理。1.3.2JDK中生成代理对象的API代理类的封装:java.lang.reflect.ProxyJDK实现代理只需要使用newProxyInstance方法,但是这个方法需要接收三个参数,完整的写法是:staticObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)1.3.3动态代理应用实例将之前的静态代理改进为动态代理模式思路图(类图)代码实现创建ITeachService接口publicinterfaceITeachService{publicvoidteach();公共无效选择(字符串名称);publicStringanswer(Stringanswer);}创建一个TeachService实现类publicclassTeachServiceimplementsITeachService{@Overridepublicvoidteach(){System.out.println("Startclass...}@Overridepublicvoidselect(Stringname){System.out.println(""+name+"回答问题...");}@OverridepublicStringanswer(Stringanswer){System.out.println("Theansweris...");返回答案;}}创建TeachServiceProxy代理类publicclassTeachServiceProxy{//待代理对象privateObjecttarget;//构造方法publicTeachServiceProxy(Object目标){this.target=target;}//获取代理对象publicObjectgetProxyInstance(){/***publicstaticObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)*1.ClassLoaderloader:指定当前使用的类加载器target对象,获取loader的方法是固定的*2.Class[]interfaces:target对象实现的接口,使用泛型方法确认类型*3.InvocationHandlerh:事件处理,执行时使用目标对象的方法,会触发当前的事件处理方法,并将目标对象当前执行的方法作为参数传递*/returnProxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println("开始使用JDK代理...");//反射机制调用目标对象的方法Objectinvoke=method.invoke(target,args);System.out.println("JDK代理使用结束...");返回调用;}});}}创建客户端调用publicclassClient{publicstaticvoidmain(String[]args){//创建目标对象TeachServiceteachService=newTeachService();//创建代理类TeachServiceProxyteachServiceProxy=newTeachServiceProxy(teachService);//获取代理对象ITeachServiceinstance=(ITeachService)teachServiceProxy.getProxyInstance();//调用方法instance.teach();instance.select("汤姆");Stringanswer=instance.answer("Java是最好的语言");System.out.println("答案="+答案);//输出//开始使用JDK代理...//开始上课...//结束使用JDK代理...//开始使用JDK代理...//汤姆回答问题...//结束使用JDK代理...//开始使用JDK代理...//答案是...//结束使用JDK代理...//answer=Javaisbestlanguage}}1.4CglibProxy1.4.1BasicIntroductionStaticProxy和JDKProxy模式都要求目标对象实现接口,但有时目标对象只是一个单独的对象,不实现任何接口。这时候目标对象可以使用Object的子类来实现代理,这就是Cglib代理Cglib代理也称为子类代理。它在内存中构建子类对象,实现目标对象的功能扩展。有些书也把Cglibproxy归为动态代理。Cglib是一个功能强大的高性能代码生成包,可以在运行时扩展Java类并实现Java接口。它被许多AOP框架广泛使用,例如SpringAOP,用于实现方法拦截。AOP编程中如何选择代理模式:目标对象需要实现接口,JDK代理目标对象不需要实现接口。Cglib包的底层是利用ASM类的字节码处理框架对字节码进行转换生成新的。1.4.2实现步骤需要导入Cglib的Jar包cglibcglib2.2.2asmasm<版本>3.2asmasm-commons3.2asmasm-tree3.2在内存中动态构建子类,注意代理类不能是final的,否则会报java.lang.IllegalArgumentException。如果目标对象的方法是final/static,则不会被拦截,即不会执行目标对象的额外业务方法。1.4.3应用实例使用Cglib代理方式实现前面案例中的思路图(类图)代码,创建TeachService类publicclassTeachService{publicvoidteach(){System.out.println("Startclass...");}publicvoidselect(Stringname){System.out.println(""+name+"回答问题...");}publicStringanswer(Stringanswer){System.out.println("答案是...");返回答案;}}创建一个TeachServiceProxy代理类publicclassTeachServiceProxyimplementsMethodInterceptor{//targetobjectprivateObjecttarget;//构造函数publicTeachServiceProxy(Objecttarget){this.target=target;}//创建代理对象publicObjectgetProxyInstance(){//1.创建工具类Enhancerenhancer=newEnhancer();//2.设置父类enhancer.setSuperclass(target.getClass());//3.设置回调函数enhancer.setCallback(this);//4.创建子类对象,即代理对象returnenhancer.create();}//重写拦截方法,会调用目标对象的方法@OverridepublicObjectintercept(Objecto,Methodmethod,Object[]args,MethodProxymethodProxy)throwsThrowable{System.out.println("启动cglib代理...");对象调用=method.invoke(target,args);System.out.println("结束cglib代理...");返回调用;}}创建客户端调用publicclassClient{publicstaticvoidmain(String[]args){//创建目标对象TeachServiceteachService=newTeachService();//创建代理类TeachServiceProxyteachServiceProxy=newTeachServiceProxy(teachService);//获取代理对象TeachServiceinstance=(TeachService)teachServiceProxy.getProxyInstance();//调用方法instance.teach();instance.select("汤姆");Stringanswer=instance.answer("Java是最好的语言");System.out.println("答案="+答案);//输出//Startcglibagent...//Startclass...//endcglibproxy...//startcglibproxy...//Tom回答了问题...//Endcglibproxy...//Startcglibproxy...//Theansweris...//Endcglibproxy...//answer=Javaisbestlanguage}}1.5普通代理模式防火墙代理内网通过代理穿透防火墙实现访问公网缓存代理例如:请求图片文件等资源时,先去缓存代理获取,如果资源是抓出来的,没问题,如果抓到资源不可用,就从公网或者数据库中抓取,然后缓存起来。远程代理是远程对象的本地代表,通过它可以将远程对象当作本地对象来调用。远程代理通过网络与真实的远程对象交流信息。同步代理:主要用在多线程编程中,完成多个线程之间的同步工作。同步代理:主要用在多线程编程中,完成多个线程之间的同步工作。