Controller接口调用链路浅析 项目初始化,创建代理类 1 2 3 4 5 AbstractClassGenerator#generate() 358:{strategy.generate(this)} -> DefaultGeneratorStrategy#generate() 25:{transform(cg).generateClass(cw)} -> Enhancer#generateClass() 670:{getMethods(...)} -> Enhancer#generateClass() 651:{CollectionUtils.filter(methods, new VisibilityPredicate(superclass, true));} -> AbstractClassGenerator#generate() 363:{gen = ReflectUtils.defineClass(...)}
主要是在AbstractClassGenerator
这个类完成代理类的生成,其中调用了cglib相关逻辑过滤原生类的方法, DefaultGeneratorStrategy
、CollectionUtils
需要导入cglib源码进行查看,而private方法则被VisibilityPredicate
这个策略过滤了,源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public boolean evaluate (Object arg) { Member member = (Member)arg; int mod = member.getModifiers(); if (Modifier.isPrivate(mod)) { return false ; } else if (Modifier.isPublic(mod)) { return true ; } else if (Modifier.isProtected(mod) && protectedOk) { return true ; } else { return samePackageOk && pkg.equals(TypeUtils.getPackageName(Type.getType(member.getDeclaringClass()))); } }
同一个代理类生成流程会被多次触发,这个原因还未深究,不知道出于什么目的,可以标记一下这个问题点。
tomcat开启线程,走到过滤器链之前的一个定向逻辑链路调用 tomcat这部分调用链绕弯很多,我没有细看这部分的映射逻辑,和我们的问题核心也没有太大关系,有兴趣可以看看,
1 2 3 4 5 6 7 8 9 10 11 at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
过滤器链路 过滤器链路是在项目启动时提前扫描好注入上下文中的,servlet原生过滤链,ApplicationFilterChain
,以下是主要调用链的逻辑:
1 2 3 4 5 6 ApplicationFilterChain#doFilter() 调用链路 -> ApplicationFilterChain#internalDoFilter() 193 索引移动,调用过滤器 -> Filter#doFilter() 相应过滤器逻辑 -> chain.doFilter(request, response) 过滤器完成后继续调用链路 -> 重复上述动作直至遍历完链路 ApplicationFilterChain#internalDoFilter() 231 servlet.service(request, response) -> 开始servlet调用接口...
完成上述tomcat的定向逻辑调用后就会开启过滤器链路
1 2 StandardWrapperValve#invoke() 174 ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
调用controller接口 过滤器链路走完后,继续servlet请求处理,执行doService、doDispatch相关操作。然后便是调用接口方法,这里将针对方法属性进行是否代理类拦截增强操作。我们先生成代理类。
Method invoke c源码下载:https://hg.openjdk.org/jdk8u,想看虚拟机源码点击hotspot,加载页面后点击左侧栏zip、gc下载源码压缩包,我们需要jdk和hotspot的源码。
我们知道源码都是使用反射进行动态代理的,这一步骤就是调用Method#invoke()
方法,一层层往下看,最终调用的native方法:
1 2 NativeMethodAccessorImpl.class private static native Object invoke0 (Method m, Object obj, Object[] args) ;
jdk源码进一步调用jvm
1 2 3 4 5 6 7 jdk-7f cf35286d52\src\share\native\sun\reflect\NativeAccessors.c JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0 (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args) { return JVM_InvokeMethod(env, m, obj, args); }
关键反射调用 Reflection::invoke_method
实力有限,这块c源码确实啃不动,单纯抛砖引玉一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 hotspot-69087 d08d473\src\share\vm\prims\jvm.cpp JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0)) JVMWrapper("JVM_InvokeMethod" ); Handle method_handle; if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) { method_handle = Handle(THREAD, JNIHandles::resolve(method)); Handle receiver (THREAD, JNIHandles::resolve(obj)) ; objArrayHandle args (THREAD, objArrayOop(JNIHandles::resolve(args0))) ; oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL); jobject res = JNIHandles::make_local(env, result); if (JvmtiExport::should_post_vm_object_alloc()) { oop ret_type = java_lang_reflect_Method::return_type(method_handle()); assert(ret_type != NULL , "sanity check: ret_type oop must not be NULL!" ); if (java_lang_Class::is_primitive(ret_type)) { JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } } return res; } else { THROW_0(vmSymbols::java_lang_StackOverflowError()); } JVM_END
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 oop Reflection::invoke_method (oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) { oop mirror = java_lang_reflect_Method::clazz(method_mirror); int slot = java_lang_reflect_Method::slot(method_mirror); bool override = java_lang_reflect_Method::override (method_mirror) != 0 ; objArrayHandle ptypes (THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror))) ; oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror); BasicType rtype; if (java_lang_Class::is_primitive(return_type_mirror)) { rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL); } else { rtype = T_OBJECT; } instanceKlassHandle klass (THREAD, java_lang_Class::as_Klass(mirror)) ; Method* m = klass->method_with_idnum(slot); if (m == NULL ) { THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke" ); } methodHandle method (THREAD, m) ; return invoke(klass, method, receiver, override , ptypes, rtype, args, true , THREAD); }
生成cglib代理类 使用System.setPropert()
定义输出路径,将 YOUR_CLASS 替换成你生成代理类的原生类,最后调用一下类方法,在对应路径下找到生成的代理类。
当然生成后的是class文件,需要反编译源码,可以使用jd-gui:https://github.com/java-decompiler/jd-gui/releases
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Main { public static void main (String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class" ); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(YOUR_CLASS.class); enhancer.setCallback(new MyMethodInterceptor()); YOUR_CLASS class = (YOUR_CLASS) enhancer.create(); class.function(); } static class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, objects); } } }
代理类过滤链路
代理类调用前都是通过Enhancer
设置基类和回调,对应上文中的enhancer.setSuperclass()
、enhancer.setCallback()
,回调类实现cglib的扩展接口,也就是实现intercept()
外部调用cglib的invoke()
通过两个fastclass相关的映射类去调用代理类的方法
代理方法,通过判断是否还有拦截增强器,进行增强或走原生方法,代理类的基类是我们的原生方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public Object invoke (int paramInt, Object paramObject, Object[] paramArrayOfObject) throws InvocationTargetException { try { switch (paramInt) { case 0 : return ((OhbController)paramObject).add((Ohb)paramArrayOfObject[0 ]); ... } } catch () {...} } public Object invoke (int paramInt, Object paramObject, Object[] paramArrayOfObject) throws InvocationTargetException { try { switch (paramInt) { case 0 : return ((OhbController$$EnhancerByCGLIB$$a5ddc9c3)paramObject).add((Ohb)paramArrayOfObject[0 ]); } } catch () {...} } public final Object add (Ohb paramOhb) { if (this .CGLIB$CALLBACK_0 == null ) CGLIB$BIND_CALLBACKS(this ); return (this .CGLIB$CALLBACK_0 != null ) ? this .CGLIB$CALLBACK_0.intercept(this , CGLIB$add$0 $Method, new Object[] { paramOhb }, CGLIB$add$0 $Proxy) : super .add(paramOhb); }
cglib 初始化生成抽象类的时候,就把private方法排除了。
现在问题点是invoke0方法执行的时候是个什么调用逻辑
1 private static native Object invoke0 (Method m, Object obj, Object[] args) ;
debug结果是,obj是代理对象,而代理对象中不存在已被过滤的方法,不能被调用链路增强,直接反射调用了方法,此时的类相当于是反射映射的,非spring管理。
而public可以在增强链路中,可以被增强,通过增强链路调用了容器中bean
1 2 3 4 5 6 -- cglib源码 <dependency > <groupId > cglib</groupId > <artifactId > cglib</artifactId > <version > 3.1</version > </dependency >
invoke0 调用代理类方法,能调用到就调用,private方法调用不到就直接映射到父类了,这个是猜测,需要验证。
多个代理,EnhancerBySpringCGLIB、FastClassBySpringCGLIB,存在映射关系,intercept走aop链
看源码,如果没有设置Enhancer callback,则直接调用父类原方法
所以private没有的方法转发是在invoke0字节码中被发射处理了