Java动态代理原理
2015-12-10 23:29
274 查看
JAVA动态代理原理
概述
同为代理,动态代理和静态代理本质上其实没什么区别,代理对象都有被代理对象的引用,并通过调用被代理对象的业务逻辑(方法),实现代理操作。对于一般程序员来说,动态代理好像要比静态代理难理解且实现起来更麻烦一些。本文档解释了动态代理的本质,通过文档的学习,你会发现动态代理也是非常简单易用的。
动态代理结构
为了方面描述,这里先假设一个动态代理的场景,并简单实现它。这个场景的原型是一个具体的项目,项目中有很多的服务类,我们用动态代理的目的是为了在每一个服务对象的方法被调用的时候做一些预处理,其中一项就是打印日志。我们可以在每个服务类方法中添加这样的业务逻辑,但方法确实太多,一个个添加会有很多多余代码。而且预处理的逻辑可能会改变,那么我们就需要更改所有的服务类了,这是一个非常不容易完成的任务。所以我们使用了动态代理。
为了方便,这里已经简化了服务类。假设某个服务类有接口IServer:
package com.test.proxy.impl;
public interface IServer {
publicvoid start();
publicvoid process();
publicvoid stop();
}
具体的服务类:
package com.test.proxy.impl;
public class Serverimplements IServer{
@Override
publicvoid
start() {
System.out.println("start");
}
@Override
publicvoid process() {
System.out.println("process");
}
@Override
publicvoid stop() {
System.out.println("stop");
}
}
我们首先实现一个DefaultHandler:
package com.test.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DefaultHandlerimplements InvocationHandler{
private Objectobj;
public DefaultHandler(Objectobj){
this.obj =obj;
}
@Override
public Object invoke(Objectobject, Method
method, Object[] args)
throws Throwable {
/**
方法调用前预处理 **/
System.out.println("method:"+method.getName()+" start");
Object ret =method.invoke(obj,args);
/**
方法调用后处理 **/
System.out.println("method:"+method.getName()+" end");
returnret;
}
}
这个类实现了InvocationHandler接口,并实现了invoke方法,代理类的所有方法调用都会调用invoke方法。
最后我们通过一个带有main入口的类,来实现这个代理。
package com.test.proxy;
import java.lang.reflect.Proxy;
import com.test.proxy.impl.IServer;
import com.test.proxy.impl.Server;
public class BusinessMain {
publicstatic
void main(String[]args) {
//初始化一个被代理对象的实例
Server serv =new Server();
//初始化handler,并将被代理对象的引用传递给它
DefaultHandler defaultHandler =new DefaultHandler(serv);
//获得代理对象实例
IServer proxyServ = (IServer)Proxy.newProxyInstance(serv.getClass().getClassLoader(),serv.getClass().getInterfaces(),defaultHandler);
//通过代理对象调用方法
proxyServ.start();
proxyServ.stop();
}
}
运行main函数,就能得到以下输出
method:start start
start
method:start end
method:stop start
stop
method:stop end
好了一个完整的动态代理已经完成了,现在我们分析一下,这是怎么实现的。
1.和静态代理一样,我们首先构造了一个被代理对象的实例。
2.构造DefaultHandler实例,并把被代理对象实例的引用传递给了这个实例,这时候DefaultHanlder的实例就包含了被代理对象的引用了,这里和静态代理有点相似了,静态代理类里也包含了被代理对象的引用。
3.构造代理对象实例,静态代理时,我们会自己完成代理类的编写。动态代理中我们通过Proxy类的静态方法newProxyInstance就获得了代理对象,并没有编写代理类。实际上我们调用的这个静态方法,直接动态生成了代理类的字节码,并通过我们传递的类加载器加载了它,然后初始化生成了代理对象。
我们看一下静态方法的代码:
public static Object newProxyInstance(ClassLoaderloader,
Class<?>[] interfaces,
InvocationHandler
h)
throws IllegalArgumentException
{
if (h ==null) {
thrownew NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, interfaces);// stack walk magic: do not refactor
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandlerih =
h;
SecurityManager sm = System.getSecurityManager();
if (sm !=null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
returnnewInstance(cons, ih);
}
});
} else {
return newInstance(cons,ih);
}
} catch (NoSuchMethodExceptione) {
thrownew InternalError(e.toString());
}
}
这段代码首先通过我们提供的类加载器和接口,动态生成了代理类字节码,并加载获得了Class对象。
Class<?> cl = getProxyClass0(loader, interfaces);
通过Class对象得到构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
最后用我们提供的Handler实例作为参数,构造了一个代理类对象。代理类对象中包含了对handler实例的引用。
现在我们具体就来谈谈代理类对象。
首先代理类对象是实现了IServer接口的,那么它就包含所有的IServer声明的方法,那么它到底是如何调用Handler中Invoke方法的呢。其实我们看看生成的代理类,就一目了然了。
import com.test.proxy.impl.IServer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ProxyCodes extends Proxy
implements IServer
{
private static Method m1;
private static Method m5;
private static Method m4;
private static Method m3;
private static Method m0;
private static Method m2;
public ProxyCodes(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void process()
throws
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void stop()
throws
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void start()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m5 = Class.forName("com.test.proxy.impl.IServer").getMethod("process", new Class[0]);
m4 = Class.forName("com.test.proxy.impl.IServer").getMethod("stop", new Class[0]);
m3 = Class.forName("com.test.proxy.impl.IServer").getMethod("start", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
代理类声明并获得了所有接口和Object类的所有方法对象的引用。代理类在调用方法时,调用Handler的invoke方法并传递参数,参看invoke方法的实现,我们就能知道整个动态代理的逻辑了。
补充说明
封装代理获得方法
关于动态代理的实现我们一般会把初始化Handler和获得代理类实例的代码封装起来,以便在使用代理的时候能和直接new一个对象一样方便。下面是一个简单的例子,只是提供一种思路。
我们实现一个DynamicProxy类来封装代码。
package com.test.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class DynamicProxy {
/**
* get
proxy with object and handler
* @param object
* @param handler
* @return
*/
publicstatic Object getProxy(Object
object,InvocationHandler handler){
//TODO
returnnull;
}
/**
* get
proxy with object
* @param object
* @return Object
*/
publicstatic Object getProxy(Object
object){
DefaultHandler defaultHandler =new DefaultHandler(object);
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),defaultHandler);
}
/**
* get
proxy with class and handler
* @param clazz
* @param handler
* @return
*/
publicstatic Object getProxy(Class<?>
clazz,InvocationHandler handler){
//TODO
returnnull;
}
/**
* get
proxy with class
* @param clazz
* @return Object
*/
publicstatic Object getProxy(Class<?>
clazz){
//TODO
returnnull;
}
}
在实际调用中,就可以这样完成:
package com.test.proxy;
import com.test.proxy.impl.IServer;
import com.test.proxy.impl.Server;
public class ProxyMain {
publicstatic
void main(String[]args) {
Server serv =new Server();
IServer proxyServ = (IServer)DynamicProxy.getProxy(serv);
//method invoke
proxyServ.start();
proxyServ.stop();
}
}
比起第一个显然要简单了一些,我们还可以进一步封装,使得使用动态代理变得更灵活和方便。
动态代理类字节码生成
JDK1.6中看Proxy方法中的源码,可以看到字节码生成代码是通过ProxyGenerator类的方法实现的。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces);
本文档字节码生成代码如下:
package com.test.proxy;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import sun.misc.ProxyGenerator;
import com.test.proxy.impl.Server;
public class GenrateBinaryCodes {
publicstatic
void main(Stringargs[])
throws IOException{
//Generate the code for ProxyClass
byte[]classCode = ProxyGenerator.generateProxyClass("ProxyCodes", Server.class.getInterfaces());
//Persist to file
FileOutputStream
fos = new FileOutputStream(new File("ProxyCodes.class"));
fos.write(classCode);
fos.flush();
fos.close();
}
}
概述
同为代理,动态代理和静态代理本质上其实没什么区别,代理对象都有被代理对象的引用,并通过调用被代理对象的业务逻辑(方法),实现代理操作。对于一般程序员来说,动态代理好像要比静态代理难理解且实现起来更麻烦一些。本文档解释了动态代理的本质,通过文档的学习,你会发现动态代理也是非常简单易用的。
动态代理结构
为了方面描述,这里先假设一个动态代理的场景,并简单实现它。这个场景的原型是一个具体的项目,项目中有很多的服务类,我们用动态代理的目的是为了在每一个服务对象的方法被调用的时候做一些预处理,其中一项就是打印日志。我们可以在每个服务类方法中添加这样的业务逻辑,但方法确实太多,一个个添加会有很多多余代码。而且预处理的逻辑可能会改变,那么我们就需要更改所有的服务类了,这是一个非常不容易完成的任务。所以我们使用了动态代理。
为了方便,这里已经简化了服务类。假设某个服务类有接口IServer:
package com.test.proxy.impl;
public interface IServer {
publicvoid start();
publicvoid process();
publicvoid stop();
}
具体的服务类:
package com.test.proxy.impl;
public class Serverimplements IServer{
@Override
publicvoid
start() {
System.out.println("start");
}
@Override
publicvoid process() {
System.out.println("process");
}
@Override
publicvoid stop() {
System.out.println("stop");
}
}
我们首先实现一个DefaultHandler:
package com.test.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DefaultHandlerimplements InvocationHandler{
private Objectobj;
public DefaultHandler(Objectobj){
this.obj =obj;
}
@Override
public Object invoke(Objectobject, Method
method, Object[] args)
throws Throwable {
/**
方法调用前预处理 **/
System.out.println("method:"+method.getName()+" start");
Object ret =method.invoke(obj,args);
/**
方法调用后处理 **/
System.out.println("method:"+method.getName()+" end");
returnret;
}
}
这个类实现了InvocationHandler接口,并实现了invoke方法,代理类的所有方法调用都会调用invoke方法。
最后我们通过一个带有main入口的类,来实现这个代理。
package com.test.proxy;
import java.lang.reflect.Proxy;
import com.test.proxy.impl.IServer;
import com.test.proxy.impl.Server;
public class BusinessMain {
publicstatic
void main(String[]args) {
//初始化一个被代理对象的实例
Server serv =new Server();
//初始化handler,并将被代理对象的引用传递给它
DefaultHandler defaultHandler =new DefaultHandler(serv);
//获得代理对象实例
IServer proxyServ = (IServer)Proxy.newProxyInstance(serv.getClass().getClassLoader(),serv.getClass().getInterfaces(),defaultHandler);
//通过代理对象调用方法
proxyServ.start();
proxyServ.stop();
}
}
运行main函数,就能得到以下输出
method:start start
start
method:start end
method:stop start
stop
method:stop end
好了一个完整的动态代理已经完成了,现在我们分析一下,这是怎么实现的。
1.和静态代理一样,我们首先构造了一个被代理对象的实例。
2.构造DefaultHandler实例,并把被代理对象实例的引用传递给了这个实例,这时候DefaultHanlder的实例就包含了被代理对象的引用了,这里和静态代理有点相似了,静态代理类里也包含了被代理对象的引用。
3.构造代理对象实例,静态代理时,我们会自己完成代理类的编写。动态代理中我们通过Proxy类的静态方法newProxyInstance就获得了代理对象,并没有编写代理类。实际上我们调用的这个静态方法,直接动态生成了代理类的字节码,并通过我们传递的类加载器加载了它,然后初始化生成了代理对象。
我们看一下静态方法的代码:
public static Object newProxyInstance(ClassLoaderloader,
Class<?>[] interfaces,
InvocationHandler
h)
throws IllegalArgumentException
{
if (h ==null) {
thrownew NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, interfaces);// stack walk magic: do not refactor
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandlerih =
h;
SecurityManager sm = System.getSecurityManager();
if (sm !=null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
returnnewInstance(cons, ih);
}
});
} else {
return newInstance(cons,ih);
}
} catch (NoSuchMethodExceptione) {
thrownew InternalError(e.toString());
}
}
这段代码首先通过我们提供的类加载器和接口,动态生成了代理类字节码,并加载获得了Class对象。
Class<?> cl = getProxyClass0(loader, interfaces);
通过Class对象得到构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
最后用我们提供的Handler实例作为参数,构造了一个代理类对象。代理类对象中包含了对handler实例的引用。
现在我们具体就来谈谈代理类对象。
首先代理类对象是实现了IServer接口的,那么它就包含所有的IServer声明的方法,那么它到底是如何调用Handler中Invoke方法的呢。其实我们看看生成的代理类,就一目了然了。
import com.test.proxy.impl.IServer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ProxyCodes extends Proxy
implements IServer
{
private static Method m1;
private static Method m5;
private static Method m4;
private static Method m3;
private static Method m0;
private static Method m2;
public ProxyCodes(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void process()
throws
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void stop()
throws
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void start()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m5 = Class.forName("com.test.proxy.impl.IServer").getMethod("process", new Class[0]);
m4 = Class.forName("com.test.proxy.impl.IServer").getMethod("stop", new Class[0]);
m3 = Class.forName("com.test.proxy.impl.IServer").getMethod("start", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
代理类声明并获得了所有接口和Object类的所有方法对象的引用。代理类在调用方法时,调用Handler的invoke方法并传递参数,参看invoke方法的实现,我们就能知道整个动态代理的逻辑了。
补充说明
封装代理获得方法
关于动态代理的实现我们一般会把初始化Handler和获得代理类实例的代码封装起来,以便在使用代理的时候能和直接new一个对象一样方便。下面是一个简单的例子,只是提供一种思路。
我们实现一个DynamicProxy类来封装代码。
package com.test.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class DynamicProxy {
/**
* get
proxy with object and handler
* @param object
* @param handler
* @return
*/
publicstatic Object getProxy(Object
object,InvocationHandler handler){
//TODO
returnnull;
}
/**
* get
proxy with object
* @param object
* @return Object
*/
publicstatic Object getProxy(Object
object){
DefaultHandler defaultHandler =new DefaultHandler(object);
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),defaultHandler);
}
/**
* get
proxy with class and handler
* @param clazz
* @param handler
* @return
*/
publicstatic Object getProxy(Class<?>
clazz,InvocationHandler handler){
//TODO
returnnull;
}
/**
* get
proxy with class
* @param clazz
* @return Object
*/
publicstatic Object getProxy(Class<?>
clazz){
//TODO
returnnull;
}
}
在实际调用中,就可以这样完成:
package com.test.proxy;
import com.test.proxy.impl.IServer;
import com.test.proxy.impl.Server;
public class ProxyMain {
publicstatic
void main(String[]args) {
Server serv =new Server();
IServer proxyServ = (IServer)DynamicProxy.getProxy(serv);
//method invoke
proxyServ.start();
proxyServ.stop();
}
}
比起第一个显然要简单了一些,我们还可以进一步封装,使得使用动态代理变得更灵活和方便。
动态代理类字节码生成
JDK1.6中看Proxy方法中的源码,可以看到字节码生成代码是通过ProxyGenerator类的方法实现的。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces);
本文档字节码生成代码如下:
package com.test.proxy;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import sun.misc.ProxyGenerator;
import com.test.proxy.impl.Server;
public class GenrateBinaryCodes {
publicstatic
void main(Stringargs[])
throws IOException{
//Generate the code for ProxyClass
byte[]classCode = ProxyGenerator.generateProxyClass("ProxyCodes", Server.class.getInterfaces());
//Persist to file
FileOutputStream
fos = new FileOutputStream(new File("ProxyCodes.class"));
fos.write(classCode);
fos.flush();
fos.close();
}
}
相关文章推荐
- 三个例子讲清楚Java反射
- Java线程2-4 单任务线程池SingleThreadPool
- java并发--队列同步器原理一
- 第三个spring冲刺第4天
- java多线程模拟聊天问题
- JAVA面向对象
- Java线程2-3 时间调度的线程池ScheduledThreadPool
- Struts2框架学习之三:result返回结果
- Spring配置文件所有类型的数据源dataSource
- java web 答辩总结
- java.lang.IndexOutOfBoundsException 错误解决
- Java线程2-2 固定大小的线程池FixedThreadPool
- Java线程2-1 线程池知识学习
- java中的原子类
- java运算符
- struts常见面试题目
- spring常见面试题目
- eclipse jetty插件的安装
- Java并发编程:volatile关键字解析
- Java回炉之排序算法