一、代理模式的应用场景 在我们的生活中,经常会见到这样的场景,如:租售房中介、婚介、经纪人、快递等,这些都是代理模式的现实生活体现。代理模式(Proxy Pattern)是指为其它对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象中间起到了中介的作用,使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。  
代理模式的类图结构:
Subject是顶层设计的接口,RealSubject是真实的对象,Proxy是代理对象,代理对象持有真实对象的引用,客户端Client调用代理对象的方法,同时也调用真实对象的方法,在代理对象前后增加一些处理。我们一想到代理模式,就会理解为代码增强 ,其实就是在原本的代码逻辑前后增加一些逻辑,而使得调用者无感知。代理模式分为静态代理和动态代理。
二、代理模式的分类 2.1 静态代理 我们直接来举例说明静态代理,青年男女到了适婚的年龄,如果没有对象,周围的亲戚朋友总是张罗着要给某某某介绍对象,这个介绍对象相亲的过程,就是一种我们人人都有份的代理。来看代码实现:
顶层接口设计Person类:
1 2 3 4 5 6 7 public interface Person {     /**      * 寻找伴侣      */     void lookForMate(); } 
 
女儿要求找对象,实现Person接口:
1 2 3 4 5 6 public class Daughter implements Person {     @Override     public void lookForMate() {         System.out.println("女儿要求:高大英俊且有钱!");     } } 
 
母亲要帮闺女相亲,实现Mother类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Mother {     private Daughter daughter;     //如何扩展呢     public Mother(Daughter daughter) {         this.daughter = daughter;     }     //目标对象的引用daughter拿到,可以调用     public void lookForMate() {         System.out.println("母亲物色女儿的对象");         daughter.lookForMate();         System.out.println("双方同意交往并确立关系");     }      } 
 
测试内容:
1 2 3 4 5 public static void main(String[] args) {     //只能帮女儿找对象,不能帮表妹、不能帮陌生人     Mother mother = new Mother(new Daughter());     mother.lookForMate(); } 
 
运行结果:
上面的这个例子是生活中的例子,我们用代码实现了生活中的代理模式。再来一个具体的实际业务场景的例子吧。我们经常会对数据库进行分库分表,分库分表后用Java代码来操作,就需要配置多个数据源,通过设置数据源路由来动态动态切换数据源。 创建订单实体类:
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 /**  * 订单实体类  */ public class Order {     private String id;     private Object orderInfo;     private Long createTime;     public String getId() {         return id;     }     public void setId(String id) {         this.id = id;     }     public Object getOrderInfo() {         return orderInfo;     }     public void setOrderInfo(Object orderInfo) {         this.orderInfo = orderInfo;     }     public Long getCreateTime() {         return createTime;     }     public void setCreateTime(Long createTime) {         this.createTime = createTime;     } } 
 
创建OrderDao持久层操作类:
1 2 3 4 5 6 public class OrderDao {     public int insert(Order order) {         System.out.println("创建order对象成功!");         return 1;     } } 
 
创建 IOrderService 接口:
1 2 3 public interface IOrderService {     int createOrder(Order order); } 
 
创建 OrderService 实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class OrderService implements IOrderService {     private OrderDao orderDao;     public OrderService(OrderDao orderDao) {         orderDao = new OrderDao();     }     @Override     public int createOrder(Order order) {         System.out.println("OrderService调用OrderDao创建订单");         return orderDao.insert(order);     } } 
 
我们来使用静态代理,完成订单创建时间自动按年份进行分库,通过使用代理对象来完成接下来的代码。创建数据源路由对象,使用ThreadLocal单例实现,DynamicDataSourceEntity:
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 DynamicDataSourceEntity {     /**      * 默认数据源      */     public static final String DEFAULT_DATA_SOURCE = null;     private static final ThreadLocal<String> local = new ThreadLocal<>();     private DynamicDataSourceEntity() {}     /**      * 获取当前正在使用的数据源      * @return      */     public static String get() {         return local.get();     }     /**      * 设置已知名字的数据源      * @param dataSource      */     public static void set(String dataSource) {         local.set(dataSource);     }     /**      * 还原当前切面的数据源      */     public static void restore() {         local.set(DEFAULT_DATA_SOURCE);     }     /**      * 根据年份动态设置数据源      * @param year      */     public static void set(int year) {         local.set("DB_" + year);     }     /**      * 清空数据源      */     public static void remove() {         local.remove();     } } 
 
创建切换数据源代理OrderServiceStaticProxy:
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 public class OrderServiceStaticProxy implements IOrderService {     private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");     private IOrderService orderService;     public OrderServiceStaticProxy(IOrderService orderService) {         this.orderService = orderService;     }     @Override     public int createOrder(Order order) {         before();         Long time = order.getCreateTime();         Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));         System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据。");         DynamicDataSourceEntity.set(dbRouter);         orderService.createOrder(order);         after();         return 0;     }     private void before(){         System.out.println("代理方法执行开始了......");     }     private void after(){         System.out.println("代理方法执行结束了......");     } } 
 
