自定義的三個注解
1、RpcReference
這個注解用于修飾類的某個字段,表示這個字段是遠程調用的引用
下面詳細解釋下這個字段的定義
@Document表示這個注解應該被javadoc文檔工具記錄,生成API文檔時使用了該注解的地方會被顯示出來
@Retention表示這個注解的聲明周期,它定義了這個注解可以在運行的時候通過反射讀取到,也就是可以通過反射來獲取到這個注解里的屬性
@Target表示這個注解只能被加在類的字段上
@Inherited表示這個注解具有繼承屬性,也就是說如果一個類的某個字段有這個注解,那么繼承這個類的子類也會繼承這個注解的行為
上面那個注解用在client端的調用服務,例如下面的HelloController這個類的HelloService函數,標識這是一個服務的遠程調用,框架在啟動時通過RpcScan注解自動將遠程服務注入為代理對象
然后在運行過程中通過反射拿到代理對象上面的RpcReference注解的屬性,獲取里面的version和group的值
2、RpcService
這個注解通常用在服務類上, 標注這個類實現了一個遠程接口,并希望將其作為實例暴露給遠程調用
下面詳細解釋下這個字段的定義
@Document表示這個注解應該被javadoc文檔工具記錄,生成API文檔時使用了該注解的地方會被顯示出來
@Retention表示這個注解的聲明周期,它定義了這個注解可以在運行的時候通過反射讀取到,也就是可以通過反射來獲取到這個注解里的屬性
@Target表示這個注解只能被加在類、接口、枚舉上面,不能加在某個類或者
@Inherited表示這個注解具有繼承屬性,也就是說如果一個類的某個字段有這個注解,那么繼承這個類的子類也會繼承這個注解的行為
如下面時他的一個調用實例
3、RpcScan
這個注解用于在Spring框架中啟用對RPC服務的自動掃描和注冊。通常在一個配置類上,告訴spring哪些包需要被掃描來找到帶有RpcReference和RpcService等注解的類,并將其注冊為spring bean或者RPC代理對象。
他結合了CustomScannerRegistrar,可以實現:
自動將遠程服務接口注入為代理對象(如?@RpcReference)
自動注冊本地服務實現為可被調用的 RPC 提供者(如?@RpcService)
下面逐行解析:
@Target里面定義了這個注解可以用在方法、接口和類上面,但實際操作中一般用在配置類上面(加了@Configuration注解用于配置Spring容器的類上面)
@Retention同上面,可以在運行時候通過反射拿到這個注解的屬性
@Import注解在配置中引入了CustomScannerRegistrar這個類,這個類是實現自定義掃描包的邏輯的,是一個實現了ImportBeanDefinitionRegistrar接口的類,用來動態注冊bean的定義
@Document表示加了這個注解的類會顯示在API文檔中
里面定義的屬性basePackage指定要掃描包的路徑
自定義掃描和服務發布的實現
CustomScannerRegistrar實現了spring的ImportBeanDefinitionRegistrar, ResourceLoaderAware接口
重寫了RegisterBeanDefinitions方法,spring在啟動時會調用這個方法,重寫后設置兩個自定義掃描器,掃描RpcService注解的類后,掃描Component注解的類保證其他基礎組件也能正常加載,掃描到指定類型后注冊成spring的bean
其實這里沒有對RpcService注解的類做特殊處理,可以自己加?
自定義一個類SpringBeanPostDefinition實現了BeanPostDefinition接口,這個接口用于對spring注冊后的bean做后處理。
重新實現這個接口的postProcessBeforeInitialization方法, spring會在每個bean初始化之前調用這個方法,對加了RpcService注解的類生成服務配置進行發布。發布服務就是將該bean的方法信息和netty通信的ip+端口地址拼成一個字符串,在zookeeper中作為一個持久化節點
重寫postProcessAfterInitialization這個接口,spring在每個bean初始化之后調用,通過反射獲取到這個類的所有字段,遍歷這些字段看是不是加了RpcReference字段,如果是的話那么這就是一個遠程服務的引用,然后去構建一個服務配置對象保存版本、分組等信息。
然后為這個字段的服務生成一個代理對象,默認情況下這些字段我們都會設置成private,所以要先將這個字段設置成可訪問并通過反射將代理對象賦值到這個字段,這樣當調用這個字段的方法時,就會通過代理對象遠程調用到服務的方法了。
使用代理對象是為了客戶端與具體服務解耦,讓客戶端感覺就像是在調用本地對象的方法一樣,不需要知道服務的具體位置、通信協議等細節信息,它只需要知道服務接口以及如何獲取代理對象即可。通過動態代理,在客戶端創建一個實現了遠程服務接口的代理對象。當客戶端調用該代理對象的方法時,代理會將方法調用轉換為網絡請求發送給遠程服務器,并處理響應返回給客戶端