Java的动态代理(dynamic proxy)
什么是动态代理(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的动态代理(dynamic proxy)的更多相关文章
- java jdk动态代理(proxy)
1. 涉及主要jdk api java.lang.reflect.InvocationHandler: public interface InvocationHandler { /** * Proce ...
- 动态代理Dynamic Proxy
代理模式是常用的Java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类 预处理消息,过滤消息,把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存在关联关系,一 ...
- Java的动态代理
什么是动态代理(dynamic proxy) 动态代理(以下称代理),利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对 ...
- java 动态代理学习(Proxy,InvocationHandler)
前几天看到java的动态代理机制,不知道是啥玩意,然后看了看.死活不知道 invoke(Object proxy, Method m, Object[] args)种的proxy是个什么东西,放在这里 ...
- JAVA设计模式-动态代理(Proxy)示例及说明
在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...
- JAVA设计模式-动态代理(Proxy)源码分析
在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...
- java的动态代理机制详解
在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的 ...
- (转)java的动态代理机制详解
原文出自:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一 ...
- [转载] java的动态代理机制详解
转载自http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代 ...
随机推荐
- .NET下dropdownlist的基本操作
//List列中索引的赋值 teacher.DataValueField = ds.Tables[0].Columns["pidcord"].ColumnName; //List列 ...
- 【原创+译文】官方文档中声明的如何创建抽屉导航栏(Navigation Drawer)
如需转载请注明出处:http://www.cnblogs.com/ghylzwsb/p/5831759.html 创建一个抽屉导航栏 抽屉式导航栏是显示在屏幕的左边缘,它是应用程序的主导航选项面板.它 ...
- CoreAnimation-05-CABasicAnimation
概述 简介 CABasicAnimation是抽象类CAPropertyAnimation的子类,可以直接使用 CABasicAnimation又称基本动画,从fromValue到toValue按照指 ...
- javascript之工厂方式定义对象
每一个函数对象都有一个length属性,表示该函数期望接收的参数个数. <html> <head> <script type="text/javascript& ...
- 解决Android SDK Manager 更新、下载慢以及待安装包列表不显示
参考地址:http://www.cnblogs.com/tc310/archive/2012/12/21/2828450.html 问题描述: Android SDK Manager 无法下载更新,或 ...
- Homebrew OS X 不可或缺的套件管理器
Homebrew OS X 不可或缺的套件管理器,可以说Homebrew就是mac下的apt-get.yum. 1.安装homebrew brew的安装很简单,使用一条ruby命令即可,Mac系统上已 ...
- Java Concurrency In Practice - Chapter 1 Introduction
1.1. A (Very) Brief History of Concurrency motivating factors for multiple programs to execute simul ...
- cocos2d-x之Action特效
bool HelloWorld::init() { if ( !Layer::init() ) { return false; } Size visibleSize = Director::getIn ...
- poj 3237 Tree [LCA] (树链剖分)
poj 3237 tree inline : 1. inline 定义的类的内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开),没有了调用的开销,效率也很高. 2. 很明显,类 ...
- dipole antenna simulation by CST
CST偶极子天线仿真,半波振子天线 一.本文使用CST仿真频率为1GHz的偶极子天线,使用2013版本.仿真的步骤为 1.选择一个CST的天线工程模板 2.设置好默认的单位 3.设置背景的材料(空气腔 ...