代理模式
所有代码地址: https://gitee.com/zyxscuec/Design-pattern.git
文章目录
(1) 概念
代理模式(ProxyPattern)的定义也非常简单,是指为其他对象提供一种代理,以控制对这个对象的访问。
代理对象在客服端和目标对象之间起到中介作用,代理模式属于结构型设计模式。
(2)适用场景
使用代理模式主要有两个目的:一保护目标对象,二增强目标对象。
Subject 是顶层接口,RealSubject 是真实对象(被代理对象),Proxy 是代理对象,代
理对象持有被代理对象的引用,客户端调用代理对象方法,同时也调用被代理对象的方法,但是在代理对象前后增加一些处理。在代码中,我们想到代理,就会理解为是代码增强,其实就是在原本逻辑前后增加一些逻辑,而调用者无感知。代理模式属于结构型模式,有静态代理和动态代理
(3)代码示例
1. 静态代理模式
举个例子:人到了适婚年龄,父母总是迫不及待希望早点抱孙子。而现在社会的人在各种压力之下,都选择晚婚晚育。于是着急的父母就开始到处为自己的子女相亲,比子女自己还着急。这个相亲的过程,就是一种我们人人都有份的代理。来看代码实现:
顶层接口 Person:
package com.alibaba.design.proxypattern; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-10:54 */ public interface Person { public void findLove(); }
儿子要找对象,实现 Son 类:
package com.alibaba.design.proxypattern.staticproxy; import com.alibaba.design.proxypattern.Person; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-10:55 */ public class Son implements Person { @Override public void findLove() { System.out.println("儿子要求:肤白貌美大长腿"); } }
父亲要帮儿子相亲,实现 Father 类:
package com.alibaba.design.proxypattern.staticproxy; import com.alibaba.design.proxypattern.Person; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-10:55 */ public class Father implements Person { private Son son; public Father(Son son){ this.son = son; } @Override public void findLove(){ System.out.println("父亲物色对象"); this.son.findLove(); System.out.println("双方父母同意,确立关系"); } public void findJob(){ } }
来看测试代码:
- FatherTest
package com.alibaba.design.proxypattern.staticproxy; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-10:58 */ public class FatherTest { public static void main(String[] args) { Father father = new Father(new Son()); father.findLove(); //农村,媒婆 //婚介所 //大家每天都在用的一种静态代理的形式 //对数据库进行分库分表 //ThreadLocal //进行数据源动态切换 } }
2. 动态代理模式
动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强。如果还以找对象为例,使用动态代理相当于是能够适应复杂的业务场景。不仅仅只是父亲给儿子找对象,如果找对象这项业务发展成了一个产业,进而出现了媒婆、婚介所等这样的形式。那么,此时用静态代理成本就更大了,需要一个更加通用的解决方案,要满足任何单身人士找对象的需求。我们升级一下代码,先来看 JDK 实现方式:
创建媒婆(婚介)JDKMeipo 类:
package com.alibaba.design.proxypattern.dynamicproxy.jdkproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-10:52 */ public class JDKMeipo implements InvocationHandler { private Object target; public Object getInstance(Object target) throws Exception{ this.target = target; Class<?> clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object obj = method.invoke(this.target,args); after(); return obj; } private void before(){ System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求"); System.out.println("开始物色"); } private void after(){ System.out.println("OK的话,准备办事"); } }
创建单身客户 Customer 类:
package com.alibaba.design.proxypattern.dynamicproxy.jdkproxy; import com.alibaba.design.proxypattern.Person; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-11:11 */ public class Customer implements Person { @Override public void findLove() { System.out.println("高富帅"); System.out.println("身高180cm"); System.out.println("有6块腹肌"); } }
测试代码
package com.alibaba.design.proxypattern.dynamicproxy.jdkproxy; import java.lang.reflect.Method; /** * @author zhouyanxiang * @create 2020-07-2020/7/30-11:11 */ public class JDKProxyTest { public static void main(String[] args) { try { Object obj = new JDKMeipo().getInstance(new Customer()); Method method = obj.getClass().getMethod("findLove",null); method.invoke(obj); }catch (Exception e){ e.printStackTrace(); } } }
(4) 模式在源码中的体现
代理模式在 Spring 源码中的应用,先看 ProxyFactoryBean 核心的方法就是 getObject()方法,我们来看一下源码:
public Object getObject() throws BeansException { initializeAdvisorChain(); if (isSingleton()) { return getSingletonInstance(); }else { if (this.targetName == null) { logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " + "Enable prototype proxies by setting the 'targetName' property."); } return newPrototypeInstance(); } }
在 getObject()方法中,主要调用 getSingletonInstance()和 newPrototypeInstance();在 Spring 的配置中,如果不做任何设置,那么 Spring 代理生成的 Bean 都是单例对象。如果修改 scope 则每次创建一个新的原型对象。
Spring 利用动态代理实现 AOP 有两个非常重要的类,一个是 JdkDynamicAopProxy 类
和 CglibAopProxy 类,来看一下类图:
Spring 中的代理选择原则
- 1、当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理。
- 2、当 Bean 没有实现接口时,Spring 选择 CGLib。
- 3、Spring 可以通过配置强制使用 CGLib,只需在 Spring 的配置文件中加入如下代码:
<aop:aspectj-autoproxy proxy-target-class="true"/>
(5)代理模式的优缺点
- 优点:
1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强。 - 缺点:
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度。
(6) 动态代理模式和静态代理模式的区别
- 1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步
新增,违背开闭原则。 - 2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开
闭原则。 - 3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,
无需修改代理类的代码。