我们通过Hello World这个例子引入了RPC 框架,知道了客户端要想调用服务端需要靠两个注解来实现,下面我们一起来实现这两个注解。
注解相关的实现代码是 RPC 框架的核心代码,后面写完后可以打包成一个 jar 包作为框架供业务代码中使用,这样我们前面写的客户端和服务端 demo 就可以正常工作了。
好了,铺垫这么多,我们真正开始写 RPC 框架代码了。RPC 框架计划提供两个注解:
- @ServiceReference
- @ServiceExpose
@ServiceReference
@ServiceReference注解用来引用服务端提供的服务,客户端启动后可以自动注入对应的bean,像调用本地方法一样调用远程服务的方法。
首先,我们来定义一个注解类,interface关键字用来声明接口,前面加一个@就可以用来定义注解类,如上面约定客户端侧注解名为:ServiceReference。
注解类还可以加很多配置项,一般用几个元注解进行修饰:
@Target表示我们定义的这个注解使用的范围,ElementType是枚举类有很多枚举值,这里我们只用到ElementType.FIELD,业务含义:当前这个自定义注解只能在类的成员变量上使用。
@Retention表示注解的保留策略,RetentionPolicy.RUNTIME的意思是希望注解能一直保留到运行期,那为什么要保留到运行期呢?因为我们希望在运行期通过这个注解自动注入依赖。如果取值为RetentionPolicy.SOURCE则表示仅保存在源码中,在代码编译后就会丢掉这个注解的信息。
@Documented与文档相关的,没有其他业务含义,这里不再赘述。
一个完整的注解类详细代码如下:
注解已经定义好了,代码非常简单,使用起来也比较简单,在类的成员变量上加一个这样的注解即可:
可能有小伙伴要问了,为什么加一个简单的注解就能将远程服务的依赖注入进来?这其实是框架背后的功劳,服务启动后会自动扫描框架的注解,根据不同的注解框架会有对应的初始化动作,至于@ServiceReference的初始化逻辑我们下一个小节再详细展开讲。
下面接着看另外一个注解。
@ServiceExpose
@ServiceExpose注解用于服务端暴露自己的服务接口(方法),进而可被客户端发现调用。
与@ServiceReference类似,定义一个注解,取名叫做:ServiceExpose。
与@ServiceReference稍微有点不同的是,多增加了一个元注解:@Component以及@Target取值不一样。
@Component是Spring原生的注解,Spring启动后会扫描注解并将其初始化一个bean,用于配合@ServiceExpose注解使用,具体逻辑后面章节会详细介绍。
@Target用于约束注解的使用范围,ElementType.TYPE表示当前这个注解仅可在类(class)、接口(interface)、枚举(enum)类上使用,在其他地方使用是非法的,会编译失败。
完整的代码如下:
代码结构
在前面搭建环境时创建了一个maven工程,我们继续在工程中创建一个 package并取名为:annotation,刚才写完的两个注解类代码放进去。
目前框架的代码结构如下:
小结
前面带领大家写完了框架中核心的两个注解类:@ServiceReference 和 @ServiceExpose,客户端常使用@ServiceReference引用服务端;服务端常使用@ServiceExpose暴露自身的服务便于客户端发现和使用。
定义注解的步骤非常简单:
- 使用@interface声明注解类
- 注解类前面添加元注解,如:@Target、@Retention等
相信大家也发现了,注解类定义好之后并不能立马生效,比如在一个类上添加注解@ServiceReference,我们期望服务启动后这个类能自动暴露自己,但是你会发现什么也没发生,这是为什么呢?
其实注解仅仅只是一种标记的手段,自身并无业务逻辑,如果你希望注解实现预期效果,需要自己去写一段驱动代码,代码中可以通过反射方式扫描所有添加了注解的地方,然后执行对应的逻辑,至于什么时候执行这段驱动代码,需要结合注解的保留策略,一般是编译或者运行中执行。
明白了这个道理之后,要想使@ServiceReference 和 @ServiceExpose这两个注解实现对应的功能,需要分别写一段驱动程序,这段代码在后续的章节会详细介绍,我们接着往下看。