1.SOFABoot 源码解析
1.1 JVM 服务
1.1.1 服务发布
以SOFABoot自带的RPC案例sofaboot-sample-with-rpc为例,详细描述SOFABoot JVM服务发布过程。
在此提前说明,源码分析主要分析主流程,以及本人认为比较重要的一些内容,对于其它部分,大家可以基于本文档,自行研读。
RPC案例的SpringXML配置文件内容如下:
1. 2. 3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4. xmlns:sofa="http://sofastack.io/schema/sofaboot" 5. xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd 6. http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd" 7. default-autowire="byName"> 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
在SpringXML配置文件中:
1. 通过XML元素bean定义PersonService接口实现类PersonServiceImpl;
2. 通过XML元素sofa:service以jvm格式发布PersonService接口服务;
启动本应用,启动流程在《启动原理》中已经详细描述,在此不再详述,直接从SpringXML配置文件加载Bean定义开始分析。
一、 注册ServiceFactoryBean类对应的Bean定义
调用AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh())方法刷新Spring应用上下文过程,通过invokeBeanFactoryPostProcessors(beanFactory)方法调用beanFactory中所有实现了BeanFactoryPostProcessor接口的类。当调用到ConfigurationClassPostProcessor类processConfigBeanDefinitions方法时,在循环处理@Configuration配置类过程中,调用ConfigurationClassBeanDefinitionReader类loadBeanDefinitions(configClasses)方法,处理解析完成的@Configuration配置类。此时,其中有一步是调用ConfigurationClassBeanDefinitionReader类loadBeanDefinitionsFromImportedResources(configClass.getImportedResources())方法,加载@ImportResource注解里面配置的SpringXML配置文件中定义的Bean。在这个方法中,使用XMLBeanDefinitionReader加载SpringXML配置文件中定义的Bean。
接下来就是SpringXML配置文件中各种标签的解析过程。
对于XML标签bean,由于是最基本的SpringXML标签,大家都比较熟悉了,在此不再详述。
对于XML标签sofa:service,其处理过程如下:
XMLBeanDefinitionReader类调用DefaultBeanDefinitionDocumentReader类registerBeanDefinitions方法注册Bean定义。
DefaultBeanDefinitionDocumentReader类调用BeanDefinitionParserDelegate类parseCustomElement方法解析自定义的XML元素。
在此,对于XML标签sofa:service,根据命名空间sofa对应的值http://sofastack.io/schema/sofaboot,在spring.handlers(此文件位于infra-sofa-boot-starter.jar)文件中,查找到XML标签的处理类SofaBootNamespaceHandler:
1. http\://sofastack.io/schema/sofaboot= com.alipay.sofa.infra.config.spring.namespace.handler.SofaBootNamespaceHandler
在SofaBootNamespaceHandler类中,调用findParserForElement方法,查找指定XML元素的解析类,此处service对应的BeanDefinitionParser解析类为com.alipay.sofa.runtime.spring.parser.ServiceDefinitionParser。
使用ServiceDefinitionParser类把SpringXML配置文件中sofa:service标签定义的服务转换为ServiceFactoryBean,并注册到Spring应用上下文中。
二、 创建 ServiceFactoryBean类的实例
调用AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh())方法刷新Spring应用上下文过程,通过finishBeanFactoryInitialization(beanFactory)方法实例化beanFactory中所有剩余的非延迟初始化的单例对象。
当实例化ServiceFactoryBean时,由于ServiceFactoryBean实现了Initializing接口,所以在调用ServiceFactoryBean类的初始化方法时,会调用ServiceFactoryBean类的afterPropertiesSet方法,进行实例的初始化操作。
到现在为止,开始JVM服务发布流程:
1. 根据XML标签sofa:service所包含的子标签sofa:binding.*(一个或多个,此处是sofa:binding.jvm),解析出服务发布的类型,此处是jvm服务。
2. 创建DefaultImplementation实例implementation,并设置其属性ref为SpringXML文件中sofa-service元素的ref属性中指定的接口实现,此处为SpringXML文件中id为personServiceImpl的com.alipay.sofa.boot.examples.demo.rpc.bean.PersonServiceImpl类的实例。
3. 创建Service接口的实现ServiceImpl,其中,服务接口类型为com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService,接口模式为spring(表明此引用服务来自于SpringXML配置文件),target为ref(即id为personServiceImpl的PersonServiceImpl实例)。
4. 如果解析出的bindings为空,则把JvmBinding增加到bindings列表中。此处,由于在SpringXML文件中只配置了binding.jvm,在第一步parseBindings方法中,由于jvm没有对应的BindingConverter实现,所以导致bindings为空。
5. 把解析出的binding(此处为JvmBinding)增加到service实例的属性bindings列表中。
6. 创建ServiceComponent实例componentInfo。
7. 从sofaRuntimeContext实例中获取组件管理器实例componentManager,并调用其register方法注册刚才创建的服务组件componentInfo,具体注册操作在doRegister方法完成:
1. private ComponentInfo doRegister(ComponentInfo ci) { 2. ComponentName name = ci.getName(); 3. if (isRegistered(name)) { 4. SofaLogger.error("Component was alreadyregistered: {0}", name); 5. return getComponentInfo(name); 6. } 7. 8. try { 9. ci.register(); 10. } catch (Throwable e) { 11. SofaLogger.error(e, "Failed toregister component: {0}", ci.getName()); 12. return null; 13. } 14. 15. SofaLogger.info("Registeringcomponent: {0}", ci.getName()); 16. 17. try { 18. ComponentInfo old = registry.putIfAbsent(ci.getName(), ci); 19. if (old != null) { 20. SofaLogger.error("Component wasalready registered: {0}", name); 21. return old; 22. } 23. if ( ci.resolve() ) { 24. typeRegistry(ci); 25. ci.activate(); 26. } 27. } catch (Throwable e) { 28. ci.exception(new Exception(e)); 29. SofaLogger.error(e, "Failed tocreate the component {0}", ci.getName()); 30. } 31. 32. return ci; 33. }
在组件管理器中注册组件的主要处理逻辑如下:
获取组件名称,此处为service:com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService 。
判断组件管理器componentManager 中是否存在指定名字的组件。如果有,则直接从组件管理器获取组件,并返回。如果没有,则继续注册流程。
调用组件ServiceComponent 类的register方法,更新组件状态为registered。
把组件置入组件管理器的registry 中(ConcurrentMap)。
调用组件ServiceComponent 类的resolve方法,更新组件状态为resolved。
把组件置入组件管理器的resolvedRegistry 中(ConcurrentMap>)。
调用组件ServiceComponent 的activate方法,激活组件,并更新组件状态为activated。
1. public void activate() throwsServiceRuntimeException { 2. 3. activateBinding (); 4. super.activate(); 5. }
调用activateBinding方法,激活服务组件的所有Binding。
1. private void activateBinding() { 2. 3. Object target = service.getTarget(); 4. …… 略 5. if (service.hasBinding()) { 6. boolean allPassed = true; 7. Set bindings =service.getBindings(); 8. for(Binding binding : bindings) { 9. BindingAdapterbindingAdapter = this.bindingAdapterFactory 10. .getBindingAdapter(binding.getBindingType()); 11. 12. if (bindingAdapter == null) { 13. throw newServiceRuntimeException(…… 略 ); 14. } 15. 16. Object outBindingResult; 17. …… 略 18. try { 19. outBindingResult =bindingAdapter.outBinding(service, binding, target, 20. getContext()); 21. } catch (Throwable t) { 22. allPassed = false; 23. …… 略 24. continue; 25. } 26. if(!Boolean.FALSE.equals(outBindingResult)) { 27. …… 略 28. } else { 29. binding.setHealthy(false); 30. SofaLogger.info("< 31. binding.getBindingType(),service); 32. } 33. } 34. 35. if (!allPassed) { 36. throw new ServiceRuntimeException(…… 略 ); 37. } 38. } 39. 40. SofaLogger.info("Register Service- {0}", service); 41. }
activateBinding方法主要处理逻辑如下:
获取服务的目标类target ;
依次遍历服务所有 Binding ,分别为每种Binding 发布服务,具体过程如下: 根据Binding 类型(此处为jvm),获取BindingAdapter接口的实现,此处为:com.alipay.sofa.runtime.service.binding. JvmBindingAdapter 。 调用 JvmBindingAdapter 类outBinding 方法,对外发布服务:
1. public Object outBinding(Object contract,JvmBinding binding, Object target, 2. SofaRuntimeContextsofaRuntimeContext) { 3. return null; 4. }
由于是JVM服务,即只在同一个JVM中同一个应用的不同模块之间可以访问,同一个JVM中不同应用及JVM外部应用无法访问,所以此处outBinding方法为空方法,不对外提供任何服务。
由此可以看出,发布JVM服务只是把服务组件置入组件管理器的registry中(ConcurrentMap),然后把服务组件的状态设置为activated,表示服务发布成功。
1.1.2 服务引用
以SOFABoot自带的RPC案例sofaboot-sample-with-rpc为例,详细描述SOFABoot JVM服务引用过程。
在此提前说明,源码分析主要分析主流程,以及本人认为比较重要的一些内容,对于其它部分,大家可以基于本文档,自行研读。
RPC案例的SpringXML配置文件内容如下:
1. 2. 3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4. xmlns:sofa="http://sofastack.io/schema/sofaboot" 5. xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd 6. http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd" 7. default-autowire="byName"> 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
在SpringXML配置文件中:
1. 通过XML元素bean定义PersonService接口实现类PersonServiceImpl;
2. 通过XML元素sofa:reference引用jvm格式的PersonService接口服务;
启动本应用,启动流程在《启动原理》中已经详细描述,在此不再详述,直接到从SpringXML配置文件加载Bean定义开始分析。
一、 注册ReferenceFactoryBean类对应的Bean定义
调用AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh())方法刷新Spring应用上下文过程,通过invokeBeanFactoryPostProcessors(beanFactory)方法调用beanFactory中所有实现了BeanFactoryPostProcessor接口的类。
当调用到ConfigurationClassPostProcessor类processConfigBeanDefinitions方法时,在循环处理@Configuration配置类过程中,调用ConfigurationClassBeanDefinitionReader类loadBeanDefinitions(configClasses)方法,处理解析完成的@Configuration配置类。此时,其中有一步是调用ConfigurationClassBeanDefinitionReader类loadBeanDefinitionsFromImportedResources(configClass.getImportedResources())方法,加载@ImportResource注解里面配置的SpringXML配置文件中定义的Bean。在这个方法中,使用XMLBeanDefinitionReader加载SpringXML配置文件中定义的Bean。
接下来就是SpringXML配置文件中各种标签的解析过程。
对于XML标签bean,由于是最基本的SpringXML标签,大家都应用比较熟悉了,在此不再详述。
对于XML标签sofa:reference,其处理过程如下:
XMLBeanDefinitionReader类调用DefaultBeanDefinitionDocumentReader类registerBeanDefinitions方法注册Bean定义;
DefaultBeanDefinitionDocumentReader类调用BeanDefinitionParserDelegate类parseCustomElement方法解析自定义的XML元素。
在此,对于XML标签sofa:reference,根据命名空间sofa对应的值http://sofastack.io/schema/sofaboot,在spring.handlers(此文件位于infra-sofa-boot-starter.jar)文件中,查找到XML标签的处理类SofaBootNamespaceHandler:
1. http\://sofastack.io/schema/sofaboot= com.alipay.sofa.infra.config.spring.namespace.handler.SofaBootNamespaceHandler
在SofaBootNamespaceHandler类中,调用findParserForElement方法,查找指定XML元素的解析类,此处service对应的BeanDefinitionParser解析类为com.alipay.sofa.runtime.spring.parser.ReferenceDefinitionParser。
使用ReferenceDefinitionParser类把SpringXML配置文件中sofa:reference标签定义的服务转换为ReferenceFactoryBean,并注册到Spring应用上下文中。
二、 创建ReferenceFactoryBean类的实例
调用AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh())方法刷新Spring应用上下文。
当通过onRefresh() 方法调用createEmbeddedServletContainer方法,在调用DefaultListableBeanFactory类doGetBeanNamesForType(ResolvableType type, booleanincludeNonSingletons, boolean allowEagerInit)方法,获取类型为org.springframework.boot.context.embedded.EmbeddedServletContainerFactory的Bean名字时,会遍历beanFactory中所有beanDefinitionNames,判断每个beanName所代表的Bean定义RootBeanDefinition中beanClass是否与EmbeddedServletContainerFactory类型相匹配。当调用org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch方法判断名为personReferenceRest的BeanDefinition对应的实例与EmbeddedServletContainerFactory是否类型匹配。由于XML标签sofa:reference被解析为ReferenceFactoryBean类,该类实现了FactoryBean接口,表明其为工厂Bean,所以为了查看此工厂Bean创建的实例的具体类型,必须实例化此工厂Bean,即ReferenceFactoryBean。至此,又会调用AbstractBeanFactory的getObject方法,获取此ReferenceFactoryBean创建的具体实例。
当实例化ReferenceFactoryBean时,由于ReferenceFactoryBean实现了Initializing接口,所以在调用ReferenceFactoryBean类的初始化方法时,会调用ReferenceFactoryBean类的afterPropertiesSet方法,进行实例的初始化操作。
到现在为止,开始JVM服务引用流程:
1. 根据XML标签sofa:reference所包含的子标签sofa:binding.*(一个或多个,此处是sofa:binding.jvm),解析出服务引用的类型,此处是jvm服务。
2. 设置ReferenceFactoryBean的bindings属性。由于在SpringXML文件中只配置了binding.jvm,并且jvm没有对应的BindingConverter实现,所以bindings为空。
3. 创建Reference接口的实现ReferenceImpl,其中,引用的服务接口类型为com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService,接口模式为spring(表明此引用服务来自于SpringXML配置文件),jvmFirst为true(表示优先使用JVM服务,即在同一个JVM内部同一个应用不同模块之间服务调用方式)。
4. 如果bindings为空,则把JvmBinding增加到bindings;
5. 把bindings中第一个Binding(此处为JvmBinding)增加到reference实例的属性bindings集合中。
6. 调用ReferenceRegisterHelper类registerReference方法:
1. public static ObjectregisterReference(Reference reference, 2. BindingAdapterFactorybindingAdapterFactory, 3. SofaRuntimePropertiessofaRuntimeProperties, 4. SofaRuntimeContextsofaRuntimeContext) { 5. Binding binding = (Binding)reference.getBindings().toArray()[0]; 6. 7. if(!binding.getBindingType().equals(JvmBinding.JVM_BINDING_TYPE) 8. &&!sofaRuntimeProperties.isDisableJvmFirst() && reference.isJvmFirst()) { 9. reference.addBinding(new JvmBinding()); 10. } 11. 12. ComponentManager componentManager =sofaRuntimeContext.getComponentManager(); 13. ReferenceComponent referenceComponent =new ReferenceComponent(reference, 14. new DefaultImplementation(),bindingAdapterFactory, sofaRuntimeProperties, 15. sofaRuntimeContext); 16. 17. if(componentManager.isRegistered(referenceComponent.getName())) { 18. returncomponentManager.getComponentInfo(referenceComponent.getName()) 19. .getImplementation().getTarget(); 20. } 21. 22. ComponentInfo componentInfo =componentManager.registerAndGet(referenceComponent); 23. return componentInfo.getImplementation().getTarget(); 24. 25. }
注册Reference组件的主要步骤:
获取reference实例包含的binding,此处为JvmBinding类型;
如果binding不是JvmBinding类型,disableJvmFirst属性为false(表明全局开启jvmFirst),jvmFirst为true,则增加JvmBinding到reference实例的属性bindings集合中。此处binding为JvmBinding,所以直接跳过,继续后续流程;
从sofaRuntimeContext实例中获取组件管理器实例componentManager;
创建ReferenceComponent实例;
判断组件管理器componentManager中是否存在指定名字的组件,例如:名称为reference:com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService:#342780926,如果有,则直接从组件管理器获取组件,并返回组件的实现的代理。如果没有,则在组件管理器中注册刚才创建的ReferenceComponent实例。具体的组件注册在doRegister方法中实现:
1. private ComponentInfodoRegister(ComponentInfo ci) { 2. ComponentName name = ci.getName(); 3. if (isRegistered(name)) { 4. SofaLogger.error("Componentwas already registered: {0}", name); 5. return getComponentInfo(name); 6. } 7. 8. try { 9. ci.register() ; 10. } catch (Throwable e) { 11. SofaLogger.error(e, "Failed toregister component: {0}", ci.getName()); 12. return null; 13. } 14. 15. SofaLogger.info("Registeringcomponent: {0}", ci.getName()); 16. 17. try { 18. ComponentInfo old = registry.putIfAbsent(ci.getName(), ci); 19. if (old != null) { 20. return old; 21. } 22. if ( ci.resolve() ) { 23. typeRegistry(ci); 24. ci.activate() ; 25. } 26. } catch (Throwable e) { 27. …… 略 28. } 29. 30. return ci; 31. }
在组件管理器中注册组件的主要过程如下:
调用组件ComponentInfo类的register方法,更新组件状态为registered。
把组件置入组件管理器的registry中(ConcurrentMap)。
调用组件ComponentInfo类的resolve方法,更新组件状态为resolved。
把组件置入组件管理器的resolvedRegistry中(ConcurrentMap>)。
调用组件ComponentInfo类的activate方法,激活组件,并更新组件状态为activated。
1. public void activate() throwsServiceRuntimeException { 2. if (reference.hasBinding()) { 3. Binding candidate = null; 4. Set bindings =reference.getBindings(); 5. if (bindings.size() == 1) { 6. candidate =bindings.iterator().next(); 7. } else if (bindings.size() > 1){ 8. Object backupProxy = null; 9. for (Binding binding :bindings) { 10. if(JvmBinding.JVM_BINDING_TYPE.getType().equals(binding.getName())) { 11. candidate = binding; 12. } else { 13. backupProxy = createProxy(reference, binding); 14. } 15. } 16. if (candidate != null) { 17. ((JvmBinding)candidate).setBackupProxy(backupProxy); 18. } 19. } 20. 21. Object proxy = null; 22. if (candidate != null) { 23. proxy = createProxy(reference, candidate); 24. } 25. 26. this.implementation = newDefaultImplementation(); 27. implementation.setTarget(proxy); 28. } 29. 30. super.activate(); 31. latch.countDown(); 32. }
在ComponentInfo类activate方法中,主要处理逻辑如下:
获取ReferenceImpl中所有Binding。
此处需要注意的是,在rpc.xsd文件中,规定XML标签sofa-reference只能有0个或1个Binding子元素。其可选子元素:binding.jvm、binding.bolt、binding.rest、binding.dubbo。
如果设置多个Binding子元素,则在实例化时抛异常。
如果不设置Binding子元素,则默认为JvmBinding。
这个隐含的规则从这部分代码中无法体现,但影响其下面的处理逻辑。
由此可知,Binding的个数只能为1或2。
当Binding的个数当为1时,则只能是JvmBinding或其他类型Binding之一。
当Binding的个数当为2时,则只能是JvmBinding和其他类型Binding之一。
在下述三种情况下,ReferenceImpl中所有Binding的个数为1:
★ 当未设置Binding子元素,而采用默认的JvmBinding;
★ 设置Binding子元素为JvmBinding;
★ 设置一个其他类型的Binding,但disableJvmFirst为false,并且jvmFirst为true。
在下述情况下,ReferenceImpl中所有Binding的个数为2:
★ 设置一个其他类型的Binding,但disableJvmFirst为true,或jvmFirst为false。
★ 设置candidate(候选Binding)为当前Binding;
★ 如果Binding类型为jvm,则设置candidate为JvmBinding。
★ 如 果Binding类型为其它类型的Binding,如RestBinding,则调用createProxy(reference,binding)方法,根据Binding类型创建代理对象。
★ 设置JvmBinding的备选代理对象(backupProxy)为刚才创建的其它类型Binding的代理对象。
如果candidate不为null,则调用createProxy(reference,candidate)方法,为候选Binding(candidate)创建代理对象proxy。否则,proxy为null。
创建DefaultImplementation实例,并设置ReferenceComponent实例target属性为刚才为candidate创建的代理对象proxy。
上述逻辑稍微有点复杂,简单来说就是:
在优先使用JVM服务的前提下,如果配置了一个其它类型的服务,如Rest服务,则把它作为JVM服务的备选服务。当JVM服务不可用时,使用备选服务。如果没有配置其它类型服务,则JVM服务没有备选服务,当JVM服务不可用时,则服务不可用,调用失败。
在不优先使用JVM服务的前提下,则只能使用配置的其它类型的服务,如Rest服务。
在此案例中,设置candidate为JvmBinding。由于只设置了JvmBinding,没有设置其它类型的Binding,所以JvmBinding的备选代理对象为null。这样当JvmBinding不可用时,无备选方案可用。
在此,我们主要关注一下通过createProxy方法为JvmBinding创建代理对象的过程:
1. private Object createProxy(Referencereference, Binding binding) { 2. BindingAdapterbindingAdapter = bindingAdapterFactory.getBindingAdapter(binding 3. .getBindingType()); 4. …… 略 5. Object proxy; 6. try { 7. proxy = bindingAdapter.inBinding(reference,binding, sofaRuntimeContext); 8. } finally { 9. …… 略 10. return proxy; 11. }
根据Binding类型,通过BindingAdapterFactoryImpl类getBindingAdapter方法,获取指定Binding类型的Binding适配器,此处是JvmBindingAdapter。
调用JvmBindingAdapter类inBinding方法创建代理对象:
1. public Object inBinding(Object contract,JvmBinding binding, 2. SofaRuntimeContextsofaRuntimeContext) { 3. return createServiceProxy((Contract)contract, binding, sofaRuntimeContext); 4. }
调用createServiceProxy方法:
1. private Object createServiceProxy(Contractcontract, JvmBinding binding, 2. SofaRuntimeContextsofaRuntimeContext) { 3. ClassLoader newClassLoader; 4. ClassLoader appClassLoader =sofaRuntimeContext.getAppClassLoader(); 5. Class> javaClass =contract.getInterfaceType(); 6. 7. try { 8. Class appLoadedClass =appClassLoader.loadClass(javaClass.getName()); 9. 10. if (appLoadedClass == javaClass) { 11. newClassLoader =appClassLoader; 12. } else { 13. newClassLoader =javaClass.getClassLoader(); 14. } 15. } catch (ClassNotFoundException e) { 16. newClassLoader =javaClass.getClassLoader(); 17. } 18. 19. ClassLoader oldClassLoader =Thread.currentThread().getContextClassLoader(); 20. 21. try { 22. Thread.currentThread().setContextClassLoader(newClassLoader); 23. ServiceProxy handler = newJvmServiceInvoker(contract, binding, sofaRuntimeContext); 24. ProxyFactory factory = newProxyFactory(); 25. if (javaClass.isInterface()) { 26. factory.addInterface(javaClass); 27. } else { 28. factory.setTargetClass(javaClass); 29. factory.setProxyTargetClass(true); 30. } 31. factory.addAdvice(handler); 32. return factory.getProxy(); 33. } finally { 34. Thread.currentThread().setContextClassLoader(oldClassLoader); 35. } 36. }
在createServiceProxy方法中,创建服务代理的主要步骤如下:
从sofaRuntimeContext获取应用类加载器appClassLoader,此处为:sun.misc.Launcher$AppClassLoader实例;
设置javaClass为入参contract的getInterfaceType方法返回的接口类型,此处为com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService;
使用应用类加载器appClassLoader加载服务接口PersonService;
设置newClassLoader为javaClass对应的Class对象的类加载器;
设置oldClassLoader为当前线程的上下文类加载器;
设置当前线程的上下文类加载器为newClassLoader;
设置handler,此处为ServiceProxy抽象类的子类JvmServiceInvoker的实例。该类继了ServiceProxy,而ServiceProxy实现了org.aopalliance.intercept.MethodInterceptor接口,表示此处创建AOP代理的方法拦截器。 该AOP 代理拦截器拦截对proxyClass指定的接口(例如:com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService)的方法调用,具体流程在后面详述;
创建AOP代理工厂类org.springframework.aop.framework.ProxyFactory的实例factory;
配置factory:如果javaClass为接口,则factory的interfaces列表中增加javaClass。否则,设置factory的targetClass为javaClass,proxyTarget为true;
把指定的AOP advice实例handler增加到advice链尾部;
调用factory的getProxy方法,为指定服务创建AOP代理对象。由于目标类是接口,则使用JDK动态代理技术。Spring使用JDK来生成代理对象,具体的生成代码放在org.springframework.aop.framework.JdkDynamicAopProxy这个类中。JdkDynamicAopProxy 同时实现了AopProxy和InvocationHandler接口,InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。invoke主流程可以简述为:获取可以应用到此方法上的通知链(InterceptorChain),如果有,则应用通知,并执行joinpoint;如果通知链为空,则直接反射执行joinpoint。
关于AOP代理对象的更多细节,在此不详细描述,有兴趣的同学自己在网上查资料看看。
至此,JvmBinding的AOP代理对象创建完毕,此代理对象的handler为JdkDynamicAopProxy,实现了PersonService接口。
接下来,创建DefaultImplementation实例,并设置ReferenceComponent实例target属性为刚才创建的candidate代理对象。
设置ReferenceFactoryBean的proxy为刚才创建的AOP代理对象。这样,当其它类引用某个服务的ReferenceFactoryBean类时,由于此类为工厂Bean,所以通过getObject方法获取的实际对象为AOP代理对象。这样对生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。
至此,JvmBinding类型的服务的代理对象创建完成。
1.1.3 客户端调用
当我们在SpringXML文件中使用sofa:reference引用服务以后,就可以在其它类中引用创建的指定接口的代理对象,并像调用本地Java类那样,调用接口的某个方法。
以下通过在Spring应用上下文中按照名字personReferenceJvm查找com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService接口的代理对象,然后调用其sayName方法,描述客户端调用Jvm服务的流程:
1. public static void main(String[] args)throws InterruptedException { 2. 3. ConfigurableApplicationContext ac =SpringApplication.run(SofaBootRpcDemoApplication.class, args); 4. 5. PersonService personService = (PersonService)ac.getBean("personReferenceJvm"); 6. 7. personService.sayName("Mike"); 8. 9. }
通过ConfigurableApplicationContext类getBean方法,获取名字为personReferenceJvm的实例,此时返回PersonService接口的AOP代理对象。此代理对象的handler为JdkDynamicAopProxy,实现了PersonService接口。
1. public Object invoke(Object proxy, Methodmethod, Object[] args) throws Throwable { 2. MethodInvocationinvocation; 3. ObjectoldProxy = null; 4. booleansetProxyContext = false; 5. 6. TargetSourcetargetSource = this.advised.targetSource; 7. Class>targetClass = null; 8. Objecttarget = null; 9. 10. try{ 11. if(!this.equalsDefined && AopUtils.isEqualsMethod(method)) { 12. //The target does not implement the equals(Object) method itself. 13. returnequals(args[0]); 14. } 15. elseif (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { 16. //The target does not implement the hashCode() method itself. 17. returnhashCode(); 18. } 19. elseif (method.getDeclaringClass() == DecoratingProxy.class) { 20. //There is only getDecoratedClass() declared -> dispatch to proxy config. 21. returnAopProxyUtils.ultimateTargetClass(this.advised); 22. } 23. elseif (!this.advised.opaque && method.getDeclaringClass().isInterface()&& 24. method.getDeclaringClass().isAssignableFrom(Advised.class)){ 25. //Service invocations on ProxyConfig with the proxy config... 26. returnAopUtils.invokeJoinpointUsingReflection(this.advised, method, args); 27. } 28. 29. ObjectretVal; 30. 31. if(this.advised.exposeProxy) { 32. //Make invocation available if necessary. 33. oldProxy= AopContext.setCurrentProxy(proxy); 34. setProxyContext= true; 35. } 36. 37. //May be null. Get as late as possible to minimize the time we "own"the target, 38. //in case it comes from a pool. 39. target= targetSource.getTarget(); 40. if(target != null) { 41. targetClass= target.getClass(); 42. } 43. 44. //Get the interception chain for this method. 45. List chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 46. 47. //Check whether we have any advice. If we don't, we can fallback on direct 48. //reflective invocation of the target, and avoid creating a MethodInvocation. 49. if(chain.isEmpty()) { 50. //We can skip creating a MethodInvocation: just invoke the target directly 51. //Note that the final invoker must be an InvokerInterceptor so we know it does 52. //nothing but a reflective operation on the target, and no hot swapping or fancyproxying. 53. Object[]argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); 54. retVal= AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); 55. } 56. else{ 57. //We need to create a method invocation... 58. invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); 59. //Proceed to the joinpoint through the interceptor chain. 60. retVal = invocation.proceed(); 61. } 62. 63. //Massage return value if necessary. 64. Class>returnType = method.getReturnType(); 65. if(retVal != null && retVal == target &&returnType.isInstance(proxy) && 66. !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())){ 67. //Special case: it returned "this" and the return type of the method 68. //is type-compatible. Note that we can't help if the target sets 69. //a reference to itself in another returned object. 70. retVal= proxy; 71. } 72. elseif (retVal == null && returnType != Void.TYPE &&returnType.isPrimitive()) { 73. thrownew AopInvocationException( 74. "Nullreturn value from advice does not match primitive return type for: " +method); 75. } 76. returnretVal; 77. } 78. finally{ 79. if(target != null && !targetSource.isStatic()) { 80. //Must have come from TargetSource. 81. targetSource.releaseTarget(target); 82. } 83. if(setProxyContext) { 84. //Restore old proxy. 85. AopContext.setCurrentProxy(oldProxy); 86. } 87. } 88. }
获取PersonService接口sayName方法拦截器链,此处只有一个,即[com.alipay.sofa.runtime.service.binding.JvmBindingAdapter$JvmServiceInvoker@4d45094f]。
为sayName方法创建一个ReflectiveMethodInvocation实例invocation,表示一个方法调用。
调用ReflectiveMethodInvocation的proceed方法,执行拦截器链:
1. public Object proceed() throws Throwable { 2. // We start with an index of -1 and incrementearly. 3. if(this.currentInterceptorIndex ==this.interceptorsAndDynamicMethodMatchers.size() - 1) { 4. returninvokeJoinpoint(); 5. } 6. ObjectinterceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); 7. if(interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){ 8. …… 略 9. } 10. else{ 11. //It's an interceptor, so we just invoke it: The pointcut will have 12. //been evaluated statically before this object was constructed. 13. return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this); 14. } 15. }
从interceptorsAndDynamicMethodMatchers中获取指定位置的拦截器,此处为com.alipay.sofa.runtime.service.binding.JvmBindingAdapter$JvmServiceInvoker的一个实例。
调用JvmBindingAdapter$JvmServiceInvoker的invoke方法:
1. public Object doInvoke(MethodInvocationinvocation) throws Throwable { 2. if (binding.isDestroyed()) { 3. throw newIllegalStateException(…… 略 ); 4. } 5. …… 略 6. Object retVal; 7. Object targetObj = this.getTarget(); 8. 9. if ((targetObj == null ||((targetObj instanceof Proxy) && binding.hasBackupProxy()))) { 10. targetObj =binding.getBackupProxy(); 11. …… 略 12. } 13. 14. if (targetObj == null) { 15. throw newIllegalStateException(…… 略 ); 16. } 17. 18. ClassLoader tcl = Thread.currentThread().getContextClassLoader(); 19. try { 20. pushThreadContextClassLoader(sofaRuntimeContext.getAppClassLoader()); 21. retVal = invocation.getMethod().invoke(targetObj,invocation.getArguments()); 22. } catch (InvocationTargetExceptionex) { 23. throw ex.getTargetException(); 24. } finally { 25. …… 略 26. popThreadContextClassLoader(tcl); 27. } 28. 29. return retVal; 30. }
调用JvmBindingAdapter$JvmServiceInvoker的getTarget方法,获取目标类:
1. protected Object getTarget() {
2. if (this.target == null) { 3. ComponentName componentName =ComponentNameFactory.createComponentName( 4. ServiceComponent.SERVICE_COMPONENT_TYPE,getInterfaceName(), 5. contract.getUniqueId()); 6. ComponentInfo componentInfo =sofaRuntimeContext.getComponentManager() 7. .getComponentInfo(componentName); 8. 9. if (componentInfo != null) { 10. this.target =componentInfo.getImplementation().getTarget(); 11. } 12. } 13. 14. return target; 15. }
根据服务接口类型为服务组件创建ComponentName实例componentName,此处组件名的字符串值为service:com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService;
从sofaRuntimeContext实例获取组件管理器,然后调用组件管理器getComponentInfo方法,获取名为componentName的服务组件,即ServiceComponent实例;
调用ServiceComponent类getImplementation方法,获取服务组件的属性implementation的值,即DefaultImplementation实例;
最后,调用DefaultImplementation的getTarget方法,获取服务具体实现类实例,此处为PersonServiceImpl的实例。此值是在ServiceFactoryBean初始化时设置的。
获取到target以后,判断target是否为null。如果为null,则设置target为候选代理对象(在一个接口发布多种类型的服务时,如Jvm、Rest等,则会为Jvm类型的服务设置一个候选,以便在Jvm服务不可用时,使用备选服务)。此处不为null,则继续服务调用。
最后,通过Java反射机制,调用目标对象的某个方法,此处为PersonServiceImpl类sayName方法。到此为止,Jvm服务调用完成,返回结果。
1.1.4 服务端响应
由于JVM服务调用是同一个JVM内Java 本地方法调用,所以直接执行目标方法,并返回处理结果。