本文共 8996 字,大约阅读时间需要 29 分钟。
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
动态代理的作用非常大,在很多底层框架中都会用得到,比如struts,Spring等都用到了动态代理,它的作用很简单,就是将你要使用的类,重新生成一个子类或本类,这样框架就可以利用这个新生成的类做一些事情,比如在该类的方法前后加一些代码。。
这样的话,你想像一下,你是不是不用修改任何已经编写好的代码,只要使用代理就可以灵活的加入任何东西,将来不喜欢了,不用也不会影响原来的代码。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
注意事项:
- 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
- 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
代理模式的基本介绍
类图:
角色分析:
图片要显示就加载,如果不显示就不加载,免得占用内存。
相当于代理人、经纪人、代理律师。明星也会处理业务资金,但是并没有经纪人专业,所以请经纪人代理(增强业务功能)。明星只负责光鲜亮丽,不需要负责处理业务资金,而处理业务资金是他的经纪人的事,因为他们够专业。所以,代理也可以作为增强业务功能的一种方式,在不改变原有代码的情况下,增强相应的业务功能。静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类
具体要求
思路分析图解(类图)
代码实现
package com.nemo.proxy.staticproxy;//接口public interface ITeacherDao { void teach(); // 授课的方法}
package com.nemo.proxy.staticproxy;public class TeacherDao implements ITeacherDao { @Override public void teach() { // TODO Auto-generated method stub System.out.println(" 老师授课中。。。。。"); }}
package com.nemo.proxy.staticproxy;//代理对象,静态代理public class TeacherDaoProxy implements ITeacherDao{ private ITeacherDao target; // 目标对象,通过接口来聚合 //构造器 public TeacherDaoProxy(ITeacherDao target) { this.target = target; } @Override public void teach() { System.out.println("开始代理 完成某些操作。。。。。 ");//方法 target.teach(); System.out.println("提交。。。。。");//方法 }}
package com.nemo.proxy.staticproxy;public class Client { public static void main(String[] args) { //创建目标对象(被代理对象) TeacherDao teacherDao = new TeacherDao(); //创建代理对象, 同时将被代理对象传递给代理对象 TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao); //通过代理对象,调用到被代理对象的方法 //即:执行的是代理对象的方法,代理对象再去调用目标对象的方法 teacherDaoProxy.teach(); }}
静态代理优缺点
动态代理的作用非常大,在很多底层框架中都会用得到,比如struts,Spring等都用到了动态代理,它的作用很简单,就是将你要使用的类,重新生成一个子类或本类,这样框架就可以利用这个新生成的类做一些事情,比如在该类的方法前后加一些代码。。
这样的话,你想像一下,你是不是不用修改任何已经编写好的代码,只要使用代理就可以灵活的加入任何东西,将来不喜欢了,不用也不会影响原来的代码。static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
应用实例要求:将前面的静态代理改进成动态代理模式(即:JDK 代理模式)
思路图解(类图)
代码实现
package com.nemo.proxy.dynamic;//接口public interface ITeacherDao { void teach(); // 授课方法 void sayHello(String name);}
package com.nemo.proxy.dynamic;public class TeacherDao implements ITeacherDao { @Override public void teach() { System.out.println(" 老师授课中.... "); } @Override public void sayHello(String name) { System.out.println("hello " + name); }}
package com.nemo.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 进行初始化 public ProxyFactory(Object target) { this.target = target; } //给目标对象 生成一个代理对象 public Object getProxyInstance() { //说明 /* * public static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler h) * //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定 * //2. Class [] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型 * //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行 * 的目标对象方法作为参数传入 */ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public 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.nemo.proxy.dynamic;public class Client { public static void main(String[] args) { //创建目标对象 ITeacherDao target = new TeacherDao(); //给目标对象,创建代理对象, 可以转成 ITeacherDao ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance(); // proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象 System.out.println("proxyInstance=" + proxyInstance.getClass()); //通过代理对象,调用目标对象的方法 //proxyInstance.teach(); proxyInstance.sayHello(" tom "); }}
应用实例要求:将前面的案例用 Cglib 代理模式实现
思路图解(类图)
代码实现+Debug 源码[待 debug]
package com.nemo.proxy.cglib;public class TeacherDao { public String teach() { System.out.println(" 老师授课中,我是 cglib 代理,不需要实现接口 "); return "hello"; }}
package com.nemo.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;public class ProxyFactory implements MethodInterceptor { //维护一个目标对象 private Object target; //构造器,传入一个被代理的对象 public ProxyFactory(Object target) { this.target = target; } //返回一个代理对象: 是 target 对象的代理对象 public Object getProxyInstance() { //1. 创建一个工具类 Enhancer enhancer = new Enhancer(); //2. 设置父类 enhancer.setSuperclass(target.getClass()); //3. 设置回调函数 enhancer.setCallback(this); //4. 创建子类对象,即代理对象 return enhancer.create(); } //重写 intercept 方法,会调用目标对象的方法 @Override public 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.nemo.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); }}
正向代理:如上网,隐藏客户端信息。
其实,正向与反向是对于我们的客户端来说的,我们要上网,如果它帮我们去上网,那它就是一个正向代理;如果它帮我们的对方服务器,那它就是一个反向代理。比如上网,我们想要访问谷歌,如果访问不上,那么我们可以搭建一台正向代理服务器,去购买网上的代理服务器,我们为客户端电脑配置上代理服务器的地址,以后电脑想要访问所有的网址,都会由代理服务器帮我们去访问,访问拿到内容以后再帮我们返回。
所以我们看到的是,我们自己搭建的服务器是帮我们进行上网,这个就是正向代理。
如果我们使用了正向代理,由于我们的请求是发给我们的代理服务器,由代理服务器转给我们的整个互联网,所以互联网上看到的所有的访问的ip地址都是来源于代理服务器的ip,这就隐藏了客户端地址信息。
反向代理:屏蔽内网服务器信息,负载均衡访问。
反向代理在我们搭建集群环境的时候非常重要。
比如有人访问我们的商城,我们的商城有我们的后台服务集群,这个集群的每一个服务器我们都可能要在内网部署,这是一个内网ip,不可能把每一个服务器的外网ip暴露给外界,这样容易引起攻击。那我们为了别人能够找到我们的内网集群,我们就可以在集群的前面前置一个服务器,这个服务器就叫反向代理。
比如我们前置一个nginx,这个nginx是拥有公网ip的,大家都可以进行访问,无论你在中国还是美国我们都可以来进行访问。但是如果我们去访问我们这个公网的服务器,它会代转给我们的服务集群。它相当于对外界屏蔽了我们整个内网服务集群的信息。
转载地址:http://uctkz.baihongyu.com/