SOFARPC 發(fā)布和引用 RPC 服務(wù)方式:
1、XML 方式(配置):xml引入 SOFA 命名空間,Bean 生命周期管理, Bean 的注入
2、Annotation 方式(注解):發(fā)布 JVM 服務(wù)更靈活,實(shí)現(xiàn)類上加?@SofaService、@SofaRefernce
3、編程 API 方式(動態(tài)):與Spring 的?ApplicationContextAware?類似
一、注解原理解析
1.1、注解是什么
元數(shù)據(jù),形式化對代碼中添加信息,編譯、運(yùn)行時使用。繼承 Annotation 接口的接口。有解析它代碼的特殊注釋,常用:
(1)Java自帶:@Override、@Deprecated(過時)、@SuppressWarnings;
(2)元注解:定義注解的注解;(3)自定義注解
1.2、元注解(JAVA 元注解)
1、@Target:指明修飾方法、類、字段屬性:
ElementType.TYPE:允許被修飾的注解作用在類、接口和枚舉上
????ElementType.FIELD:作用屬性字段上
????ElementType.METHOD:作用方法上
????ElementType.PARAMETER:方法參數(shù)
????ElementType.CONSTRUCTOR:構(gòu)造器
????ElementType.LOCAL_VARIABLE:本地局部變量
????ElementType.ANNOTATION_TYPE:注解
????ElementType.PACKAGE:允許作用在包上
2、@Retention:被修飾注解的生命周期,三種類型:
RetentionPolicy.SOURCE:只保留源文件中,編譯成class時,class中木有。
RetentionPolicy.CLASS:只保留class文件中,加載class文件內(nèi)存,虛擬機(jī)將注解去掉,程序中不能訪問。
RetentionPolicy.RUNTIME:運(yùn)行期在內(nèi)存中。反射獲得某個類上所有注解。
3、@Documented:執(zhí)行 JavaDoc 文檔打包時被保存 doc 文檔,反之打包時丟棄。
4、@Inherited:可繼承性,修飾類的子類自動繼承父類的該注解。
1.3、注解解析方式
1.3.1、編譯器的掃描
指的是編譯器在對 java 代碼編譯字節(jié)碼的過程中會檢測到某個類或者方法被一些注解修飾,這時它就會對于這些注解進(jìn)行某些處理。典型的就是注解?@Override,一旦編譯器檢測到某個方法被修飾了?@Override?注解,編譯器就會檢查當(dāng)前方法的方法簽名是否真正重寫了父類的某個方法,也就是比較父類中是否具有一個同樣的方法簽名。
這一種情況只適用于那些編譯器已經(jīng)熟知的注解類,比如 JDK 內(nèi)置的幾個注解,而你自定義的注解,編譯器是不知道你這個注解的作用的。
1.3.1、運(yùn)行期反射
首先對虛擬機(jī)的幾個注解相關(guān)的屬性表進(jìn)行介紹,先大體了解注解在字節(jié)碼文件中是如何存儲的。虛擬機(jī)規(guī)范定義了一系列和注解相關(guān)的屬性表,也就是說,無論是字段、方法或是類本身,如果被注解修飾了,就可以被寫進(jìn)字節(jié)碼文件。屬性表有以下幾種:
RuntimeVisibleAnnotations:運(yùn)行時可見的注解
RuntimeInVisibleAnnotations:運(yùn)行時不可見的注解
RuntimeVisibleParameterAnnotations:運(yùn)行時可見的方法參數(shù)注解
RuntimeInVisibleParameterAnnotations:運(yùn)行時不可見的方法參數(shù)注解
AnnotationDefault:注解類元素的默認(rèn)值
java.lang.reflect.AnnotatedElement接口是所有程序元素(Class、Method和Constructor)的父接口,程序通過反射獲取了某個類的 AnnotatedElemen t對象之后,利用 Java 的反射機(jī)獲取程序代碼中的注解,然后根據(jù)預(yù)先設(shè)定的處理規(guī)則解析處理相關(guān)注解以達(dá)到主機(jī)本身設(shè)定的功能目標(biāo)。
本質(zhì)上來說,反射機(jī)制就是注解使用的核心,程序可以調(diào)用該對象的以下方法來訪問 Annotation信息:
getAnnotation:返回指定的注解
isAnnotationPresent:判定當(dāng)前元素是否被指定注解修飾
getAnnotations:返回所有的注解
getDeclaredAnnotation:返回本元素的指定注解
getDeclaredAnnotations:返回本元素的所有注解,不包含父類繼承而來的
二、SOFARPC 源碼解析
2.1、注解說明
以com.alipay.sofa.runtime.api.annotation.SofaReference?為例子(SofaService 類似),源碼如下:
@SofaReference?生命周期為 RetentionPolicy.RUNTIME,永久保存,反射獲取;
注解作用目標(biāo) ElementType.FIELD,ElementType.METHOD,允許作用在方法和屬性字段上;
RPC 綁定方式 JVM、BOLT、REST 三種;默認(rèn)服務(wù)綁定關(guān)系為 JVM 方式;
2.2、服務(wù)發(fā)布與引用解析
通過?ServiceAnnotationBeanPostProcesso?類中postProcessAfterInitialization、postProcessBeforeInitialization 分別進(jìn)行服務(wù)的發(fā)布和引用:
獲取 SofaService.class、SofaReference.class 指定注解
獲取 SOFA 引用類型,默認(rèn) void,獲取引用uniqueId
2.2.1、總體流程
服務(wù)發(fā)布和引用整體流程圖,主要包含:注解解析、組件生成、組件注冊。
2.2.2、服務(wù)發(fā)布
目標(biāo):@SofaService將類注冊到 SOFA Context 中。發(fā)布到 SofaRuntimeContext 的過程其實(shí)就是把服務(wù)組件對象塞到?ConcurrentMap<ComponentName, ComponentInfo> registry對象中,通過 registry 進(jìn)行查找。
步驟:
(1)遍歷 SOFA 綁定關(guān)系, handleSofaServiceBinding 方法進(jìn)行不同類型 RPC Binding。
(2)生成 ServiceComponent 服務(wù)組件。
(3)調(diào)用 ServiceComponent 的register、resolve、activate方法,逐一調(diào)用對應(yīng) BindingAdapter 對外暴露服務(wù)。
(4)不同 BindingAdapter的 outBinding 服務(wù)處理策略不同。JvmBindingAdapter 返回空,不需暴露給外部,直接通過 registry 對象查找來調(diào)用;?RPC BindingAdapter 將服務(wù)信息推送注冊中心 Config。
將 ServiceComponent 注冊到 sofa 的上下文sofaRuntimeContext 中。
2.2.3、服務(wù)引用
目標(biāo):@SofaReference將 SOFA Context 中服務(wù)注冊成 Spring 中bean。通過?ReferenceRegisterHelper.registerReference()?方法從上下文中g(shù)et代理對象,?
registerReference() 內(nèi)部操作:
(1)注解 jvmFirst()?為 true 時,自動添加本地 JVM binding,避免跨機(jī)調(diào)用。
(2)生成 ReferenceComponent 服務(wù)組件對象。
(3)與 ServiceComponent 處理方式類似,ReferenceComponent 也會添加到ConcurrentMap<ComponentName, ComponentInfo> registry對象中,分別執(zhí)行組件的register、resolve、activate 三個方法。其中 register、resolve 方法主要是改變組件生命周期,代理對象生成就是在 activate 方法中完成。
(4)ReferenceComponent 組件通過不同類型 binding 生成不同類型的代理對象。只有一個binding,使用當(dāng)前;多個 binding,優(yōu)先用 jvm binding 來生成本地調(diào)用的代理對象,本地不存在,用遠(yuǎn)程代理對象。
(5)JvmBindingAdapter 的 inBinding 方法,直接借助于動態(tài)代理技術(shù)進(jìn)行生成代理對象,對于 RpcBindingAdapter 的 inBinding,在構(gòu)造的過程存在向注冊中心訂閱的邏輯。
?4、總結(jié)
1、XML :配置簡潔,但編碼多
Annotation:@SofaService 省去<sofa:service>?聲明,bean 定義必須有
SOFA: 注冊BeanPostProcessor處理@SofaService?和?@SofaReference注解。發(fā)布引用對象屬于當(dāng)前 bean 實(shí)例變量, xml 方式發(fā)布和引用, Bean 生命周期 InitializingBean#afterPropertiesSet?方法進(jìn)行擴(kuò)展。工程中注解掃描是對所有 bean 操作,只能通過實(shí)現(xiàn) spring 的 beanpostprocessor 這個接口,另外有些屬性可能在發(fā)布時需要用到。
注解服務(wù)發(fā)布和引用,分別基于 Bean 生命周期 BeanPostProcessor#postProcessAfterInitialization、#postProcessBeforeInitialization方法擴(kuò)展。
發(fā)布引用方式:
? ??XML:集中式元數(shù)據(jù),不綁源代碼
? ??注解:分散式的元數(shù)據(jù),綁定源代碼。
參考文檔
Java annotation:
https://en.wikipedia.org/wiki/Java_annotation
SOFASTACK 服務(wù)發(fā)布/服務(wù)引用: