众所周知Java的动态代理由Proxy和InvocationHander实现。以下代码演示了简单地应用:
public interface IFoo {
public void load(int i);
public void save(Object o);
public List<Object> list();
}
public class Foo implements IFoo {
public void load(int i) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("load() is running");
}
public void save(Object o) {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("save() is running");
}
public List<Object> list() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("list() is running");
return null;
}
}
用动态代理实现计算运行时间和打印日志。
public class MyProxy {
public static void main(String[] args) {
time();
log();
}
static void time() {
final Foo foo = new Foo();
IFoo myFoo = (IFoo) Proxy.newProxyInstance(
MyProxy.class.getClassLoader(), foo.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
long start = new Date().getTime();
Object rtn = method.invoke(foo, args);
long end = new Date().getTime();
System.out.println(method.getName() + " method takes "
+ TimeUnit.MILLISECONDS.toSeconds(end - start)
+ " seconds");
return rtn;
}
});
System.out.println(myFoo.getClass());
myFoo.list();
myFoo.load(5);
myFoo.save("");
}
static void log() {
final Foo foo = new Foo();
IFoo myFoo = (IFoo) Proxy.newProxyInstance(
MyProxy.class.getClassLoader(), foo.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
Object rtn = method.invoke(foo, args);
System.out.println(method.getName() + "("
+ (args == null ? "" : args) + ") runs at "
+ new Date());
return rtn;
}
});
System.out.println(myFoo.getClass());
myFoo.list();
myFoo.load(5);
myFoo.save("");
}
}
运行结果如下:
class com.sun.proxy.$Proxy0
list() is running
list method takes 3 seconds
load() is running
load method takes 1 seconds
save() is running
save method takes 2 seconds
class com.sun.proxy.$Proxy0
list() is running
list() runs at Fri Mar 28 17:52:13 CST 2014
load() is running
load([Ljava.lang.Object;@961dff) runs at Fri Mar 28 17:52:14 CST 2014
save() is running
save([Ljava.lang.Object;@18b81e3) runs at Fri Mar 28 17:52:16 CST 2014
可以看到动态生成了类型$Proxy0的对象即完成计时和日志功能的代理对象。而这里最核心的部分毫无疑问是newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h),这个方法返回一个实例,该实例继承了Proxy类并实现了IFoo接口,并接受一个InvocationHandler作为成员变量,而每一个方法的具体实现实际上调用的都是实现InvocationHandler接口的匿名内部类对象的invoke(Object proxy, Method method,Object[] args) 方法,在该方法内部通过反射延续了被代理对象的行为。
接下来,我们通过查看一些源码片段来做更深入地了解:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
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() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
这里的核心是Class<?> cl = getProxyClass0(loader, interfaces),而后面的部分是通过反射实例化一个代理对象。
选取部分代码来解读生成代理类Class的大概步骤,首先是定义class name。它由三部分组成proxyPkg + proxyClassNamePrefix + num。package name,如果没有非公共接口即采用com.sun.proxy作为包名加上$Proxy和自增长的数字,这也就是上文运行结果中看到的com.sun.proxy.$Proxy0。而接下来才是更加核心的部分生成符合JVM规范的Class字节码。
try {
String proxyPkg = null; // package to define proxy class in
for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
{
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
proxyClasses.put(proxyClass, null);
}
通过以下代码片段可以看出它按照JVM规范输出字节码,魔数、常量池、访问标志、类索引等等
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 access_flags;
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
而下面这段代码也部分印证了代理对象的方法实际上是对InvocationHandler的invoke方法的调用。
out.writeShort(cp.getInterfaceMethodRef(
"java/lang/reflect/InvocationHandler",
"invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
"[Ljava/lang/Object;)Ljava/lang/Object;"));
但从这些字节码我们似乎还很难一睹这个代理类的全貌,为了一睹为快,我尝试借助Instrumentaion来获取内存中的这个Class对象(当然我们可以通过修改JDK源码)。
public class MyTransformer implements ClassFileTransformer {
public static void premain(String options, Instrumentation ins) {
ins.addTransformer(new MyTransformer());
}
public byte[] transform(ClassLoader loader, String className, Class cBR,
java.security.ProtectionDomain pD, byte[] classfileBuffer)
throws IllegalClassFormatException {
if (className.contains("com/sun/proxy/$Proxy0")) {
try {
FileChannel fc = new FileOutputStream("D:\\$Proxy0.class")
.getChannel();
fc.write(ByteBuffer.wrap(classfileBuffer));
fc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return classfileBuffer;
}
}
然后打包成Jar,设置Premain-Class,指定javaagent运行,得到class文件,然后反编译,end of story
package com.sun.proxy;
import IFoo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.List;
public final class $Proxy0
extends Proxy
implements IFoo
{
private static Method m1;
private static Method m5;
private static Method m0;
private static Method m3;
private static Method m4;
private static Method m2;
public $Proxy0(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 save(Object paramObject)
throws
{
try
{
this.h.invoke(this, m5, new Object[] { paramObject });
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 void load(int paramInt)
throws
{
try
{
this.h.invoke(this, m3, new Object[] { Integer.valueOf(paramInt) });
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final List list()
throws
{
try
{
return (List)this.h.invoke(this, m4, null);
}
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("IFoo").getMethod("save", new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("IFoo").getMethod("load", new Class[] { Integer.TYPE });
m4 = Class.forName("IFoo").getMethod("list", 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());
}
}
}
转载于:https://blog.51cto.com/thatmonkey/1387491