1.代理(proxy)模式的定义
- 给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
- 代理模式主要分为静态代理和动态代理。
2.代理模式的主要优缺点
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点:
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
3.代理模式的主要角色
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
4.代理模式的结构图
5. 代理模式的实现,以买票为例
- 静态代理:由程序员编写的代理类,并在程序运行前就编译好了。
- 动态代理:代理类在程序运行时创建的代理方式被成为动态代理。
5.1 静态代理
- 创建买票接口
package com.lw.designpattern.proxy.staticproxy; /** * @Classname Ticket * @Description 买票接口 * @Author lw * @Date 2019-12-27 08:46 */ public interface Ticket { /** * 买票 */ public void buyTicket(); }
- 创建买票接口实现类,实现Ticket接口
package com.lw.designpattern.proxy.staticproxy; /** * @Classname TicketImpl * @Description 买票实现类 * @Author lw * @Date 2019-12-27 08:48 */ public class TicketImpl implements Ticket { @Override public void buyTicket() { System.out.println("买票。。。。。。"); } }
- 创建买票代理类,实现Ticket接口
package com.lw.designpattern.proxy.staticproxy; import com.lw.designpattern.proxy.Ticket; /** * @Classname StaticProxy * @Description (买票)静态代理类 * @Author lw * @Date 2019-12-27 08:51 */ public class StaticProxy implements Ticket { private Ticket ticket; public StaticProxy(Ticket ticket){ this.ticket = ticket; } @Override public void buyTicket() { System.out.println("代理买票开始"); ticket.buyTicket(); System.out.println("代理买票成功"); } }
- 单元测试
/** * 代理模式-静态代理 */ @Test public void testStaticProxy(){ // 目标对象 Ticket ticket = new TicketImpl(); // 代理对象 StaticProxy cattleProxy = new StaticProxy(ticket); // 买票 cattleProxy.buyTicket(); }
打印结果
5.2 JDK动态代理
代理步骤:
1. 定义一个事件管理器类实现invocationHandle接口,并重写invoke(代理类,被代理的方法,方法的参数列表)方法。
2. 实现被代理类及其实现的接口,
3. 调用Proxy.newProxyInstance(类加载器,类实现的接口,事务处理器对象);生成一个代理实例。
4. 通过该代理实例调用方法。
- 创建JDK动态代理工厂。买票接口Ticket 和买票接口实现类TicketImpl跟上面(静态代理)一致
package com.lw.designpattern.proxy.jdkdynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @Classname JdkProxyFactory * @Description (买票)JDK代理工厂 * @Author lw * @Date 2019-12-27 08:57 */ public class JdkProxyFactory { /** 目标对象 */ private Object target; public JdkProxyFactory(Object target){ this.target = target; } 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 returnValue = method.invoke(target, args); System.out.println("代理买票成功"); return returnValue; } } ); } }
- 单元测试
/** * 代理模式-JDK动态代理 */ @Test public void testJdkDynamicProxy(){ // 目标对象 Ticket ticket = new TicketImpl(); // 给目标对象,创建代理对象 Ticket proxy = (Ticket) new JdkProxyFactory(ticket).getProxyInstance(); // 买票 proxy.buyTicket(); }
打印结果
5.3 CGLib动态代理
- 创建CGLib动态代理工厂。买票接口Ticket 和买票接口实现类TicketImpl跟上面(静态代理)一致
package com.lw.designpattern.proxy.cglibdynamicproxy; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @Classname CglibProxyFactory * @Description (买票)CGLib动态代理 * @Author lw * @Date 2019-12-27 09:02 */ public class CglibProxyFactory implements MethodInterceptor { /** 目标对象 */ private Object target; public CglibProxyFactory(Object target) { this.target = target; } /** * 给目标对象创建一个代理对象 * * @return Object */ public Object getProxyInstance(){ // 1.工具类 Enhancer en = new Enhancer(); // 2.设置父类 en.setSuperclass(target.getClass()); // 3.设置回调函数 en.setCallback(this); // 4.创建子类(代理对象) return en.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("代理买票开始"); // 执行目标对象的方法 Object returnValue = method.invoke(target, objects); System.out.println("代理买票成功"); return returnValue; } }
- 单元测试
/** * 代理模式-CGLib动态代理 */ @Test public void testCglibDynamicProxy(){ // 目标对象 Ticket ticket = new TicketImpl(); // 代理对象 Ticket proxy = (Ticket)new CglibProxyFactory(ticket).getProxyInstance(); // 买票 proxy.buyTicket(); }
打印结果
6.静态代理、JDK动态代理、CGLib动态代理三者的区别
静态代理:
- 目标对象要实现接口,而且代理对象需要实现接口。
- 这样的代理类只能代理特定接口的对象,灵活度不够。
JDK动态代理:
- 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。
CGLib动态代理:
- 代理对象不需要实现接口,目标对象也可以是单纯的一个对象。
7.代理模式的应用场景
- 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。
- 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。
- 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
- 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。
- 延迟加载,指为了提高系统的性能,延迟对目标的加载。