什么是动态代理(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. 深入理解Spring AOP思想

    什么是AOP?AOP解决了什么问题? 在传统的开发模式中,以下层次的是非常常见的一种,业务层每一个方法都要有重复的事务代码 如何改善这个问题? AOP希望将A.B 这些分散在各个业务逻辑中的相同代码, ...

  2. RabbitMQ消息队列(九)-通过Headers模式分发消息(.Net Core版)

    Headers类型的exchange使用的比较少,以至于官方文档貌似都没提到,它是忽略routingKey的一种路由方式.是使用Headers来匹配的.Headers是一个键值对,可以定义成Hasht ...

  3. linux集群自动化搭建(生成密钥对+分发公钥+远程批量执行脚本)

    之前介绍过ansible的使用,通过ssh授权批量控制服务器集群 但是生成密钥和分发公钥的时候都是需要确认密码的,这一步也是可以自动化的,利用ssh + expect + scp就可以实现,其实只用这 ...

  4. Spring中使用的设计模式

    目录 Spring使用的设计模式 1.单例模式 2.原型模式 3.模板模式 4.观察者模式 5.工厂模式 简单工厂模式 工厂方法模式 6.适配器模式 7.装饰者模式 8.代理模式 9.策略模式   S ...

  5. [Css] css3的flex布局

    flex思维导图 {"name":"flex","children":[{"name":"传统布局方式&quo ...

  6. DSAPI HTTP监听服务端与客户端_指令版

    前面介绍了DSAPI多功能组件编程应用-HTTP监听服务端与客户端的内容,这里介绍一个适用于更高效更快速的基于HTTP监听的服务端.客户端. 在本篇,你将见到前所未有的超简化超傻瓜式的HTTP监听服务 ...

  7. [MySQL]select和where子句优化

    数据库优化:1.可以在单个SQL语句,整个应用程序,单个数据库服务器或多个联网数据库服务器的级别进行优化2.数据库性能取决于数据库级别的几个因素,例如表,查询和配置设置3.在数据库级别进行优化,在硬件 ...

  8. vue中如何使用mockjs摸拟接口的各种数据

    mockjs的作用 生成模拟数据 模拟 Ajax 请求,返回模拟数据 基于 HTML 模板生成模拟数据(后续更新) 帮助编写单元测试(后续更新) Vue 中使用 mock 有两种使用方式,一种是仅编写 ...

  9. arcgis api 3.x for js 入门开发系列五地图态势标绘(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...

  10. 作为开发人员,这四类Code Review方法你都知道吗?

    本文翻译自:https://dzone.com/articles/4-types-of-code-reviews-any-professional-developer 转载请注明出处:葡萄城官网,葡萄 ...