什么是动态代理(dynamic proxy)

动态代理(以下称代理),利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象)

(Using Java Reflection to create dynamic implementations of interfaces at runtime)。

代理的是接口(Interfaces),不是类(Class),更不是抽象类。

动态代理有什么用

解决特定问题:一个接口的实现在编译时无法知道,需要在运行时才能实现

实现某些设计模式:适配器(Adapter)或修饰器(Decorator)

面向切面编程:如AOP in Spring

创建动态代理

利用Java的Proxy类,调用Proxy.newProxyInstance(),创建动态对象十分简单。

InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });

Foo f = (Foo) proxyClass.
getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler }); //或更简单
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);

Proxy.newProxyInstance()方法有三个参数:

1. 类加载器(Class Loader)

2. 需要实现的接口数组

3. InvocationHandler接口。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。这是动态代理的关键所在。

InvocationHandler接口

接口里有一个invoke()方法。基本的做法是,创建一个类,实现这个方法,利用反射在invoke()方法里实现需求:

public class MyInvocationHandler implements InvocationHandler{

  public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//do something "dynamic"
}
}

invoke()方法同样有三个参数:

1. 动态代理类的引用,通常情况下不需要它。但可以使用getClass()方法,得到proxy的Class类从而取得实例的类信息,如方法列表,annotation等。

2. 方法对象的引用,代表被动态代理类调用的方法。从中可得到方法名,参数类型,返回类型等等

3. args对象数组,代表被调用方法的参数。注意基本类型(int,long)会被装箱成对象类型(Interger, Long)

动态代理例子

1. 模拟AOP

public interface IVehical {

    void run();

}

//concrete implementation
public class Car implements IVehical{ public void run() {
System.out.println("Car is running");
} } //proxy class
public class VehicalProxy { private IVehical vehical; public VehicalProxy(IVehical vehical) {
this.vehical = vehical;
} public IVehical create(){
final Class<?>[] interfaces = new Class[]{IVehical.class};
final VehicalInvacationHandler handler = new VehicalInvacationHandler(vehical); return (IVehical) Proxy.newProxyInstance(IVehical.class.getClassLoader(), interfaces, handler);
} public class VehicalInvacationHandler implements InvocationHandler{ private final IVehical vehical; public VehicalInvacationHandler(IVehical vehical) {
this.vehical = vehical;
} public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable { System.out.println("--before running...");
Object ret = method.invoke(vehical, args);
System.out.println("--after running..."); return ret;
} }
} public class Main {
public static void main(String[] args) { IVehical car = new Car();
VehicalProxy proxy = new VehicalProxy(car); IVehical proxyObj = proxy.create();
proxyObj.run();
}
}
/*
* output:
* --before running...
* Car is running
* --after running...
* */

可以看出,对IVehical接口的调用,会交由Proxy的invoke方法处理,并在不改变run()的源代码下,新增了动态的逻辑(before running/after running),这正式AOP所做的。

