JDK动态代理
JDK动态代理是基于接口使用,通过Proxy.newProxyInstance方法使用。
传入参数:当前类加载器、目标接口、带目标类的InvocationHandle实现类
示例代码
package com.jdklib;
import org.springframework.stereotype.Component;
@Component
public class UserDaoImpl implements UserDao {
@Override
public void query() {
System.out.println("UserDaoImpl");
}
}
package com.jdklib;
import org.springframework.stereotype.Component;
@Component
public interface UserDao {
public void query();
}
package com.jdklib;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandle implements InvocationHandler {
Object target;
public MyInvocationHandle(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy");
return method.invoke( target,args );
}
}
package com.jdklib;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.lang.reflect.Proxy;
@Configuration
@ComponentScan("com.jdklib")
@EnableAspectJAutoProxy
public class AppConfig {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext( AppConfig.class );
for (int i = 1; i < 20; i++) {
UserDao userDao = (UserDao) Proxy.newProxyInstance( AppConfig.class.getClassLoader(), new Class[]{UserDao.class}, new MyInvocationHandle(new UserDaoImpl()) );
userDao.query();
}
userDao.query();
byte[] bytes= ProxyGenerator.generateProxyClass("$proxy",new Class[]{UserDao.class} );
FileOutputStream fileOutputStream = new FileOutputStream( "c://$proxy.class" );
fileOutputStream.write( bytes );
fileOutputStream.close();
}
}
生成的代理类
import com.jdklib1.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $proxy extends Proxy implements UserDao {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $proxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void query() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.jdklib1.UserDao").getMethod("query");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
degub分析
第一次调用链
1、Proxy#newProxyInstance
newProxyInstance.png
2、Constructor#newInstance
Constructor.png
3、sun.reflect.DelegatingConstructorAccessorImpl#newInstance
DelegatingConstructorAccessorImpl-newInstance.png
4、sun.reflect.NativeConstructorAccessorImpl#newInstance
NativeConstructorAccessorImpl-newInstance.png
5、native的newInstance0
private static native Object newInstance0(Constructor<?> var0, Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException;
第16次调用链
4、sun.reflect.NativeConstructorAccessorImpl#newInstance
NativeConstructorAccessorImplnewInstance16.png
ReflectionFactory.inflationThreshold 默认值是15
5、native的newInstance0
第17次调用链
3、sun.reflect.DelegatingConstructorAccessorImpl#newInstance
GeneratedConstructorAccessor17.png
4、执行GeneratedConstructorAccessor3代理类方法
第16次调用generate方法生成代理类GeneratesConstructorAccessor,第17次执行GeneratesConstructorAccessor代理类方法
asm.png
Inflation机制
反射类加载器会导致Perm溢出
Constructor
package sun.reflect;
import java.lang.reflect.InvocationTargetException;
public interface ConstructorAccessor {
Object newInstance(Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException;
}
ConstructorAccessor 有下面实现类
1、NativeConstructorAccessorImpl
JNI(java本地接口)
2、DelegatingConstructorAccessorImpl
注入ConstructorAccessorImpl属性(值是1或3),实现newInstance
3、GeneratedConstructorAccessorX
通过代理类
Method
package sun.reflect;
import java.lang.reflect.InvocationTargetException;
public interface MethodAccessor {
Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException;
}
MethodAccessor 有下面实现类
1、NativeMethodAccessorImpl
NativeMethodAccessorImpl是通过JNI(java本地接口)存取器获取反射类字节码信息
2、DelegatingMethodAccessorImpl
DelegatingMethodAccessorImpl注入MethodAccessorImpl属性(值是1或3),实现invoke调用
3、GeneratedMethodAccessorX
通过代理类加载反射类字节码
numInvocations字段
NativeConstructorAccessorImpl或NativeMethodAccessorImpl都有一个字段numInvocations。用来计算调用JNI次数
当大于15就会生成GeneratedConstructorAccessorX或GeneratedConstructorAccessorX的字节码类文件,该字节码通过DelegatingClassLoader类加载。
为什么设计这种机制:为了提高效率
generate方法
MethodAccessorGenerator类
class MethodAccessorGenerator extends AccessorGenerator {
public MethodAccessor generateMethod(Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6) {
return (MethodAccessor)this.generate(var1, var2, var3, var4, var5, var6, false, false, (Class)null);
}
public ConstructorAccessor generateConstructor(Class<?> var1, Class<?>[] var2, Class<?>[] var3, int var4) {
return (ConstructorAccessor)this.generate(var1, "<init>", var2, Void.TYPE, var3, var4, true, false, (Class)null);
}
public SerializationConstructorAccessorImpl generateSerializationConstructor(Class<?> var1, Class<?>[] var2, Class<?>[] var3, int var4, Class<?> var5) {
return (SerializationConstructorAccessorImpl)this.generate(var1, "<init>", var2, Void.TYPE, var3, var4, true, true, var5);
}
private MagicAccessorImpl generate(final Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6, boolean var7, boolean var8, Class<?> var9) {
ByteVector var10 = ByteVectorFactory.create();
this.asm = new ClassFileAssembler(var10);
this.declaringClass = var1;
this.parameterTypes = var3;
this.returnType = var4;
this.modifiers = var6;
this.isConstructor = var7;
this.forSerialization = var8;
this.asm.emitMagicAndVersion();
short var11 = 42;
boolean var12 = this.usesPrimitiveTypes();
if (var12) {
var11 = (short)(var11 + 72);
}
if (var8) {
var11 = (short)(var11 + 2);
}
var11 += (short)(2 * this.numNonPrimitiveParameterTypes());
this.asm.emitShort(add(var11, (short)1));
final String var13 = generateName(var7, var8);
this.asm.emitConstantPoolUTF8(var13);
this.asm.emitConstantPoolClass(this.asm.cpi());
this.thisClass = this.asm.cpi();
if (var7) {
if (var8) {
this.asm.emitConstantPoolUTF8("sun/reflect/SerializationConstructorAccessorImpl");
} else {
this.asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");
}
} else {
this.asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");
}
this.asm.emitConstantPoolClass(this.asm.cpi());
this.superClass = this.asm.cpi();
this.asm.emitConstantPoolUTF8(getClassName(var1, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
this.targetClass = this.asm.cpi();
short var14 = 0;
if (var8) {
this.asm.emitConstantPoolUTF8(getClassName(var9, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
var14 = this.asm.cpi();
}
this.asm.emitConstantPoolUTF8(var2);
this.asm.emitConstantPoolUTF8(this.buildInternalSignature());
this.asm.emitConstantPoolNameAndType(sub(this.asm.cpi(), (short)1), this.asm.cpi());
if (this.isInterface()) {
this.asm.emitConstantPoolInterfaceMethodref(this.targetClass, this.asm.cpi());
} else if (var8) {
this.asm.emitConstantPoolMethodref(var14, this.asm.cpi());
} else {
this.asm.emitConstantPoolMethodref(this.targetClass, this.asm.cpi());
}
this.targetMethodRef = this.asm.cpi();
if (var7) {
this.asm.emitConstantPoolUTF8("newInstance");
} else {
this.asm.emitConstantPoolUTF8("invoke");
}
this.invokeIdx = this.asm.cpi();
if (var7) {
this.asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");
} else {
this.asm.emitConstantPoolUTF8("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
}
this.invokeDescriptorIdx = this.asm.cpi();
this.nonPrimitiveParametersBaseIdx = add(this.asm.cpi(), (short)2);
for(int var15 = 0; var15 < var3.length; ++var15) {
Class var16 = var3[var15];
if (!isPrimitive(var16)) {
this.asm.emitConstantPoolUTF8(getClassName(var16, false));
this.asm.emitConstantPoolClass(this.asm.cpi());
}
}
this.emitCommonConstantPoolEntries();
if (var12) {
this.emitBoxingContantPoolEntries();
}
if (this.asm.cpi() != var11) {
throw new InternalError("Adjust this code (cpi = " + this.asm.cpi() + ", numCPEntries = " + var11 + ")");
} else {
this.asm.emitShort((short)1);
this.asm.emitShort(this.thisClass);
this.asm.emitShort(this.superClass);
this.asm.emitShort((short)0);
this.asm.emitShort((short)0);
this.asm.emitShort((short)2);
this.emitConstructor();
this.emitInvoke();
this.asm.emitShort((short)0);
var10.trim();
final byte[] var17 = var10.getData();
return (MagicAccessorImpl)AccessController.doPrivileged(new PrivilegedAction<MagicAccessorImpl>() {
public MagicAccessorImpl run() {
try {
return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();
} catch (IllegalAccessException | InstantiationException var2) {
throw new InternalError(var2);
}
}
});
}
}
private static synchronized String generateName(boolean var0, boolean var1) {
int var2;
if (var0) {
if (var1) {
var2 = ++serializationConstructorSymnum;
return "sun/reflect/GeneratedSerializationConstructorAccessor" + var2;
} else {
var2 = ++constructorSymnum;
return "sun/reflect/GeneratedConstructorAccessor" + var2;
}
} else {
var2 = ++methodSymnum;
return "sun/reflect/GeneratedMethodAccessor" + var2;
}
}
}
static Class<?> defineClass(String var0, byte[] var1, int var2, int var3, final ClassLoader var4) {
ClassLoader var5 = (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return new DelegatingClassLoader(var4);
}
});
return unsafe.defineClass(var0, var1, var2, var3, var5, (ProtectionDomain)null);
}
generate方法实现了
1、asm生成字节码数组
2、defineClass生成DelegatingClassLoader代理类加载器
3、generateName方法生成字节码文件GeneratedXXXAccessorX
DelegatingClassLoader.png
Inflation机制提高了反射的性能,但是对于重度使用有两个问题
1、初次加载性能较低
2、动态加载的字节码导致PermGen持续增长
总结
JDK是基于接口实现
Inflation机制
关键是生成代理类和调用inovke方法的实现方式
1、JNI
2、asm字节码