代理模式的理解

    首先代理二字的含义,程序中代理与字面意思的代理并无区别。比如现实生活中办理车辆审车,我们经常会听说花钱找代理(又称黄牛)办手续,即办手续这个事,不是我们亲自执行,而是通过代理(即黄牛)去车管所办理。再比如联系明星商业出演,那明星一般也不会直接和商家对线,而是通过明星的经纪人协商时间地点出场费等。
    换到程序中代理二字也是上述含义:即调用一个对象时,不是直接与此对象示例交互,而是通过对象的代理来完成交互。这就是代理最基本的含义。
    那增加这层代理的好处也可类比现实生活进行理解:
  • 直观的,可以避免调用方和被调用的对象产生直接的联系。也就是程序设计中耦合度问题。
  • 其次,通过代理可以实现目标对象本身所不具备的行为功能。比如明星只负责演出,而经纪人则负责安排档期、经费、缴税等工作。
基于上述的现实案例做铺垫,我们梳理并识别代理模式涉及的几个组成部分:
  • 目标对象:车主、明星
  • 代理人:黄牛、经纪人
  • 行为:办审核手续、唱歌跳舞
  • 提出方、调用方:警察、商人

说回到程序中,代理主要有以下两种形式:

代理的形式之一:静态代理

这里采用明星商演的场景来演示:
public interface IStar {
void SingAsong(String songName);
}
public class Star implements IStar {
private String _name;
public Star(String name) {
this._name = name;
}
@Override
public void SingAsong(String songName) {
System.out.println("i am " + _name + ",I'm singing....");
}
}
public class StaticStarProxy implements IStar {
Star zhangsanStar = new Star("zhangsan");
@Override
public void SingAsong(String songName) {
this.PlanSingDate();
zhangsanStar.SingAsong(songName);
this.PayingTax();
}
private String PlanSingDate() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.MONTH, 2);
Date singDate = calendar.getTime();
System.out.println("sing date =" + singDate);
return singDate.toString();
}
private void PayingTax() {
System.out.println("payint tex....");
}
}

public class Main {
public static void main(String[] args) { System.out.println("starting...."); //直接调用,直接与明星对线
IStar zhangsanStar = new Star("zhangsan");
zhangsanStar.SingAsong("molihua");
System.out.println();
System.out.println("stoping");
}
上述示例代码中,Star就是明星本人,StarProxy就是代理人,IStar就是行为,Main就是调用方。
程序中静态一般是代表稳定不变的、相对固定的描述。静态代理也就是说代理是相对不变化的,代理程序(StarProxy)怎么写的一般就不会变化。
很显然,明星不止一个,并且明星的行为也不仅仅是唱歌。采用静态代理就会产生几个问题:
  • 如果采用多个明星对应多个代理类的话,那么就会产生大量的代理类出现。【虽然在现实生活中是合理的,但是程序中一般追求精简】
  • 如果采用多个明星对应一个代理类,那么这个代理类的实现就会很复杂,且代理类变更频繁。
  • 还有一个问题,如果明星增加技能(比如跳舞),那么代理类就需要同步的变更。
于是,程序中提出了动态代理来解决这些问题。

代理的形式之二:动态代理

所谓动态代理,直观理解就是代理是动态生成的,而不需要提前编写好。

public class DnyamicStarProxy implements InvocationHandler {
IStar _targetStar; public DnyamicStarProxy(IStar star) {
this._targetStar = star;
} @Override
public Object invoke(Object proxyInstance, Method method, Object[] objects) throws Throwable {
this.PlanSingDate();
Object invokeResult = method.invoke(_targetStar, objects);
this.PayingTax();
return invokeResult;
} private String PlanSingDate() {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.MONTH, 2);
Date singDate = calendar.getTime();
System.out.println("sign date =" + singDate);
return singDate.toString();
} private void PayingTax() {
System.out.println("the agent is paying tex....");
}
}

