Java中的动态代理

来源:互联网 发布:狼人杀 法官 软件 编辑:程序博客网 时间:2024/06/15 12:50

JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。下面我们看一个示例:
/**
定义一个Animal接口
**/
public interface Animal {
void eat();
}

/**
定义一个Animal接口的实现类Cat
**/
public class Cat implements Animal {
public void eat() {
System.out.println(“Cat eat”);
}
}

/**
定义一个InvocationHandler接口的实现类AnimalInvocationHandler
**/
public class AnimalInvocationHandler implements InvocationHandler {

private Object mProxy;public AnimalInvocationHandler(Object object){    mProxy = object;}/** * 通过Proxy.newProxyInstance方法生产动态代理类 * @return */public Object getProxy(){    return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), mProxy.getClass().getInterfaces(), this);}/** * 掉用被代理类的接口方法最终会掉用invoke方法 */@Overridepublic Object invoke(Object proxy, Method method, Object[] args)        throws Throwable {   System.out.println("----- before -----");   Object result = method.invoke(mProxy, args);   System.out.println("----- after -----");   return result;}

}

/**
测试类
**/
public class Main {

public static void main(String[] args) throws Exception{    Animal animal = new Cat();    AnimalInvocationHandler animalInvocationHandler = new AnimalInvocationHandler(animal);    Animal proxy = (Animal)animalInvocationHandler.getProxy();    proxy.eat();    createProxyClassFile();}/** * 将动态生产的代理类class文件保存下来 */ public static void createProxyClassFile()     {       String name = "ProxySubject";       byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Animal.class } );       try      {         FileOutputStream out = new FileOutputStream( name + ".class" );         out.write( data );         out.close();       }       catch( Exception e )       {         e.printStackTrace();       }     }   

}
运行main方法我们可以看到当掉用Animal proxy = (Animal)animalInvocationHandler.getProxy();
proxy.eat();
最终掉用的是AnimalInvocationHandler类中的invoke方法。
WHY?我们可以看下保存下来的动态生成类的class 代码:
public final class ProxySubject extends Proxy implements Animal
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;

public ProxySubject(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 void eat()
{
try
{
this.h.invoke(this, m3, null);
return;
}
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 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”) });
m3 = Class.forName(“com.zld.dynamicproxy.Animal”).getMethod(“eat”, new Class[0]);
m2 = Class.forName(“java.lang.Object”).getMethod(“toString”, 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());
}
}
}

可以看到该类继承自Proxy并实现了Animal接口,在看下该类的eat方法就明白了为什么会掉用AnimalInvocationHandler类的invoke方法。
PS(ProxyGenerator类的导入可以通过点击右边的Add》打开access rules设置》选择resolution为accessible》按照提示在下面的rule pattern输入相应的pattern;

pattern 可以输入* 代表所有的包均可导入 ,也可以输入相应包路径下面的类可以进入访问,例如sun/misc/*等等;)

原创粉丝点击