Proxy,也就是“代理”了。

意思就是。你不用去做,别人取代你去处理。比方说:租房。你仅仅要找到“我爱我家”中介,把全部的事情交给他们去代劳,

“我爱我家”此时就是你的Proxy。

它在程序开发中起到了非常关键的数据,比方传说中的 AOP(面向切面编程)。就是针对代理的一种应用。

此外,在设计模式中,另一个“代理模式”。在公司里要上外网,要在浏览器里设置一个 HTTP 代理。

wocao。代理无处不在啊,不搞明确,岂不是非常没面子?

凡是都要由浅入深,学习也不例外。先来一个 Hello World 吧:

public interface Hello {
void say(String name);
}

这是一个 Hello 接口,不用解释了,大家都懂的。

赶紧上实现类吧:

public class HelloImpl implements Hello{
@Override
public void say(String name) {
System.out.println("Hello! " + name);
}
}

wocao!这简直就是
So easy 嘛。可是有个问题:假设要在 println() 方法前面和后面分别须要处理一些逻辑,怎么做呢?把这些逻辑写死在 say() 方法里面吗?肯定不够优雅啦,菜鸟一般这样干。作为一名资深的程序猿。我坚决不能这样做!

我要用代理。写一个 HelloProxy 类,让它去调用 HelloImpl 的 say() 方法,在调用的前后分别进行逻辑处理不即可了吗?赶紧搞一个吧:

public class HelloProxy  implements Hello{
private HelloImpl helloImpl;
public HelloProxy(){}
public HelloProxy(HelloImpl helloImpl){
this.helloImpl = helloImpl;
}
@Override
public void say(String name) {
before();
helloImpl.say(name);
after();
}
private void before() {
System.out.println("Before");
}
private void after() {
System.out.println("After");
}
}

我将 HelloProxy 类实现了 Hello 接口(和 HelloImpl 实现同样的接口),而且在构造方法中 new 出一个 HelloImpl 类的实例。

这样一来,我就能够在 HelloProxy 的 say() 方法里面去调用 HelloImpl 的 say() 方法了。

更重要的是。我还能够在调用的前后分别加上 before() 与 after() 方法,在这两个方法里去实现那些前后逻辑。

用一个 main 方法来測试一下吧:

public class StaticClient {
public static void main(String[] args) {
Hello helloProxy = new HelloProxy(new HelloImpl());
helloProxy.say("Jack");
}
}

执行后,打印出:

Before

Hello! Jack

After

轻而易举,我就写出了这么优雅的代码(暗自小嗨了一把)。

不久后,我在一本设计模式的书上看到,原来我写的这个 HelloProxy 就是所谓的“代理模式”啊!

我仅仅能说,自己和 GoF(四人帮)的距离有接近了一点。

于是我疯狂的使用“代理模式”,项目中到处都有 XxxProxy 的声影。直到有一天。架构师看到了我的代码,他惊呆了!他对我说:“你怎么这么喜欢用静态代理呢?你就不会用动态代理吗?给我全都重构了!”。

我表面上点了点头,说:“好的!

”。事实上我根本都不知道什么是“静态代理”,什么又是“动态代理”。我继续翻开我那本垫桌脚的设计模式。深入得研究了一番,最后我才明确,原来我一直用的都是“静态代理”啊,怪不得架构师说我到处都是 XxxProxy 类了。

我好恨我自己!一定要将这些垃圾 Proxy 都重构为“动态代理”。

于是我就是用 JDK 给我们提供的动态代理方案,写了一个 DynamicProxy:

public class DynamicProxy  implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void before() {
System.out.println("Before");
}
private void after() {
System.out.println("After");
}
}

在 DynamicProxy 类中。我定义了一个 Object 类型的 target 变量,它就是被代理的目标对象,通过构造函数来初始化(如今流行叫“注入”了,我认为叫“射入”也不错哦。构造函数初始化叫“正着射”。所以 reflect 方式就叫“反着射”,简称“反射”)。

言归正传。DynamicProxy 实现了 InvocationHandler 接口。那么必须实现该接口的 invoke 方法,參数不做解释,望文生义吧,是 JRE 给我们“射”进来的。在该方法中。直接通过反射去 invoke method,在调用前后分别处理 before 与 after,最后将 result 返回。

写一个 main() 方法看看实际怎么用吧:

public class DynamicClient {
public static void main(String[] args) {
Hello hello = new HelloImpl();
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
Hello helloProxy = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
dynamicProxy
);
helloProxy.say("Jack");
}
}

没错,意思就是,用我写的这个通用的 DynamicProxy 类去包装 HelloImpl 实例,然后再调用 JDK 给我们提供的 Proxy 类的工厂方法 newProxyInstance() 去动态地创建一个 Hello 接口的代理类。最后调用这个代理类的 say() 方法。

执行一下,结果和曾经一样。动态代理成功了。