public class Main {
public static void main(String[] args) { System.out.println("starting...."); //直接调用,直接与明星对线
IStar zhangsanStar = new Star("zhangsan");
zhangsanStar.SingAsong("molihua");
System.out.println(); //代理模式,通过经纪人对线
IStar zhangsanProxy = (IStar) new StaticStarProxy();
zhangsanProxy.SingAsong("molihua");
System.out.println(); //代理模式,动态代理调用
System.out.println("邀请李四演出:");
IStar lisiStar = new Star("lisi");
IStar lisiStarProxy = (IStar) Proxy.newProxyInstance(IStar.class.getClassLoader()
, new Class[]{IStar.class}
, new DnyamicStarProxy(lisiStar));
lisiStarProxy.SingAsong("molihua");
System.out.println(); System.out.println("邀请王五演出:");
IStar wangwuStar = new Star("wangwu");
IStar wangwuStarProxy = (IStar) Proxy.newProxyInstance(IStar.class.getClassLoader()
, new Class[]{IStar.class}
, new DnyamicStarProxy(wangwuStar));
wangwuStarProxy.SingAsong("molihua");
System.out.println(); System.out.println("stoping");
}
输出结果:
starting....
i am zhangsan,I'm singing.... sing date =Wed Mar 16 18:14:17 CST 2022
i am zhangsan,I'm singing....
payint tex.... 邀请李四演出:
sign date =Wed Mar 16 18:14:17 CST 2022
i am lisi,I'm singing....
the agent is paying tex.... 邀请王五演出:
sign date =Wed Mar 16 18:14:17 CST 2022
i am wangwu,I'm singing....
the agent is paying tex.... stoping 进程已结束,退出代码为 0
 
首先对代码中陌生的类和方法做一个说明:
InvocationHandler类的Object invoke(Object o, Method method, Object[] objects)
  • 入参o代表:动态生成的代理类对象
  • 入参method代表;调用方调用的哪个方法,演示代码中即代表SingAsong方法
  • 入参objects代表:调用方法传入的参数,即实参
 
Proxy类的static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • 参数loader:代表java反射中的类加载器
  • 参数interfaces:代表依赖的接口
  • 参数h:代表动态代理类的的对象,即DnyamicStarProxy
 
站在调用方角度来说,可以这么理解:调用SingAsong时不是直接new一个Star实例就调用了,而是通过Proxy.newProxyInstance动态的创建一个代理类的对象,然后通过此代理类的对象来调用。
 
通过演示代码,可以看出以下特征:
  • 只有一个DnyamicStarProxy类,而省掉了具体的各个代理人的类;
  • 对商人(调用方)来说,只需要提前知道想要邀请的明星(李四、王五),然后通过特定的代码就生成了明星经纪人对象(lisiStarProxy、wangwuStarProxy)
  • 得到经纪人对象后,就可以通过经纪人对象调用执行具体的行为(唱歌)。
同时,我们梳理分析看看动态代理方式的各个组成部分的依赖情况:
  • 目标对象:Star,没有额外的变化
  • 代理人:省掉了具体的xxx代理人,转为DnyamicStarProxy类,从而依赖InvocationHandler类。
  • 行为:IStar,没有额外的变化
  • 调用方:依赖IStar接口、特定的Star类对象、Proxy类、DnyamicStarProxy类
 
现在,我们考虑一个问题:演示代码中是如何调用了Star类的Sing方法?
根据程序执行输出,可以判断是在DnyamicStarProxy.Invoke中调用的。于是可以看到反射技术的应用,即method.invoke()处利用反射真正执行了Star的SingAsong()方法。
 

动态代理的一些常见思考题:

Java常用的动态代理技术有哪些?

本次演示程序虽然用到反射技术来实现动态代理,但也有其它技术来实现比如CGLIB等

演示代码中,调用方还是new了具体的明星类(lisiStar、wangwutar),这就产生了严重的依赖。有什么办法可以解除这个依赖?

DnyamicStarProxy.Invoke中通过反射执行了目标对象的方法(Star.SingAsong),此时执行的代码是本地的代码。如果这里通过socket将方法名、入参发送到另一台server上执行,然后把结果在再通过网络返回,这就是RPC框架中常用的动态代理了。

虽然是动态代理,但根据前面的铺垫,按说也是会生成代理类的吧,那动态生成的代理长什么样?

