网站建设信(信科网络),网站建站什么目录,wordpress 显示页面标题,搜索引擎是什么介绍
基础介绍
代理模式为一个对象提供一个代理对象#xff0c;以控制对这个对象的访问。即通过代理对象访问目标对象#xff0c;这样做的好处是#xff1a;可以在不修改目标对象代码的基础上#xff0c;增强额外的功能操作#xff0c;即扩展目标对象的功能被代理的对象…介绍
基础介绍
代理模式为一个对象提供一个代理对象以控制对这个对象的访问。即通过代理对象访问目标对象这样做的好处是可以在不修改目标对象代码的基础上增强额外的功能操作即扩展目标对象的功能被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象代理模式有不同的形式主要有静态代理、动态代理和 Cglib代理三种形式
案例实现
静态代理
介绍
静态代理在使用时需要定义接口或者父类被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
登场角色 Subject(主体)定义了使 Proxy角色 和 RealSubject角色 之间具有一致性的接口RealSubiect(实际的主体)RealSubject角色本人会在Proxy角色代理人无法胜任工作时出场。它实现了在 Subject角色 中定义的接口(API)Proxy(代理人)Proxy角色处理来自Client角色的请求。只有当自己不能处理时它才会将工作交给RealSubject角色。Proxy角色只有在必要时才会生成RealSubject 角色。Proxy角色实现 Subject角色中定义的接口(API)Client(请求者)使用Proxy对象的角色
案例一
【应用实例】
定义一个接口ITeacherDao目标对象TeacherDAO实现接口ITeacherDAO使用静态代理方式就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO调用的时候通过调用代理对象的方法来调用目标对象 【Client要做什么事情】
创建TeacherDaoProxy代理对象创建TeacherDao 对象将TeacherDao对象交给TeacherDaoProxy对象执行方法
实现
【接口】
package com.atguigu.proxy.staticproxy;/*** 接口*/
public interface ITeacherDao {/*** 授课*/void teach();
}【目标对象】
package com.atguigu.proxy.staticproxy;public class TeacherDao implements ITeacherDao {Overridepublic void teach() {System.out.println( 老师授课中 。。。。。);}}【代理对象】
package com.atguigu.proxy.staticproxy;/*** 代理对象*/
public class TeacherDaoProxy implements ITeacherDao {/*** 目标对象通过接口来聚合*/private ITeacherDao target;/*** 构造器* param target*/public TeacherDaoProxy(ITeacherDao target) {this.target target;}Overridepublic void teach() {System.out.println(开始代理 完成某些操作。。。。。 );// 中间还可以写一下其他代码比如统计方法调用的次数target.teach();System.out.println(代理结束 。。。。。);}}【客户端】
package com.atguigu.proxy.staticproxy;public class Client {public static void main(String[] args) {//创建目标对象(被代理对象)TeacherDao teacherDao new TeacherDao();//创建代理对象, 同时将被代理对象传递给代理对象TeacherDaoProxy teacherDaoProxy new TeacherDaoProxy(teacherDao);//通过代理对象调用到被代理对象的方法//执行的是代理对象的方法代理对象再去调用目标对象的方法teacherDaoProxy.teach();}}【运行】
开始代理 完成某些操作。。。。。 老师授课中 。。。。。
代理结束 。。。。。Process finished with exit code 0分析
优点在不修改目标对象的功能前提下能通过代理对象对目标进行功能扩展缺点因为代理对象需要与目标对象实现一样的接口一旦接口增加方法目标对象与代理对象都要维护
案例二 实现
【接口】
package com.atguigu.proxy.Sample;public interface Printable {/*** 设置名字** param name*/public abstract void setPrinterName(String name);/*** 获取名字** return*/public abstract String getPrinterName();/*** 显示文字打印输出** param string*/public abstract void print(String string);
}【目标对象打印机】
package com.atguigu.proxy.Sample;/*** 打印机*/
public class Printer implements Printable {private String name;public Printer() {heavyJob(正在生成Printer的实例);}/*** 构造函数** param name*/public Printer(String name) {this.name name;heavyJob(正在生成Printer的实例( name ));}/*** 设置打印机名字** param name*/public void setPrinterName(String name) {this.name name;}/*** 获取打印机名字** return*/public String getPrinterName() {return name;}/*** 显示带打印机名字** param string*/public void print(String string) {System.out.println( name );System.out.println(string);}/*** 重活 持续干5秒钟** param msg*/private void heavyJob(String msg) {System.out.print(msg);for (int i 0; i 5; i) {try {Thread.sleep(1000);} catch (InterruptedException e) {}System.out.print(.);}System.out.println(结束。);}
}【代理对象】
package com.atguigu.proxy.Sample;public class PrinterProxy implements Printable {/*** 名字*/private String name;/*** 目标对象*/private Printer real;public PrinterProxy() {}/*** 构造函数* param name*/public PrinterProxy(String name) {this.name name;}/*** 设置名字* param name*/public synchronized void setPrinterName(String name) { if (real ! null) {// 如果已经有 目标对象// 同时设置 目标对象 的名字real.setPrinterName(name);}this.name name;}/*** 获取名字* return*/public String getPrinterName() {return name;}/*** 显示* param string*/public void print(String string) {realize();// 将真正打印的活委托给 目标对象real.print(string);}/*** 生成目标对象实例*/private synchronized void realize() {if (real null) {real new Printer(name);}}
}问setPrinterName方法和realize方法都是synchronized方法。如果不使用synchronized会有什么问题呢?
答如果不使用synchronized可能会存在多个线程同时调用setPrinterName或realize方法导致多个对象同时创建或设置名字从而影响程序的正确性和稳定性。因此使用synchronized关键字进行同步是为了保证线程安全性和正确性。
【主类】
package com.atguigu.proxy.Sample;public class Main {public static void main(String[] args) {Printable p new PrinterProxy(Alice);System.out.println(现在的名字是 p.getPrinterName() 。);p.setPrinterName(Bob);System.out.println(现在的名字是 p.getPrinterName() 。);p.print(Hello, world.);}
}【运行】
现在的名字是Alice。
现在的名字是Bob。
正在生成Printer的实例(Bob).....结束。Bob
Hello, world.Process finished with exit code 0总结
使用代理模式可以提升处理速度如果目标对象较庞大代理模式只在真正需要执行目标对象的工作时才生成目标对象的实例可以避免系统一启动就需要生成所有实例导致启动缓慢打开一个文档时也不会立即去生成所有图形对象而是浏览至图形附近才生成图形的实例否则会导致文档打开卡顿在http代理中通过 Web 浏览器访问 Web 页面时并不会每次都去访问远程 Web 服务器来获取页面的内容而是会先去获取HTTP代理缓存的页面。只有当需要最新页面内容或是页面的缓存期限过期时才去访问远程Web服务器。Web客户端扮演的是 Client角色HTTP代理扮演的是Proxy角色实现了缓存功能而Web服务器扮演的则是RealSubiect角色
【划分代理人和本人的好处】
隐藏实际对象的实现细节保护真实对象不被直接访问增加程序的安全性代理对象可以充当过滤器在访问前进行权限校验、参数校验等操作提高程序的健壮性和可靠性代理对象可以提供额外的功能如实现缓存、懒加载等增强了程序的扩展性和灵活性可以降低某些操作的耦合度划分代理人和本人可以让客户端和服务端松耦合方便修改和维护
动态代理
介绍
代理对象不需要实现接口但是目标对象要实现接口否则不能用动态代理代理对象的生成是利用JDK的API动态地在内存中构建代理对象动态代理也叫做: JDK代理、接口代理
【JDK中生成代理对象的API】
API所在包java.lang.reflect.Proxy使用方法来动态返回代理对象
static Object newProxylnstance(ClassLoader loader, Class?[] interfaces,InvocationHandler h )【getProxyInstance0方法需要做的事】
根据传入的目标对象利用反射机制返回一个代理对象最后通过代理对象调用目标对象的方法
案例一
实现
【接口】
package com.atguigu.proxy.dynamic;public interface ITeacherDao {/*** 授课*/void teach();void sayHello(String name);
}【目标对象实现接口】
package com.atguigu.proxy.dynamic;public class TeacherDao implements ITeacherDao {Overridepublic void teach() {System.out.println( 老师授课中.... );}Overridepublic void sayHello(String name) {System.out.println(hello name);}}【代理对象】
package com.atguigu.proxy.dynamic;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyFactory {/*** 维护一个目标对象, Object*/private Object target;/*** 构造器对target进行初始化** param target*/public ProxyFactory(Object target) {this.target target;}/*** 给 目标对象 生成一个代理对象* return*/public Object getProxyInstance() {/** public static Object newProxyInstance(ClassLoader loader,Class?[] interfaces,InvocationHandler h)//参数1. ClassLoader loader 指定目标对象的类加载器对象.getClass().getClassLoader()//参数2. Class?[] interfaces: 目标对象实现的接口类型使用泛型方法确认类型获取该类的所有接口target.getClass().getInterfaces()//参数3. InvocationHandler h : 事件处理执行目标对象的方法时会触发事件处理器方法, 会把当前执行的目标对象方法作为参数传入*/return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(JDK代理开始~~);//反射机制调用目标对象的方法Object returnVal method.invoke(target, args);System.out.println(JDK代理提交);return returnVal;}});}}【客户端】
package com.atguigu.proxy.dynamic;public class Client {public static void main(String[] args) {//创建目标对象ITeacherDao target new TeacherDao();//给目标对象创建代理对象, 可以转成 ITeacherDaoITeacherDao proxyInstance (ITeacherDao)new ProxyFactory(target).getProxyInstance();// proxyInstanceclass com.sun.proxy.$Proxy0 内存中动态生成了代理对象System.out.println(proxyInstance proxyInstance.getClass());//通过代理对象调用目标对象的方法proxyInstance.teach();proxyInstance.sayHello( tom );}}【运行】
proxyInstanceclass com.sun.proxy.$Proxy0
JDK代理开始~~老师授课中....
JDK代理提交
JDK代理开始~~
hello tom
JDK代理提交Process finished with exit code 0invoke那行代码就是执行方法的过程
如果方法有参数会存在args中 返回值存储在returnVal中
Cglib代理
静态代理和动态代理都要求目标对象是实现一个接口但是有时候目标对象只是一个单独的对象并没有实现任何的接口这个时候可使用目标对象子类来实现代理这就是Cglib代理也叫子类代理Cglib代理是在内存中构建一个子类对象从而实现对目标对象功能扩展有些书籍也将Cglib代理归属到动态代理Cglib是一个强大的高性能的代码生成包它可以在运行期扩展iava类与实现iava接口。它被许多AOP的框架使用例如Spring AOP用来实现方法拦截Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
【在AOP编程中如何选择代理模式】
目标对象要求实现接口用动态代理目标对象不要求实现接口用Cglib代理
【实现步骤】
引入相关jar包 在内存中动态构建子类注意代理的类不能为final否则报错java.lang.IllegalArgumentException目标对象的方法如果为final/static那么就不会被拦截即不会执行目标对象额外的业务方法即这些方法不能通过代理调用 案例一
实现
【目标对象】
package com.atguigu.proxy.cglib;public class TeacherDao {public String teach() {System.out.println( 老师授课中我是cglib代理不需要实现接口 );return hello;}
}【代理对象】
package com.atguigu.proxy.cglib;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;/*** 需要实现MethodInterceptor的接口*/
public class ProxyFactory implements MethodInterceptor {/*** 维护一个目标对象*/private Object target;/*** 构造器传入一个被代理的对象** param target*/public ProxyFactory(Object target) {this.target target;}/*** 返回 target 对象的代理对象** return*/public Object getProxyInstance() {//1. 创建一个工具类Enhancer enhancer new Enhancer();//2. 设置父类enhancer.setSuperclass(target.getClass());//3. 设置回调函数enhancer.setCallback(this);//4. 创建子类对象即代理对象return enhancer.create();}/*** 必须重写 intercept 方法来调用目标对象的方法** param arg0* param method* param args* param arg3* return* throws Throwable*/Overridepublic Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {System.out.println(Cglib代理模式 ~~ 开始);Object returnVal method.invoke(target, args);System.out.println(Cglib代理模式 ~~ 提交);return returnVal;}}【客户端】
package com.atguigu.proxy.cglib;public class Client {public static void main(String[] args) {//创建目标对象TeacherDao target new TeacherDao();//获取到代理对象并且将目标对象传递给代理对象TeacherDao proxyInstance (TeacherDao) new ProxyFactory(target).getProxyInstance();//执行代理对象的方法触发 intecept 方法从而实现对目标对象的调用目标对象的方法有返回值就可以获取返回值String res proxyInstance.teach();System.out.println(res res);}}【运行】
Cglib代理模式 ~~ 开始老师授课中我是cglib代理不需要实现接口
Cglib代理模式 ~~ 提交
reshelloProcess finished with exit code 0代理模式的变体
防火墙代理内网通过代理穿透防火墙实现对公网的访问缓存代理当请求图片文件等资源时先到缓存代理中取如果取不到资源再到公网或者数据库取然后缓存远程代理远程对象的本地代表通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息和协作 同步代理主要使用在多线程编程中完成多线程间同步工作比如很多人买票不是直接去访问买票接口而是访问代理代理来做同步最后再去访问买票接口
文章说明
本文章为本人学习尚硅谷的学习笔记文章中大部分内容来源于尚硅谷视频点击学习尚硅谷相关课程也有部分内容来自于自己的思考发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识如有侵权请联系删除最后对尚硅谷的优质课程表示感谢。本人还同步阅读《图解设计模式》书籍图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社2017.1进而综合两者的内容让知识点更加全面