1.SOFABoot源码解析
1.1 JVM服务
1.1.1 服务发布
以SOFABoot自带的RPC案例sofaboot-sample-with-rpc为例,详细描述SOFABoot JVM服务发布过程。
在此提前说明,源码分析主要分析主流程,以及本人认为比较重要的一些内容,对于其它部分,大家可以基于本文档,自行研读。
RPC案例的SpringXML配置文件内容如下:
1. <?xml version="1.0"encoding="UTF-8"?>
2. <beansxmlns="http://www.springframework.org/schema/beans"
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. <bean id="personServiceImpl"class="com.alipay.sofa.boot.examples.demo.rpc.bean.PersonServiceImpl"/>
10.
11. <sofa:serviceref="personServiceImpl"interface="com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService">
12. <sofa:binding.jvm/>
13. </sofa:service>
14.
15. <sofa:referenceid="personReferenceRest"interface="com.alipay.sofa.boot.examples.demo.rpc.bean.PersonService">
16. <sofa:binding.jvm/>
17. </sofa:reference>
18.
19. <bean id="personFilter"class="com.alipay.sofa.boot.examples.demo.rpc.bean.PersonServiceFilter"/>
20.
21. </beans>
在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<ComponentName, ComponentInfo>)。
- 调用组件ServiceComponent类的resolve方法,更新组件状态为resolved。
- 把组件置入组件管理器的resolvedRegistry中(ConcurrentMap<ComponentType, Map<ComponentName,ComponentInfo>>)。
- 调用组件ServiceComponent的activate方法,激活组件,并更新组件状态为activated。
1. public void activate() throwsServiceRuntimeException {
2.
3. activateBinding();
4. super.activate();
5. }
调用activateBinding方法,激活服务组件的所有Binding。