JAVA动态代理
阅读前请先熟悉下面几点内容,可以帮助理解
一、JDK动态代理和CGLIB字节码生成的区别?
* JDK动态代理只能对实现了接口的类生成代理,而不能针对类* CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
二、静态代理、动态代理区别?
静态代理:由程序员创建或工具生成代理类的源码,再编译代理类,即代理类和委托类的关系再程序运行前就已经存在。
动态代理:在运行期间使用动态生成字节码形式,动态创建代理类。使用的工具有 jdkproxy、cglibproxy 等。
三、设计模式中的代理模式。
------------分界线----------------
业务接口:
package jianlejun;
public interface IShopping {
public void buyMac();
public int buyMedicine(int num);
}
业务实现类:
package jianlejun;
public class Shopping implements IShopping {
@Override
public void buyMac() {
System.out.println("帮我去香港买mac电脑~~");
}
@Override
public int buyMedicine(int num) {
System.out.println("帮我去日本买"+num+"份药物~~");
return num;
}
}
业务代理调用类:
package jianlejun;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 业务代理操作类,必须实现InvocationHandler接口
* 最终真实方法的调用者都是该类完成
* 代理对象调用业务方法时,会执行业务代理操作类的invoke()方法
*
* @author 简乐君
*
*/
public class ProxyHandler implements InvocationHandler{
private Object targetClass;
public ProxyHandler(Object targetClass) {
this.targetClass = targetClass;
}
/**
* client中通过代理对象调用的业务方法都会作为method参数传递进来
* 代理对象调用业务方法时,只是执行了业务代理操作类的invoke()方法
* 必须显示调用method.invoke()方法,代理调用的业务方法才会被有效执行,
* proxy:代理对象
* method:代理对象执行的方法
* args:业务方法中的传递的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class clazz = proxy.getClass();
System.out.println("proxy所对应的类名为====>"+clazz.getName());
String methodName = method.getName();
System.out.println("代理调用的业务方法名为====>"+methodName);
/**
* targetClass:方法所在的目标类;
* args:业务方法中传递的参数;
* 返回结果为业务方法的返回值
*/
Object returnObj = method.invoke(targetClass, args);
return returnObj;
}
}
客户端调用:
package jianlejun;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
IShopping is = new Shopping();//创建业务对象
InvocationHandler ih = new ProxyHandler(is);//创建业务代理调用类
ClassLoader loader = is.getClass().getClassLoader();//获取业务对象的类加载器,用于动态创建代理类
Class []intf = is.getClass().getInterfaces();//获取业务对象所实现的接口列表,用于动态创建代理类
IShopping proxy = (IShopping) Proxy.newProxyInstance(loader, intf, ih);//利用反射机制,在运行期间动态生成代理类
proxy.buyMac();//通过代理对象调用业务方法,调用时执行的是InvocationHandler中的invoke()方法
int num = proxy.buyMedicine(100);
}
}
输出结果:
proxy所对应的类名为====>com.sun.proxy.$Proxy0
代理调用的业务方法名为====>buyMac
帮我去香港买mac电脑~~
proxy所对应的类名为====>com.sun.proxy.$Proxy0
代理调用的业务方法名为====>buyMedicine
帮我去日本买100份药物~~
好,那假如现在我们想看看jdk动态代理机制生成的代理类长啥样,该怎么办呢?很简单,我们只需要在客户端加入一行代码即可:
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//打开保存proxy动态生成文件的开关
在工作空间下找到该项目,此时生成的class文件目录是比较特殊的,它跟src在同一级目录下,如果运行时报错
FileNotFoundException:com/sun/proxy/$proxy0.class(系统找不到指定路径),此时解决的方法可以在项目里与src同级下建包名为com.sun.proxy的包,再运行即可,然后运用反编译软件(http://jd.benow.ca)反编译该class文件即可。
来,我们来一睹动态生成的代理类的风采:
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jianlejun.IShopping;
public final class $Proxy0
extends Proxy
implements IShopping
{
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
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 int buyMedicine(int paramInt)
{
try
{
return ((Integer)this.h.invoke(this, m4, new Object[] { Integer.valueOf(paramInt) })).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void buyMac()
{
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()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
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") });
m4 = Class.forName("jianlejun.IShopping").getMethod("buyMedicine", new Class[] { Integer.TYPE });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("jianlejun.IShopping").getMethod("buyMac", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
现在来理解为什么代理对象调用业务方法时执行的是InvocationHandler中的invoke方法就很好理解了
public final int buyMedicine(int paramInt)
{
try
{
return ((Integer)this.h.invoke(this, m4, new Object[] { Integer.valueOf(paramInt) })).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
在方法内,并没有像静态代理模式一样直接调用业务类业务方法,而是委托给了InvocationHandler的invoke方法,并且后续也并没有显示调用业务类的业务方法,所以要在InvocationHandler的invoke方法中显示调用method.invoke()才算是正确调用了业务方法。切记切记