    确实是运行期间动态生成了代理类,如下所示:
注意动态代理类的SingAsong方法;
查看代码


//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
// import Stars.IStar;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException; public final class DynamicGeneratedStarProxyClass extends Proxy implements IStar {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0; public DynamicGeneratedStarProxyClass(InvocationHandler var1) throws {
super(var1);
} public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
} public final void SingAsong(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
} public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("Stars.IStar").getMethod("SingAsong", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
 

动态代理及java演示的更多相关文章

  1. 动态代理:JDK原生动态代理(Java Proxy)和CGLIB动态代理原理+附静态态代理

    本文只是对原文的梳理总结,以及自行理解.自己总结的比较简单,而且不深入,不如直接看原文.不过自己梳理一遍更有助于理解. 详细可参考原文:http://www.cnblogs.com/Carpenter ...

  2. 实现动态代理(Java和spring)

    一.Java实现动态代理 1.创建接口 package com.oyy.mw.biz.i; public interface Cal { public int add(int num1,int num ...

  3. 设计模式之动态代理(Java的JDK动态代理实现)

    先来看一下思维导图: 对于JDK的动态代理,孔浩老师说学习的方法是把它记下来. 先写一个主题接口类,表示要完成的一个主题. package com.liwei.dynaproxy; /** * 要代理 ...

  4. Java JDK 动态代理使用及实现原理分析

    转载:http://blog.csdn.net/jiankunking   一.什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理 ...

  5. Java动态代理深入解析

    要想了解Java动态代理,首先要了解什么叫做代理,熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中,有一种叫做代理(Proxy)的对象结构型模式,动态代理中的代理,指的就是这种设计模式. 在我 ...

  6. Java编程的逻辑 (86) - 动态代理

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  7. java动态代理_aop2

      一.什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 代理模 ...

  8. Java基础加强-(注解,动态代理,类加载器,servlet3.0新特性)

    1.   Annotation注解 1.1.  Annotation概述 Annotation是JDK 5.0以后提供对元数据的支持,可以在编译.加载和运行时被读取,并执行相应的处理.所谓Annota ...

  9. Java动态代理——框架中的应用场景和基本原理

    前言 之前已经用了5篇文章完整解释了java动态代理的原理,本文将会为这个系列补上最后一块拼图,展示java动态代理的使用方式和应用场景 主要分为以下4个部分 1.为什么要使用java动态代理 2.如 ...

随机推荐

  1. AtCoder Beginner Contest 173 题解

    AtCoder Beginner Contest 173 题解 目录 AtCoder Beginner Contest 173 题解 A - Payment B - Judge Status Summ ...

  2. java 、abstract修饰的【抽象类】【比如几何图形类】

    现实中问题引入 现实中一类具有共同特征的类,但是无法具体实现.,比如我们定义了一个几何类,叫做Shape,我们有一个方法要计算周长,直接在每个子类实现虽然可以,但是无法通过Shape定义的实例来访问到 ...

  3. JS使用html()获取html代码获取不到input、textarea控件填写的值

    我们可以重写一个方法 (function ($) { var oldHTML = $.fn.html; $.fn.formhtml = function () { if (arguments.leng ...

  4. Linux(centos7)设置docker服务开机自启动以及容器自启动

    docker服务开机自启动 systemctl enable docker 设置容器自启动 可以在运行的时候通过设置--restart 参数 docker run --restart always - ...

  5. ARTS Week 18

    Algorithm 本周的 LeetCode 题目为 55. 跳跃游戏 给定一个非负整数数组 nums, 你最初位于数组的 第一个下标 .数组中的每个元素代表你在该位置可以跳跃的最大长度.判断你是否能 ...

  6. codeforc 603-A. Alternative Thinking(规律)

    A. Alternative Thinking time limit per test 2 seconds memory limit per test 256 megabytes   Kevin ha ...

  7. MySQL 尽量避免使用 TIMESTAMP

    MySQL 中常见的时间类型有三种DATE, DATETIME和 TIMESTAMP,其中DATE类型用于表示日期,但是不会包含时间,格式为YYYY-MM-DD,而DATETIME和TIMESTAMP ...

  8. 【系统分析】《系统分析与设计方法》 Jeffrey L.Whitten 第1部分 系统开发环境 第3章 信息系统开发

    1.解释为什么对企业来说,拥有一个标准的系统开发过程很重要. 开发过程的成熟,使项目时间和费用减少,生产率和质量提高 2.如何关联系统生命周期和系统开发方法学? 系统生命周期包含系统开发阶段,其中使用 ...

  9. Proximal Algorithms 5 Parallel and Distributed Algorithms

    目录 问题的结构 consensus 更为一般的情况 Exchange 问题 Global exchange 更为一般的情况 Allocation Proximal Algorithms 这一节,介绍 ...

  10. matplotlib 高阶之Transformations Tutorial

    目录 Data coordinates Axes coordinates Blended transformations 混合坐标系统 plotting in physical units 使用off ...