事实上,动态代理就是帮我们自己主动生成 XxxProxy 类的法宝啊。

要注意的是。Proxy.newProxyInstance() 方法的參数实在是让我“无语吐槽”。

參数1:ClassLoader

參数2:该实现类的全部接口

參数3:动态代理对象

调用完了还要来一个强制类型转换一下。

wocao!

这一坨 shi 一定要想办法封装一下,避免再次发生到处都是 Proxy.newProxyInstance(),这样架构师又要骂我了。

于是我将这个 DynamicProxy 重构了:

public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
@SuppressWarnings("unchecked")
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
private void before() {
System.out.println("Before");
}
private void after() {
System.out.println("After");
}
}

我在 DynamicProxy 里加入了一个 getProxy() 方法,无需传入不论什么參数。将刚才所说的那一坨 shi。放在这种方法中,而且该方法返回一个泛型类型,就不会强制类型转换了。方法头上加那个 @SuppressWarnings("unchecked") 注解表示忽略编译时的警告(由于 Proxy.newProxyInstance() 方法返回的是一个 Object,这里我强制转换为 T 了,这是向下转型,IDE
中就会有警告,编译时也会出现提示,非常烦)。

好了,这下子使用 DynamicProxy 就简单了吧:

public class DynamicClient {
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImpl());
Hello helloProxy = dynamicProxy.getProxy();
helloProxy.say("Jack");
}
}

确实简单用 2 行代理就去掉了前面的 7 行代码(省了 5 行),架构师看到了这种代码肯定会表扬我!

经过一番代码重构后。我提交了全部的代码。架构师看到了。没有吱声…… 可我总算学会了动态代理。

用了这个 DynamicProxy 以后,我认为它还是很爽的,爽的地方是,接口变了。这个动态代理类不用动。而静态代理就不一样了。接口变了。实现类还要动。代理类也要动。

但我也发现动态代理并非“万灵丹”,它也有搞不定的时候,比方说,我要代理一个没有不论什么接口的类,它就没有勇武之地了!这就是 JDK 给我们提供的动态代理。让我不知道该说什么了。

于是我又開始调研,是否能代理没有接口的类呢?最终让我找到了这颗“银弹”!

那就是 CGLib 这个类库。尽管它看起来不太起眼,但 Spring、Hibernate
这样牛逼的开源框架都用到了它。它就是一个在执行期间动态生成字节码的工具,也就是动态生成代理类了。

说起来好高深。实际用起来一点都不难。我再搞一个 CGLibProxy 吧:

public class CglibProxy implements MethodInterceptor, Serializable {
private static final long serialVersionUID = 1L;
private void doBefore() {
System.out.println("before method invoke");
}
private void doAfter() {
System.out.println("after method invoke");
}
public Object getProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
enhancer.setClassLoader(target.getClass().getClassLoader());
return enhancer.create();
}
public Object getProxy(Class<? extends Object> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
enhancer.setInterfaces(new Class[] { Serializable.class });
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
try {
// 调用之前
doBefore();
// 调用原始对象的方法
Object result = proxy.invokeSuper(obj, args);
// 调用之后
doAfter();
return result;
} catch (Throwable e) {
throw e;
}
}
}

须要实现 CGLib 给我们提供的 MethodInterceptor 实现类。并填充 intercept() 方法。方法中最后一个 MethodProxy 类型的參数 proxy,值得注意!CGLib 给我们提供的是方法级别的代理。也能够理解为对方法的拦截(这不就是传说中的“方法拦截器”吗?)。

这个功能对于我们这群屌丝程序猿而言,如同雪中送炭啊,此乃神器也!我们直接调用 proxy 的 invokeSuper() 方法。将被代理的对象 obj 以及方法參数 args 传入当中就可以。

与 DynamicProxy 类似,我在 CGlibProxy 中也加入了一个泛型的 getProxy() 方法。便于我们能够高速地获取自己主动生成的代理对象。还是用一个 main() 方法来描写叙述吧:

public class CglibClient {
public static void main(String[] args) {
CglibProxy cglib = new CglibProxy(); HelloImpl proxy = (HelloImpl)cglib.getProxy(new HelloImpl());
proxy.say("Somnus"); System.out.println("*****************************************************************"); HelloImpl proxy2 = (HelloImpl)cglib.getProxy(HelloImpl.class);
proxy2.say("Somnus");
}
}

仍然通过 2 行代码就能够返回代理对象了,与 JDK 动态代理不同的是,这里不须要不论什么的接口信息,对谁都能够生成动态代理对象(无论它是“屌丝”还是“高富帅”)。说它是神器,过分吗?

我一向都是以追求完美而著称。2 行代码返回代理对象,我认为还是有些多余,我不想总是去 new 这个 CGLibProxy 对象,最好 new 一次,以后随时拿随时用。

于是我想到了“单例模式”:

public class CglibProxy implements MethodInterceptor {
private static CglibProxy instance = newCglibProxy();
private CglbProxy() {
}
public static CglibProxy getInstance() {
return instance;
}
...
}

我加了以上几行代码,就搞定了!

须要说明的是:这里有一个 private 的构造方法,就是为了限制外界不能再去 new 它了,换句话说,我在这里把它给“阉”了。

用一个 main() 方法来证明我的简单主义思想:

public static void main(String[] args) {
HelloImpl helloImpl = CglibProxy.getInstance().getProxy(HelloImpl.class);
helloImpl.say("Jack");
}

没错吧?仅仅需 1 行代码就能够获代替理对象了!

总结一下,我们今天谈到了无代理、静态代理、JDK 动态代理、CGLib 动态代理,事实上代理的世界远不止这么小,还有非常多实际的应用场景。本文一開始谈

到的 AOP 是一个最为典型的案例,所以有必要再进行继续下去。

代理Proxy初探的更多相关文章

  1. 代理(Proxy)和反射(Reflection)

    前面的话 ES5和ES6致力于为开发者提供JS已有却不可调用的功能.例如在ES5出现以前,JS环境中的对象包含许多不可枚举和不可写的属性,但开发者不能定义自己的不可枚举或不可写属性,于是ES5引入了O ...

  2. 深度揭秘ES6代理Proxy

    最近在博客上看到关于ES6代理的文章都是一些关于如何使用Proxy的例子,很少有说明Proxy原理的文章,要知道只有真正掌握了一项技术的原理,才能够写出精妙绝伦的代码,所以我觉得有必要写一篇关于深刻揭 ...

  3. Webpack代理proxy配置,解决本地跨域调试问题,同时允许绑定host域名调试

    Webpack代理proxy配置,解决本地跨域调试问题,同时允许绑定host域名调试 会撸码的小马 关注 2018.05.29 17:30* 字数 212 阅读 1488评论 0喜欢 2 接到上一章, ...

  4. 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance

    浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...

  5. 初识代理——Proxy

    无处不在的模式——Proxy 最近在看<设计模式之禅>,看到代理模式这一章的时候,发现自己在写spring项目的时候其实很多时候都用到了代理,无论是依赖注入.AOP还是其他,可以说是无处不 ...

  6. JAVA设计模式-动态代理(Proxy)示例及说明

    在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...

  7. JAVA设计模式-动态代理(Proxy)源码分析

    在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...

  8. java动态代理--proxy&cglib

    大纲 代理 proxy cglib 小结 一.代理 为什么要用代理?其实就是希望不修改对象的情况下,增强对象. 静态代理: 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法, ...

  9. 神秘代理-Proxy

    前言: 代理模式作为常见的设计模式之一,在项目开发中不可或缺.本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正! 1.如何实现代理: [假设有个关于汽车移动(move)的计时需求]设计:Movea ...

随机推荐

  1. go语言之进阶篇创建goroutine协程

    1.goroutine是什么 goroutine是Go并行设计的核心.goroutine说到底其实就是协程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现 ...

  2. 创想三维:5款最好用的免费3D建模软件【转】

    虽然网上有需要现成的免费三维模型,但对于许多人而言,3D打印机最吸引他们之处是可以设计创造完全属于自己的模型.问题是,现代专业级CAD软件大多价格高昂,例如Solidworks或Zbrush这样的程序 ...

  3. 线程 Thread Runnable 守护线程 join MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. ASP.NET压力测试

    本文导读:对于直接面对互联网用户的WEB应用,在开发设计的时候必须格外小心,因为谁也不知道在单位时间内WEB程序访问和运行的速度.所以,在程序设计完成以后,最后针对程序进行一些严格的甚至是苛刻的测试, ...

  5. nodejs 项目的session验证

    原文:https://www.codexpedia.com/node-js/a-very-basic-session-auth-in-node-js-with-express-js/ -------- ...

  6. Linux杀毒软件ClamAV初次体验

    1:官网 http://www.clamav.net 2:Ubuntu下安装ClamAV sudo apt-get update--更新系统 sudo apt-get install clamav-- ...

  7. Linux扩展文件分区

    **************操作之前请看章节6,看系统是否支持LVM分区管理方式*************** 1:新增磁盘 插入新的磁盘,比如物理机可以直接在卡槽插入,虚拟机可以在控制台添加磁盘或者 ...

  8. linux不解压超大日志gz包直接查找特定内容

    gzip -dc myfile.gz | grep 'Exception' | more 或者 gzip -c myfile.gz | grep 'Exception' | more

  9. 【Python】安装geocoder

    C:\Users\horn1\Desktop\python\49-geo>pip install geocoder Collecting geocoder Downloading https:/ ...

  10. 【Nodejs】使用request批量下载MP3,文件数量内容都没问题

    看来request远强于http.request是毋庸置疑的了. 代码如下: //====================================================== // 喜 ...