- 前置技能: Java、代理模式
newProxyInstance
在上一文中(没看的欢迎回去复习,要考的~),我们通过JDK实现了动态代理。为什么实现了InvocationHandler
接口就可以进行代理? 我们这一次尝试从 Main
开始去分析一下源码。可以看到Main
调用了
Proxy.newProxyInstance(RobTicket.class.getClassLoader(),
new Class[]{RobTicket.class},
new RobTicketInvocationHandler(new _12306()))
去构造一个_12306
的类,点进去看一下 newProxyInstance
到底做了什么
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// InvocationHandler不可为空,这也就是为什么我们一开始要实现InvocationHandler接口
Objects.requireNonNull(h);
// 浅拷贝
final Class<?>[] intfs = interfaces.clone();
/*当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器,也就是这里的 SecurityManager。*/
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.
*/
try {
if (sm != null) {
//检查创建代理类所需的权限。
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取构造方法对象
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//当修饰符不为public时,将其修改为可执行
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);
}
}
Ok, 上面我们可以看到生成一个代理类的大概步骤如下:
- 检查(非空检查、权限检查)
- 生成代理类(主要是通过
getProxyClass0
) - 获取构造器,若修饰符不为public,则修改其可执行权限(
setAccessible(true)
) - 返回通过构造器所创建的代理类对象
关于检查这一块,我们不做过多讲解,doc里有很清晰的描述(大致就是检查一下包权限和类加载器)。我们从第二步开始进行分析。
getProxyClass0
从 newProxyInstance
查看getProxyClass0
, 可以看到第一次是同一个类中进行跳转,然后调用了proxyClassCache.get(loader, interfaces)
这个方法。
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
// 这里是从缓存中拿到的代理类。如果不存在则会先创建
return proxyClassCache.get(loader, interfaces);
}
我们接着跟下去,发现代码如下:
WeakCache
final class WeakCache<K, P, V> {
private final ReferenceQueue<K> refQueue
= new ReferenceQueue<>();
// the key type is Object for supporting null key
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
= new ConcurrentHashMap<>();
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
= new ConcurrentHashMap<>();
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
//key --> ClassLoader loader | parameter --> Class<?>... interfaces
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
//虽然这个方法没有给注释,但是我们跟进refQueue.poll()发现这个方法的作用是查看是否有可引用对象。然后再结合这个方法看,就能知道这个方法的目的:删除refQueue中所有已被回收的引用对象~
expungeStaleEntries();
//将 ClassLoader 弄成cacheKey的形式(这是一个弱引用). 根据下面的注释可以倒推这里的cacheKey作为一级缓存使用
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
// 这里大意是组装一个二级缓存,。用到了线程安全的ConcurrentMap
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
// 当loader==null的时候就会出现valuesMap==null的情况
if (valuesMap == null) {
//尝试为values分配空间(有就返回,不存在就创建并put)
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
//如果不等于null的话说明还有救,就把valuesMap换成新地址
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
// 在这里生成key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
// 轮询, 直到能从supplier取到value为止.理论上一次就够了,但是WeakCache没加锁,valueFactory是可能被其他线程修改的?所以这里采用了轮询,见缝插针咯~
while (true) {
if (supplier != null) {
//在这里取得所需要的代理类,详见Factory中的get方法
V value = supplier.get();
if (value != null) {
return value;
}
}
// 下面对以下3种情况进行了处理.通过下面的处理后,某些情况下将会将supplier具体化为Factory(Factory 是 Supplier 的实现)
// 1. 在缓存中没有该supplier --> new 一个 Factory 作为 supplier 的值
// 2. 缓存中已有supplier --> 尝试替换valuesMap中的supplier为现在的factory
// 懒加载
if (factory == null) {
// Factory的构造和get方法推荐去看一下, 有一些比较有意思的东西,总共50行左右。这里就不作论述了
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
//supplier == null且valueMap成功put
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
private void expungeStaleEntries() {
CacheKey<K> cacheKey;
while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
cacheKey.expungeFrom(map, reverseMap);
}
}
}
这一部分是对于缓存的一些处理, 其中关于检查方面的代码依旧用了较多篇幅,逻辑部分不是很多。大致就是从缓存中获取代理,关于subKeyFactory.apply(key, parameter)
这里
//Proxy
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
//WeakCache
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
我们通过Proxy
类下的默认构造和WeakCache
这里的赋值能发现这里的subKeyFactory
是一个KeyFactory实例,而valueFactory
则是一个ProxyClassFactory实例(下面要考的,记住咯)
其中比较重要的是supplier.get()
这个地方,如果这里是Factory实例的话,get()方法会调用ProxyClassFactory
去生成一个代理类。这里先看一下Factory中的方法
@Override
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);
// put into reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
// try replacing us with CacheValue (this should always succeed)
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
上面是自带的注释,因为比较详尽就不多做解释了。这里我们需要重点关注valueFactory.apply(key, parameter)
这一行。create new value,这不就是咱要的东西嘛!跳转到调用的地方。有点长,不要怂,胜利就在眼前了!
ProxyClassFactory
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
// 前缀,看来带这个的都是走这里造出来的
private static final String proxyClassNamePrefix = "$Proxy";
// 自增的Long,还是线程安全的
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//这个循环的目的 --> 验证从传进来的类加载器加载出来的接口是不是和给的这堆接口是一样的玩意(可以去看看双亲委派哟)
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* 看看这万一是不是真的是接口,免得对牛谈琴
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
//亲, 您不是接口嘞, 别想着偷渡了
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
* 避免重复实现某一接口(表示看到这里之前不知道能重复实现一个接口,写完这篇文章我去试试)
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
* 还是校验,工业代码真的是...emmmm保险箱?
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.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) {
// 如果没有非public(双重否定 --> 如果都是public就用com.sun.proxy这个包)
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
* 不必BB的线程安全..
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 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());
}
}
}
ProxyGenerator
希望就在前方~ 我们再点开ProxyGenerator.generateProxyClass
这个方法,看一下具体实现:
public static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
//boss哟~
final byte[] classFile = gen.generateClassFile();
//这一堆是持久化生成出来的代理类,不用管
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;
}
又调用了一层… 再跟进去看看gen.generateClassFile();
这个方法。前方高能预警:
private byte[] generateClassFile() {
/* ============================================================
* Step 1: Assemble ProxyMethod objects for all methods to
* generate proxy dispatching code for.
* 为所有方法组装ProxyMethod对象,以生成代理调度代码。
*/
/*
* Record that proxy methods are needed for the hashCode, equals,
* and toString methods of java.lang.Object. This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
* 这三个方法不用说了吧...hashCode,equals,toString
*/
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
/*
* Now record all of the methods from the proxy interfaces, giving
* earlier interfaces precedence over later ones with duplicate
* methods.
* 添加所有接口要求实现的方法
*/
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
/*
* For each set of proxy methods with the same signature,
* verify that the methods' return types are compatible.
* 验证返回值
*/
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* ============================================================
* Step 2: Assemble FieldInfo and MethodInfo structs for all of
* fields and methods in the class we are generating.
* 为这些方法生成字段和方法信息
*/
try {
//构造方法
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// generate code for proxy method and add it
methods.add(pm.generateMethod());
}
}
//生成静态代码块
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
/* ============================================================
* Step 3: Write the final class file.
* 最终文件
*/
/*
* Make sure that constant pool indexes are reserved for the
* following items before starting to write the final class file.
* 确保在开始写最终之前constant pool 的索引会被保留
*/
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (Class<?> intf: interfaces) {
cp.getClass(dotToSlash(intf.getName()));
}
/*
* Disallow new constant pool additions beyond this point, since
* we are about to write the final constant pool table.
* 设置只读 --> 下面要开始往常量池里写东西了
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
/*
* Write all the items of the "ClassFile" structure.
* See JVMS section 4.1.
*/
// u4 magic;
// 熟悉的CAFEBABE,看过JVM虚拟机的应该都知道。
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(accessFlags);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (Class<?> intf : interfaces) {
dout.writeShort(cp.getClass(
dotToSlash(intf.getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
return bout.toByteArray();
}
在这里如果我们配置了System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
那么会在com.sun.proxy
下生成我们代理类的字节码文件,还是使用上一文中的JDK动态代理的代码,最终生成结果如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import cn.bestsort.code.proxy.RobTicket;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//可以看到实现了我们需要代理的类的接口
public final class $Proxy0 extends Proxy implements RobTicket {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(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 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 void startRob() throws {
try {
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("cn.bestsort.code.proxy.RobTicket").getMethod("startRob");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
这里的supper.h
其实是Proxy
类里的成员变量InvocationHandler
,这玩意就是我们上一文中实现的那个接口。忘了?没关系,来复习一下:
public class RobTicketInvocationHandler implements InvocationHandler {
private Object object;
public RobTicketInvocationHandler(Object o){ object = o; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(object, args);
end();
return result;
}
private void before(){
System.out.println("导入身份信息");
}
private void end(){
System.out.println("抢票成功, 开始付款");
}
}
因为在构造啊的时候我们将这个自己实现的RobTicketInvocationHandler
作为h传进去了,所以调用的invoke其实是我们自己写的这个invoke方法,也就是说,到此为止,JDK动态代理的过程就结束了~
RobTicket robTicket = (RobTicket) Proxy.newProxyInstance(
RobTicket.class.getClassLoader(),
new Class[]{RobTicket.class},
new RobTicketInvocationHandler(new _12306())
);
完结撒花~~~
后记
第一次这么完整地看完一部分源码,好激动。。。
最后,欢迎关注我的公众号丫 --> 直接在公众号搜索页面搜 bestsort, bestsort的秘密基地就是我啦~
非常感谢街灯下的小草他的关于JDK动态代理的那篇文章给我阅读源码的大致脉络提供了非常大的帮助(可惜他那篇文章有一部分有错误,差点让我怀疑人生…)