The Proxy pattern allows us to create an intermediary that acts as an interface to another resource, while also hiding the underlying complexity of the component.
前言 代理模式是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介。
代理模式 基本介绍:
1) 代理模式:为一个对象 提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的 好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能; 2) 被代理的对象可以是 远程对象、 创建开销大的对象或 需要安全控制 的 对象; 3) 代理模式有不同的形式, 主要有三种 静态代理、理 动态代理 (JDK 代理、接口代理)和 Cglib代 代理 理 ( 可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
代理模式-UML图:
代理模式-类图:
静态代理 静态代理模式的基本介绍:
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
静态代理优缺点:
1) 优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展; 2) 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类; 3) 一旦接口增加方法,目标对象与代理对象都要维护。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class StaticProxy { public static void main (String[] args) { TeachDaoProxy teachDaoProxy = new TeachDaoProxy(new TeachDao()); teachDaoProxy.teach(); } } interface ITeacherDao { void teach () ; } class TeachDao implements ITeacherDao { @Override public void teach () { System.out.println("老师正在授课" ); } } class TeachDaoProxy implements ITeacherDao { ITeacherDao teacherDao; public TeachDaoProxy (ITeacherDao teacherDao) { this .teacherDao = teacherDao; } @Override public void teach () { System.out.println("备课" ); teacherDao.teach(); System.out.println("下课" ); } }
JDK 动态代理 动态代理模式的基本介绍:
1) 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理; 2) 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象; 3) 动态代理也叫做:JDK代理、接口代理;
JDK 中生成代理对象的API:
1) 代理类所在包:java.lang.reflect.Proxy; 2) JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是: static Object newProxyInstance(ClassLoader loader, Class interfaces,InvocationHandler h )。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class JDKDynamicProxy { public static void main (String[] args) { StudentProxyFactory factory = new StudentProxyFactory(new StudentDao()); IStudentDao proxyInstance = (IStudentDao) factory.getProxyInstance(); System.out.println("proxyInstance:" + proxyInstance.getClass()); proxyInstance.study(); } } interface IStudentDao { void study () ; } class StudentDao implements IStudentDao { @Override public void study () { System.out.println("学生在学习" ); } } class StudentProxyFactory { IStudentDao target; public StudentProxyFactory (IStudentDao studentDao) { this .target = studentDao; } public Object getProxyInstance () { 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("学生预习" ); Object invoke = method.invoke(target, args); System.out.println("下课放学" ); return invoke; } }); } }
Cglib 动态代理 Cglib 代理模式的基本介绍:
1) 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理; 2) Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib 代理归属到动态代理; 3) Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截; 4) 在AOP编程中如何选择代理模式: 1. 目标对象需要实现接口,用JDK代理 2. 目标对象不需要实现接口,用Cglib代理 5) Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibDynamicProxy { public static void main (String[] args) { Staff staff = new Staff(); StaffProxyFactory factory = new StaffProxyFactory(staff); Staff proxyInstance = (Staff) factory.getProxyInstance(); proxyInstance.work(); } } class Staff { void work () { System.out.println("======员工工作======" ); } } class StaffProxyFactory implements MethodInterceptor { private Object target; public StaffProxyFactory (Object target) { this .target = target; } public Object getProxyInstance () { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this ); return enhancer.create(); } @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("Cglib代理,打卡上班。" ); Object invoke = method.invoke(target, objects); System.out.println("Cglib代理,打卡下班。" ); return invoke; } }
总结 代理模式的注意事项和细节:
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景: 按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
延伸 代理模式(Proxy) 代理模式-菜鸟教程 设计模式之——代理模式 The Proxy Pattern in Java 尚硅谷Java设计模式,韩顺平图解java设计模式