深入讲,Proxy.newInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
(1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy.
$Proxy0类实现了interfaces的接口,并继承了Proxy类.
(2)实例化$Proxy0并在构造方法中把BusinessHandler传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:
class Proxy{
   InvocationHandler h=null;
   protected Proxy(InvocationHandler h) {
    this.h = h;
   }
   ...
}

另外,如果将invoke()方法代码改成如下:

    public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable { System.out.println("--before running...");
// Object ret = method.invoke(vehical, args);
((IVehical)proxy).run();
System.out.println("--after running..."); return null;
}

结果会是因为run()方法会引发invoke(),而invoke()又执行run(),如此下去变成死循环,最后栈溢出

所以invoke 接口中的proxy参数不能用于调用所实现接口的某些方法(见参考4)。

2. 利用动态代理实现设计模式,修饰器和适配器:

见参考5

3. 在项目中,可以使用动态代理,获取配置文件,非常方便且有优势:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value { /**
* The actual value expression: e.g. "#{systemProperties.myProp}".
*/
String value();
} /**
* config interfaces, map the config properties file:
* db.url =
* db.validation = true
* db.pool.size = 100
*/
public interface IConfig { @Value("db.url")
String dbUrl(); @Value("db.validation")
boolean isValidated(); @Value("db.pool.size")
int poolSize(); } //proxy class
public final class ConfigFactory { private ConfigFactory() {} public static IConfig create(final InputStream is) throws IOException{ final Properties properties = new Properties();
properties.load(is); return (IConfig) Proxy.newProxyInstance(IConfig.class.getClassLoader(),
new Class[] { IConfig.class }, new PropertyMapper(properties)); } public static final class PropertyMapper implements InvocationHandler { private final Properties properties; public PropertyMapper(Properties properties) {
this.properties = properties;
} public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable { final Value value = method.getAnnotation(Value.class); if (value == null) return null; String property = properties.getProperty(value.value());
if (property == null) return (null); final Class<?> returns = method.getReturnType();
if (returns.isPrimitive())
{
if (returns.equals(int.class)) return (Integer.valueOf(property));
else if (returns.equals(long.class)) return (Long.valueOf(property));
else if (returns.equals(double.class)) return (Double.valueOf(property));
else if (returns.equals(float.class)) return (Float.valueOf(property));
else if (returns.equals(boolean.class)) return (Boolean.valueOf(property));
} return property;
} }
} public static void main(String[] args) throws FileNotFoundException, IOException { IConfig config = ConfigFactory.create(new FileInputStream("config/config.properties"));
String dbUrl = config.dbUrl();
boolean isLoginValidated = config.isValidated();
int dbPoolSize = config.poolSize(); }

利用动态代理载入配置文件,并将每一个配置映射成方法,方便我们使用追踪。

最后,有个小挑战就是,将动态代理类$Proxy还原出来,暂时还没做。请看参考3

参考:

http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html

http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html

http://hi.baidu.com/malecu/item/9e0edc115cb597a1feded5a0

http://www.cnblogs.com/duanxz/archive/2012/12/03/2799504.html

http://www.ibm.com/developerworks/cn/java/j-jtp08305.html

 
 

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

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

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

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

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

  3. Java特性-动态代理

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

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

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

  5. java的动态代理机制

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

  6. java中动态代理

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

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

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

  8. (转)java的动态代理机制详解

    原文出自:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一 ...

  9. [转载] java的动态代理机制详解

    转载自http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代 ...

  10. 【译】11. Java反射——动态代理

    原文地址:http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html 博主最近比较忙,争取每周翻译四篇.等不急的请移步原文网页. ...

随机推荐

  1. Leetcode - 剑指offer 面试题29:数组中出现次数超过一半的数字及其变形(腾讯2015秋招 编程题4)

    剑指offer 面试题29:数组中出现次数超过一半的数字 提交网址: http://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163 ...

  2. SpringBoot入门教程(十)应用监控Actuator

    Actuator可能大家非常熟悉,它是springboot提供对应用自身监控,以及对应用系统配置查看等功能.spring-boot-starter-actuator模块的实现对于实施微服务的中小团队来 ...

  3. 解构领域驱动设计(一):为什么DDD能够解决软件复杂性

    1 为什么我要研究领域驱动设计 1.1 设计方法各样且代码无法反映设计 我大概从2017年10月份开始研究DDD,当时在一家物流信息化的公司任职架构师,研究DDD的初衷在于为团队寻找一种软件设计的方法 ...

  4. 前端笔记之JavaScript(十一)event&BOM&鼠标/盒子位置&拖拽/滚轮

    一.事件对象event 1.1 preventdefault()和returnValue阻止默认事件 通知浏览器不要执行与事件关联的默认动作. preventdefault()  支持Chrome等高 ...

  5. 使用mpvue开发小程序教程(四)

    在上一章节中,我们将vue-cli命令行工具生成的代码骨架中的src目录清理了一遍,然后从头开始配置和编写了一个可以运行的小程序页面,算是正真走上了使用mpvue开发小程序的第一步.今天我们将进一步来 ...

  6. 【Java基础】【08面向对象_继承&方法&final】

    08.01_面向对象(代码块的概述和分类)(了解)(面试的时候会问,开发不用或者很少用) A:代码块概述 在Java中,使用{}括起来的代码被称为代码块. B:代码块分类 根据其位置和声明的不同,可以 ...

  7. 写在最前面 - 《看懂每一行代码 - kubernetes》

    我要写什么 <看懂每一行代码 - kubernetes>会包含k8s整个项目的源码解析,考虑到门槛问题,在开始分析k8s之前我会通过一些更低难度的golang开源项目讲解来帮助大家提升go ...

  8. 使用MaxCompute Java SDK 执行任务卡住了,怎么办?

    场景一 用户AA: “亲,用 MaxCompute Java SDK 跑作业,为什么卡住不动了?”me: “有 Logview 吗?发来看下”A: “没有,我用的是SDK,没Logview” 场景二 ...

  9. 日志收集ELK+kafka相关博客

    SpringBoot+kafka+ELK分布式日志收集 使用 logstash + kafka + elasticsearch 实现日志监控 Kibana 安装 与 汉化 windows系统安装运行f ...

  10. 如何将JAR包发布到Maven中央仓库?

    将jar包发布到Maven中央仓库(Maven Central Repository),这样所有的Java开发者都可以使用Maven直接导入依赖,例如fundebug-java: <!-- ht ...