自定义的三个注解
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,所以要先将这个字段设置成可访问并通过反射将代理对象赋值到这个字段,这样当调用这个字段的方法时,就会通过代理对象远程调用到服务的方法了。
使用代理对象是为了客户端与具体服务解耦,让客户端感觉就像是在调用本地对象的方法一样,不需要知道服务的具体位置、通信协议等细节信息,它只需要知道服务接口以及如何获取代理对象即可。通过动态代理,在客户端创建一个实现了远程服务接口的代理对象。当客户端调用该代理对象的方法时,代理会将方法调用转换为网络请求发送给远程服务器,并处理响应返回给客户端