main方法的代码:
 public static void main(String[] args) throws ParseException {
Order order = new Order();
order.setId("010101001");
//Date today = new Date();
//order.setCreateTime(today.getTime());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
Date date = sdf.parse("2019/02/01");
order.setCreateTime(date.getTime());
IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
orderService.createOrder(order);}
运行结果是:
符合我们的预期效果。现在我们再来回顾一下类图,看是不是和我们最先画的类结构一致:
2.2 动态代理 动态代理和静态代理的思路基本是一致的,只不过动态代理的功能更加强大,随着业务的扩展适应性更强。前面说到的母亲替闺女找对象的例子,如果找对象的业务发展为一个行业,那么就是婚姻中介了。来升级代码的实现过程,以满足帮助更多的单身人士找对象的需求。下面使用JDK的方式实现婚姻介绍所。
2.2.1 JDK实现方式 创建婚姻介绍JDKMarriage类:
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 public class JDKMarriage implements InvocationHandler {     private Object target;     public Object getInstance(Object target) {         this.target = target;         Class<?> clazz = target.getClass();         return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);     }     @Override     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {         before();         Object object = method.invoke(this.target, args);         after();         return object;     }     private void before(){         System.out.println("我是婚姻介绍所:要给你找对象,现在已经拿到你的需求");         System.out.println("开始物色");     }     private void after(){         System.out.println("如果合适的话,就准备办事");     } } 
 
创建单身客户Customer类:
1 2 3 4 5 6 7 8 public class Customer implements Person {     @Override     public void lookForMate() {         System.out.println("高富帅");         System.out.println("身高180cm");         System.out.println("有房有车");     } } 
 
测试main方法代码:
1 2 3 4 5 6 7  public static void main(String[] args) {     JDKMarriage marriage = new JDKMarriage();     Person person = (Person) marriage.getInstance(new Customer());     person.lookForMate(); } 
 
