【总结】:JDK动态代理的原理是通过目标对象提供的classloader、interfaces的Class对象数组、InvocatiionHandler提供的代理逻辑
来重新定义并在内存生成一个代理类的class文件并且通过构造器对象实例化并返回。使得用户使用代理类对象调用业务接口方法的时候,调用的是invocationHandler
的invoke
方法,最终完成代理。
- 都很熟悉的以下这段代码,JDK的动态代理,使用
Proxy.newProxyInstance()
获得代理对象,并且提供InvocationHandler
来设置前置和后置处理的相关代码。今天看看它的源码
public interface ISinger {
void sing();
}
public class Singer implements ISinger {
@Override
public void sing() {
System.out.println("唱一会儿歌");
}
}
public class Test {
public static void main(String[] args) {
Singer target = new Singer();
Object proxyInstance = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args1) -> {
System.out.println("代理前:向观众问好");
Object returnValue = method.invoke(target, args1);
System.out.println("代理后:向观众问好");
return returnValue;
});
ISinger singerProxy = (ISinger) proxyInstance;
singerProxy.sing();
}
}
newProxyInstance
主要做了三件事情,
- 生产制定的代理类
- 获取构造器对象
- 使用构造器对象实例化代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//判空
Objects.requireNonNull(h);
//克隆该类实现的所有接口
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 查找或生成特定的代理类
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
* 使用制定的 invocation handler 调用构造器
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
/**
* 常量 constructorParams:
* private static final Class<?>[] constructorParams = { InvocationHandler.class };
**/
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 通过构造器对象来实例化代理对象并返回
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
getProxyClass0()
具体负责生成代理类
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//如果缓存中有代理类则使用,如果没有则使用ProxyClassFactory生成
return proxyClassCache.get(loader, interfaces);
}
ProxyClassFactory.apply()
完成了代理对象字节码的生成
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//此处省略了一大堆代码……(主要是接口、包和其他一些类文件相关的校验)
/*
* Generate the specified proxy class.
* 关键在这里,生成了代理对象的字节码
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
由于jdk8的源码到上面的部分就到了native方法了,因此以下内容参考了其他博主的内容[1]进行学习
public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) {
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
// 真正生成字节码的方法
final byte[] classFile = gen.generateClassFile();
// 如果saveGeneratedFiles为true 则生成字节码文件,所以在开始我们要设置这个参数
// 当然,也可以通过返回的bytes自己输出
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i+1, name.length()) + ".class");
} else {
path = Paths.get(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError( "I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
阅读参考文献法发现生成的代理对象是继承了Proxy
类并且会实现用户定义的接口,这里是ISinger
接口。因此获得代理对象之后可以进行强转并且进行调用。
参考文献:
网友评论