-
什么是动态代理
动态代理是相对于静态代理来说的,所谓静态代理就是一个类,通常称之为代理类,代理类内部持有一个真正的对象,并且这个代理类继承自这个真正的对象类或者实现它们共同的接口(一般是面向接口更合理),然后重写或实现所有方法,在其中再调用真正对象的对应方法。
静态代理的应用场景更多的是在被代理类在不方便修改时(比如sdk等第三方库的类),这时你想扩展或者新增它的一些功能的话就可以使用静态代理的方式。它的缺点就是不够灵活,比如说一个Fruit接口,你想要所有使用Fruit接口的类的方法调用时都增加一些逻辑,如果使用静态代理的方式,那么你需要创建出针对所有已知的实现了Fruit接口的类的代理类,而且,一旦出现了新的Fruit的实现类时,你需要同样创建一个对应的代理类才行。
针对静态代理的弊端,动态代理应运而生。
所谓动态代理指的就是在运行时才知道使用的是哪个实现类。
Java在java.lang.reflect包中提供了一个动态代理的工具类Proxy ,通过它我们就可以给任何类轻松地创建动态代理。
比如现在有一个Fruit接口:
public interface Fruit { public void getName(); }
有一个实现类Apple:
public class Apple implements Fruit { public void getName() { System.out.println("Apple"); } }
我们现在想在调用getName方法时增加一些逻辑该怎么办呢?先创建一个处理类实现自InvocationHandler接口:
public class FruitInvocationHandler implements InvocationHandler { private Object target; public FruitInvocationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before getName ... "); Object result = method.invoke(this.target, args); System.out.println("after getName... "); return result; } }
最后来使用Proxy调用原来的getName方法:
@Test public void proxyHello() throws Exception { //1. 创建被代理的目标对象 Fruit fruit= new Apple(); //2. 创建调用处理器 FruitInvocationHandler handler = new FruitInvocationHandler(fruit); //3. 获取对应的 ClassLoader ClassLoader classLoader = fruit.getClass().getClassLoader(); //4. 获取所有接口的interface Class[] interfaces = fruit.getClass().getInterfaces(); //5. 创建代理类 Fruit fruitProxy = (Fruit) Proxy.newProxyInstance(classLoader, interfaces, handler); //5. 调用方法 fruitProxy.getName(); }
下面我们就从源码角度看看它是怎么做到的。
-
源码分析
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); //构建代理类 Class<?> cl = getProxyClass0(loader, intfs); try { //反射代理类构造方法 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { cons.setAccessible(true); } //调用代理类构造方法,传入的参数是InvocationHandler的实现类对象 return cons.newInstance(new Object[]{h}); } catch... }
可以看到,这里使用反射构造了一个代理类对象,并把InvocationHandler的实现类对象传给它。关键在于代理类是什么。
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } return proxyClassCache.get(loader, interfaces); } private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
proxyClassCache是一个WeakCache对象,它的get方法如下:
public V get(K key, P parameter) { ... Object cacheKey = CacheKey.valueOf(key, refQueue); ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); if (oldValuesMap != null) { valuesMap = oldValuesMap; } } Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { V value = supplier.get(); if (value != null) { return value; } } if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { supplier = factory; } } else { if (valuesMap.replace(subKey, supplier, factory)) { supplier = factory; } else { supplier = valuesMap.get(subKey); } } } }
这个方法中简单概括就是获取之前的Supplier,如果没有就构造一个Factory(WeakCache的内部类),它实现了Supplier,WeakCache的get方法最终返回的就是Supplier的get方法返回的值,看一下WeakCache.Factory的get方法的实现:
@Override public synchronized V get() { ... V value = null; try { //通过valueFactory的apply方法构造返回值 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { valuesMap.remove(subKey, this); } } assert value != null; //缓存 ... return value; }
valueFactory是WeakCache在构造时赋值的,我们找到上面proxyClassCache的赋值代码发现valueFactory是ProxyClassFactory,它的apply方法如下:
@Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { //接口检查 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { 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"); } if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } //下面这部分概括起来就是如果实现接口中有非public类型的就把这个接口的包名作为最终Proxy的名字的前缀 String proxyPkg = null; int accessFlags = Modifier.PUBLIC | Modifier.FINAL; for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); //如果有非public接口 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)) { //所有的非public接口必须在同一包下 throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } //不含非public接口的话就不加这个pkg前缀 if (proxyPkg == null) { proxyPkg = ""; } //一个代码块 { //获取所有实现接口的所有方法,并且getMethods中手动添加Object中通用的equals、hashCode和toString方法(后面就知道为什么这么做) List<Method> methods = getMethods(interfaces); //按照方法签名进行排序,经过这一步后,重载的方法或者相同签名的方法会在相邻的位置上 Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE); //排序就是为了这一步的校验算法,内部会以排序后的顺序为逻辑前提 validateReturnTypes(methods); //去除重复方法并拿到所有方法的异常类型,同样以排序后的顺序为逻辑前提 List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods); Method[] methodsArray = methods.toArray(new Method[methods.size()]); Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]); //Proxy的名字 long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; //创建Proxy类(Class类型) return generateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray); } }
generateProxy方法是一个native方法,它在/art/runtime/native/java_lang_reflect_Proxy.cc中注册的:
namespace art { static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArray interfaces, jobject loader, jobjectArray methods, jobjectArray throws) { ScopedFastNativeObjectAccess soa(env); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); return soa.AddLocalReference<jclass>(class_linker->CreateProxyClass( soa, name, interfaces, loader, methods, throws)); } static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Proxy, generateProxy, "(Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/reflect/Method;[[Ljava/lang/Class;)Ljava/lang/Class;"), }; void register_java_lang_reflect_Proxy(JNIEnv* env) { REGISTER_NATIVE_METHODS("java/lang/reflect/Proxy"); } }
可见通过ClassLinker的CreateProxyClass函数创建:
mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& soa, jstring name, jobjectArray interfaces, jobject loader, jobjectArray methods, jobjectArray throws) { Thread* self = soa.Self(); StackHandleScope<10> hs(self); MutableHandle<mirror::Class> temp_klass(hs.NewHandle( AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)))); ... temp_klass->SetObjectSize(sizeof(mirror::Proxy)); // Set the class access flags incl. VerificationAttempted, so we do not try to set the flag on the methods. temp_klass->SetAccessFlags(kAccClassIsProxy | kAccPublic | kAccFinal | kAccVerificationAttempted); temp_klass->SetClassLoader(soa.Decode<mirror::ClassLoader>(loader)); temp_klass->SetName(soa.Decode<mirror::String>(name)); temp_klass->SetDexCache(GetClassRoot(kJavaLangReflectProxy)->GetDexCache()); // Object has an empty iftable, copy it for that reason. temp_klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable()); ... // Needs to be before we insert the class so that the allocator field is set. LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(temp_klass->GetClassLoader()); ... // Instance fields are inherited, but we add a couple of static fields... const size_t num_fields = 2; LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self, allocator, num_fields); temp_klass->SetSFieldsPtr(sfields); // 1. Create a static field 'interfaces' that holds the _declared_ interfaces implemented by our proxy, so Class.getInterfaces doesn't return the flattened set. ArtField& interfaces_sfield = sfields->At(0); interfaces_sfield.SetDexFieldIndex(0); interfaces_sfield.SetDeclaringClass(temp_klass.Get()); interfaces_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal); // 2. Create a static field 'throws' that holds exceptions thrown by our methods. ArtField& throws_sfield = sfields->At(1); throws_sfield.SetDexFieldIndex(1); throws_sfield.SetDeclaringClass(temp_klass.Get()); throws_sfield.SetAccessFlags(kAccStatic | kAccPublic | kAccFinal); // Proxies have 1 direct method, the constructor const size_t num_direct_methods = 1; // They have as many virtual methods as the array auto h_methods = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Method>>(methods)); const size_t num_virtual_methods = h_methods->GetLength(); // Create the methods array. LengthPrefixedArray<ArtMethod>* proxy_class_methods = AllocArtMethodArray( self, allocator, num_direct_methods + num_virtual_methods); temp_klass->SetMethodsPtr(proxy_class_methods, num_direct_methods, num_virtual_methods); //创建构造方法 CreateProxyConstructor(temp_klass, temp_klass->GetDirectMethodUnchecked(0, image_pointer_size_)); //创建方法 for (size_t i = 0; i < num_virtual_methods; ++i) { auto* virtual_method = temp_klass->GetVirtualMethodUnchecked(i, image_pointer_size_); auto* prototype = h_methods->Get(i)->GetArtMethod(); CreateProxyMethod(temp_klass, prototype, virtual_method); ... } ... //继承自java.lang.reflect.Proxy temp_klass->SetSuperClass(GetClassRoot(kJavaLangReflectProxy)); Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(temp_klass); MutableHandle<mirror::Class> klass = hs.NewHandle<mirror::Class>(nullptr); ... interfaces_sfield.SetObject<false>( klass.Get(), soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)); throws_sfield.SetObject<false>( klass.Get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws)); ... return klass.Get(); }
概括一下:
通过传进来的interfaces、methods、throws等信息去创建在native层的class信息,构造一个temp_class来保存这一切,然后把加载到ClassLoader中,最后用kclass来指向这一切,它的Get()方法返回一个java层的Class对象。
其中,CreateProxyConstructor方法中:
void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) { ... ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->FindConstructor( "(Ljava/lang/reflect/InvocationHandler;)V", image_pointer_size_); ... out->CopyFrom(proxy_constructor, image_pointer_size_); out->SetAccessFlags((out->GetAccessFlags() & ~kAccProtected) | kAccPublic | kAccCompileDontBother); out->SetDeclaringClass(klass.Get()); out->SetDataPtrSize(proxy_constructor, image_pointer_size_); }
这一步就是创建包含一个InvocationHandler参数的Proxy构造方法。
void ClassLinker::CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, ArtMethod* out) { out->CopyFrom(prototype, image_pointer_size_); out->SetDeclaringClass(klass.Get()); out->SetAccessFlags((out->GetAccessFlags() & ~kRemoveFlags) | kAddFlags); out->SetCodeItemOffset(0); out->SetDataPtrSize(prototype, image_pointer_size_); //设置方法执行入口为InvocationHandler out->SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); }
CreateProxyMethod方法中会创建同名方法,并且设置方法的执行入口为InvocationHandler的invoke函数,GetQuickProxyInvokeHandler()得到的就是我们newProxyInstance中传入的InvocationHandler。
-
总结
Proxy的动态代理是通过被代理类的interfaces信息在native层构造Class类,然后再设置class信息,这个过程中会根据被代理类的methods信息设置class的每个方法的ArtMethod结构体,包括构造方法(根据一个包含InvocationHandler参数的签名构建),在其他方法中会写入ArtMethod结构体中一个执行入口,当代理类的方法被调用时就会优先执行这个入口,这个入口设置的就是InvocationHandler,它的invoke方法会被调用,从而达到代理的效果。
动态代理Proxy
相关文章:
- iOS多线程运用
- iOS--CoreML初识
- iOS中frame和bounds区别
- java学习之变量
- iOS14 -[PHPhotoLibrary presentLimitedLibraryPickerFromViewController-] crash的问题
- vue中$refs, $emit, $on, $once, $off的使用详解
- [Vue warn]- The client-side rendered virtual DOM tree is not matching server-rendered content. Th...
- 基于Java,PPT转Html5(保留动画,可私有化部署,可对接接口)
- JS Date getTime 后变为 8点,js时间如何不从8点开始计算
- C++技能点之智能指针(二)