运行结果:
上面的动态代理案例通过实现InvocationHandler接口来完成的,在前面的数据源路由业务,也要用动态代理来实现一下,我们来看下代码:
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 public class OrderServiceDynamicProxy implements InvocationHandler {     private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");     public Object target;     public Object getInstance(Object target) {         this.target = target;         Class<?> clazz = target.getClass();         return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);     }     @Override     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {         before(args[0]);         Object object = method.invoke(target, args);         after();         return object;     }     public void before(Object target) {         try {             System.out.println("代理方法执行开始了......");             Long time = (Long)target.getClass().getMethod("getCreateTime").invoke(target);             Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));             System.out.println("动态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据");             DynamicDataSourceEntity.set(dbRouter);         } catch (IllegalAccessException e) {             e.printStackTrace();         } catch (InvocationTargetException e) {             e.printStackTrace();         } catch (NoSuchMethodException e) {             e.printStackTrace();         }     }     public void after() {         System.out.println("代理方法执行结束了......");     } } 
 
测试main方法代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main(String[] args) throws ParseException {         Order order = new Order();         order.setId("010101001"); //        Date today = new Date(); //        order.setCreateTime(today.getTime());         SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");         Date date = sdf.parse("2019/02/01");         order.setCreateTime(date.getTime());         IOrderService orderService = (IOrderService) new OrderServiceDynamicProxy().getInstance(new OrderService());         orderService.createOrder(order); } 
 
运行结果:
依然可以达到想要的运行效果。但是,动态代理实现之后,我们不仅能实现 Order 的数据源动态路由,还可以实现其他任何类的数据源路由。
2.2.2 CGLib代理调用API及原理分析 pom依赖:
1 2 3 4 5 <dependency> 	<groupId>cglib</groupId> 	<artifactId>cglib</artifactId> 	<version>3.3.0</version> </dependency> 
 
还是以婚姻介绍所为例,创建CglibMarriage类:
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 public class CglibMarriage implements MethodInterceptor {     public Object getInstance(Class<?> clazz) throws Exception {         Enhancer enhancer = new Enhancer();         //要把哪个类设置成为生成的新类的父类         enhancer.setSuperclass(clazz);         enhancer.setCallback(this);         return enhancer.create();     }     @Override     public Object intercept(Object o, Method method, Object[] objects,                             MethodProxy methodProxy) throws Throwable {         before();         Object obj = methodProxy.invokeSuper(o, objects);         after();         return obj;     }     private void before(){         System.out.println("我是婚姻介绍所:要给你找对象,现在已经拿到你的需求");         System.out.println("开始物色");     }     private void after(){         System.out.println("如果合适的话,就准备办事");     } } 
 
接着创建单身客户类Customer:
1 2 3 4 5 6 7 8 public class Customer {          public void lookForMate() {         System.out.println("高富帅");         System.out.println("身高180cm");         System.out.println("有房有车");     } } 
 
注意:CGLib代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现动态代理的。  来看测试代码:
1 2 3 4 5 6 7 8 9 10 public static void main(String[] args) {         try {             Customer customer = (Customer)new CglibMarriage().getInstance(Customer.class);             customer.lookForMate();         } catch (Exception e) {             e.printStackTrace();         } } 
 
CGLib代理执行代理对象的方法效率之所以比JDK的高,是因为CGLib采用了FastClass机制,FastClass的原理是:为代理类和被代理类各生成一个class,这个class会为代理类或被代理类的方法分配一个index(int类型),这个index当作入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。 
2.2.3 CGLib和JDK动态代理对比 1、JDK动态代理实现了被代理对象的接口,CGLib代理继承了被代理对象。
2、JDK和CGLib都在运行期间生成字节码,JDK动态代理直接生成class字节码,CGLib代理通过asm框架生成class字节码,CGLib代理实现更复杂,生成代理类比JDK动态代理效率低。
3、JDK动态代理调用代理方法是通过反射机制调用的,CGLib代理是通过FastClass机制直接调用方法的,CGLib代理的执行效率高。
三、Spring与代理模式 3.1 代理模式在Spring源码中的应用 先看 ProxyFactoryBean   核心的方法就是 getObject()  方法,我们来看一下源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Nullable public Object getObject() throws BeansException {     this.initializeAdvisorChain();     if (this.isSingleton()) {         return this.getSingletonInstance();     } else {         if (this.targetName == null) {             this.logger.info("Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");         }         return this.newPrototypeInstance();     } } 
 
在 getObject()方法中,主要调用 getSingletonInstance()  和newPrototypeInstance()  ; 在 Spring 的配置中,如果不做任何设置,那么 Spring 代理生成的 Bean 都是单例对象。如果修改 scope则每次创建一个新的原型对象。newPrototypeInstance()里面的逻辑比较复杂,我们后面的课程再做深入研究,这里我们先做简单的了解。
3.2 Spring 中的代理选择原则 Spring 利用动态代理实现 AOP 有两个非常重要的类,一个是 JdkDynamicAopProxy  类和 CglibAopProxy  类,来看一下类图:
当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理; 
 
当 Bean 没有实现接口时,Spring 选择 CGLib。 
 
三、 静态代理和动态的本质区别 1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。
四、代理模式的优缺点 使用代理模式具有以下几个优点:
当然,代理模式也是有缺点的:
在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢;