图截于《大话设计模式》

Proxy模式是常用的设计模式,其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

用户可以更加结构图,自己编码完成Proxy模式。这种实现称为静态代理。

Java提供了java.lang.reflect.Proxy类与InvocationHandler接口,配合反射,可以实现动态代理。静态代理的代理类与代理操作,都是事先编码,运行过程种无法修改代理结构。动态代理的代理与代理操作,都是在运行过程中,动态生成,可以在运行过程中,修改代理结构。

InvocationHandler类提供代理操作行为,动态构建的代理类使用该接口调用代理操作。

Proxy类主要负责动态构建代理类,有以下静态方法:

  • InvocationHandler getInvocationHandler(Object proxy)
  • Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
  • boolean isProxyClass(Class<?> cl)
  • Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

调用getProxyClass()会动态生成Proxy类的子类,并使用loader参数指定的类加载器加载;第二个参数interfaces指定该子类将要继承的接口,可以指定多个接口。

interface Foo {

void funcA();

}

Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);

System.out.println(proxyClass.getName());

for (Class<?> interfaceType : proxyClass.getInterfaces())

System.out.println("\t" + interfaceType);

Class<?> proxyClassB = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class, AutoCloseable.class);

System.out.println(proxyClassB.getName());

for (Class<?> interfaceType : proxyClassB.getInterfaces())

System.out.println("\t" + interfaceType);

输出如下:

testproxy.$Proxy0

interface testproxy.Foo

testproxy.$Proxy1

interface testproxy.Foo

interface java.lang.AutoCloseable

由输出可以推测,调用Proxy.getProxyClass()后,生成子类为:

$Proxy* extends Proxy implements interfasces

*为从0开始编号的整数,代表是第几个被创建的Proxy的子类。interfaces是Proxy.getProxyClass()方法的第二个不定参数。这些类的字节码创建在内存中。例如上面代码生成的子类为:

$Proxy0 extends Proxy implements Foo {...}

$Proxy1 extends Proxy implements Foo, AutoCloseable {...}

实现同接口的Proxy子类,只会被创建一次,拥有共同的Class实例。

Class<?> proxyClassA = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);

Class<?> proxyClassB = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);

System.out.println(proxyClassA.getName());

System.out.println(proxyClassB.getName());

输出如下:

testproxy.$Proxy0

testproxy.$Proxy0

由于Proxy类只有一个带InvocationHandler接口的参数,所有所以需要获取指定版本的构造函数后,传入InvocationHandler接口的实现类获取动态子类的实例。

Foo fooProxy = (Foo) proxyClass.getConstructor(InvocationHandler.class)

.newInstance(handler);

Proxy类提供的Proxy.newProxyInstance(),一步到位,简化了获取动态子类实例的操作。

Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),

new Class<?>[] { Foo.class },

handler)

fooProxy的实例为"$Proxy0 extends Proxy implements Foo {...}"类

InvocationHandler接口有一个invoke()方法需要实现,动态代理类正式调用这个方法执行代理操作。举个例子:

class SimpleHandler implements InvocationHandler {

final private Object target;

public SimpleHandler(Object target) {

// *需要保留委托类的引用

this.target = target;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("---Proxy before---");

// *不适用第一个参数proxy,使用构造函数保留的引用调用委托类方法

Object result = method.invoke(target, args);

System.out.println("---Proxy end---");

return result;

}

}

使用以下委托类和代码测试。

class FooImp implements Foo {

@Override

public void funcA() {

System.out.println("FooImp");

}

}

// Test

Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),

new Class<?>[] { Foo.class },

new SimpleHandler(new FooImp()));

fooProxy.funcA();

System.out.println(fooProxy.toString());

输出如下:

---Proxy before---

FooImp

---Proxy end---

---Proxy before---

---Proxy end---

testproxy.FooImp@4aa298b7

fooProxy的是实例是Proxy类的动态子类,调用该子类的funcA()方法时,实际上先调用了invoke()方法,再通过反射调用委托类的funcA()方法。

为什么toString()方法也被代理了?

实际上,Object的equal()、hashCode()也同时被代理的,这个与Proxy子类的构建过程有关,可以参考资料《JDK动态代理实现原理》。

实现InvocationHandler接口时,红色的注释指出两个疑点:

  • 为什么要保留委托类实例的引用?
  • 为什么不使用第一个参数proxy?

这个两个问题其实是一个问题,invoke()函数的第一个参数proxy引用的实例是Proxy类的动态子类,而不是委托类。如果使用反射调用动态子类的方法,又会再次调用invoke()函数,陷入无限循环中,直到内存移除崩溃。因此,需要保留委托类的引用,让invoke()方法可以调用到委托类的方法。这样又引出一个问题,proxy参数又什么用?

proxy参数可以做为返回值,实现方法链。具体可以参考《Understanding "proxy" arguments of the invoke method of java.lang.reflect.InvocationHandler

Java之动态代理简介的更多相关文章

  1. java之动态代理

    摘要 相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象. 这里说的静态代理可以理解为之前使用的装饰者模式,从之前使用装饰者模式实 ...

  2. 【设计模式】Java设计模式 - 动态代理

    [设计模式]Java设计模式 - 动态代理 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 最近工作比较忙,没啥时间学习 目录 [设计模 ...

  3. java的动态代理机制详解

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

  4. java中动态代理实现机制

    前言: 代理模式是常用的java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系 ...

  5. Java特性-动态代理

    代理在开发中无处不在: 我们完成一个接口开发A,接口下有很多个实现类,这些类有些共同要处理的部分,比如每一个类都定义了接口A中的方法getXX(String name).我现在想把每次调用某个实现类的 ...

  6. java --- 设计模式 --- 动态代理

    Java设计模式——动态代理 java提供了动态代理的对象,本文主要探究它的实现, 动态代理是AOP(面向切面编程, Aspect Oriented Programming)的基础实现方式, 动态代理 ...

  7. java的动态代理机制

    前几天看到java的动态代理机制,不知道是啥玩意,然后看了看.死活不知道 invoke(Object proxy, Method m, Object[] args)种的proxy是个什么东西,放在这里 ...

  8. java中动态代理

    一.在java中怎样实现动态代理 1.我们要有一个接口,还要有一个接口的实现类,而这个实现类呢就是我们要代理的对象 接口: package org.dynamicproxy.test; public ...

  9. Java的动态代理机制详解(转)

    在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...

随机推荐

  1. cheap louis vuitton outlet

    <h1>louis vuitton outlet store</h1>2 nigerian networking systems chosen seeing that enem ...

  2. 如何禁止同IP站点查询和同IP站点查询的原理分析 Robots.txt屏蔽BINGBOT

    很多站长工具中都有“同IP站点查询”.“IP反查域名”这种服务不少人都不知道是什么原理,其实这些服务几乎都是用BING(以前的LIVE)来实现 的,BING有个特别功能 BING抓取页面时会把站点的I ...

  3. leetCode 95.Unique Binary Search Trees II (唯一二叉搜索树) 解题思路和方法

    Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. For e ...

  4. spring源码解析之IOC容器(一)

    学习优秀框架的源码,是提升个人技术水平必不可少的一个环节.如果只是停留在知道怎么用,但是不懂其中的来龙去脉,在技术的道路上注定走不长远.最近,学习了一段时间的spring源码,现在整理出来,以便日后温 ...

  5. 企业级API设计

    最近对service的API设计,在team内有些讨论,主要集中在API是足够抽象.通用好呢, 还是具体.易用好? 其实这个是要折衷的,通用的好处是以后更改API的可能性小,但坏处是想要通用,里面的字 ...

  6. 部署mongodb中需要注意的调参

    部署mongodb的生产服务器,给出如下相关建议: 使用虚拟化环境: 系统配置 1)推荐RAID配置 RAID(Redundant Array of Independent Disk,独立磁盘冗余阵列 ...

  7. 小程序框架MpVue踩坑日记(一)

    小程序也做了几个小功能模块了,总觉得需要总结一下,踩坑什么的还是得记录一下啊. 好吧,其实是为了方便回顾 首先,说到小程序框架,大家都知道wepy,不过,我是没用过 美团开发团队到mpvue到是个实在 ...

  8. Easyui combobox 怎么加载数据

    说明:开发环境 vs2012 asp.net mvc4 c# 1.效果图 2.HTML代码 <%@ Page Language="C#" AutoEventWireup=&q ...

  9. javac 编译源文件出现"java:1: 需要为 class、interface 或 enum"、" [javac] 锘縫"错误

    [javac] HelloWorld.java:1: 需要为 class.interface 或 enum [javac] 锘縫ackage com.csdn.demo; [javac] ^ [jav ...

  10. Spring项目中使用jackson序列化key为对象Map

    1.注入ObjectMapper2.注册类HistoricTaskInstance的序列化和反序列化类HistoricTaskInstanceKeySerializer,HistoricTaskIns ...