接上一小节,我们通过 Hello World 这个例子引入了RPC框架,知道了客户端要想调用服务端需要靠两个注解来实现,下面我们一起来实现这两个注解。
注解相关的实现代码是 RPC 框架的核心代码,后面写完后可以打包成一个 jar 包作为框架供业务代码中使用,这样我们前面写的客户端和服务端 demo 就可以正常工作了。
好了,铺垫这么多,我们真正开始写 RPC 框架代码了。RPC 框架计划提供两个注解:
- @ServiceReference
- @ServiceExpose
@ServiceReference
@ServiceReference 注解用来引用服务端提供的服务,客户端启动后可以自动注入对应的 bean,就像调用本地方法一样调用远程服务的方法。
首先,我们来定义一个注解类,interface 关键字用来声明接口,前面加一个 @ 就可以用来定义注解类,如上文约定客户端侧注解名为:ServiceReference。
public @interface ServiceReference {
}
注解类还需要增加一些配置项,一般用几个元注解进行修饰:
@Target 表示我们定义的这个注解使用的范围,ElementType 是枚举类,有很多枚举值,这里我们只用到 ElementType.FIELD。其业务含义是:当前这个自定义注解只能在类的成员变量上使用。
@Retention 表示注解的保留策略。RetentionPolicy.RUNTIME 的意思是希望注解能一直保留到运行期。为什么要保留到运行期呢?因为我们希望在运行期通过这个注解自动注入依赖。如果取值为 RetentionPolicy.SOURCE 则表示仅保存在源码中,在代码编译后就会丢掉这个注解的信息。
@Documented 是与文档相关的元注解,没有其他业务含义,这里不再赘述。
一个完整的 @ServiceReference 注解类详细代码如下:
@Target({ElementType.FIELD})
// 注解保留策略
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceReference {
}
注解已经定义好了,代码非常简单,使用起来也比较简单,在类的成员变量上加一个这样的注解即可:
class A {
@ServiceReference
private XxxService xxxService;
……省略
}
可能有小伙伴要问了,为什么加一个简单的注解就能将远程服务的依赖注入进来?这其实是框架背后的功劳,服务启动后框架会自动扫描相关注解,根据不同的注解执行对应的初始化动作。至于 @ServiceReference 的具体初始化逻辑,我们下一个小节再详细展开讲。
下面接着看另外一个核心注解。
@ServiceExpose
@ServiceExpose 注解用于服务端暴露自己的服务接口(或方法),进而可被客户端发现和调用。
与 @ServiceReference 类似,先定义一个注解,取名叫做 ServiceExpose。
public @interface ServiceExpose {
}
与 @ServiceReference 稍微有点不同的是,我们为它多增加了一个元注解 @Component,并且 @Target 的取值也不同。
@Component 是 Spring 原生的注解。Spring 启动后会扫描此注解并将其初始化为一个 bean,这是为了配合 @ServiceExpose 注解的使用,具体逻辑后面的章节会详细介绍。
@Target 用于约束注解的使用范围。ElementType.TYPE 表示当前这个注解仅可在类(class)、接口(interface)、枚举(enum)上使用,在其他地方使用是非法的,会导致编译失败。
@ServiceExpose 注解的完整代码如下:
// 元注解,Spring 原生注解
@Component
// 约束注解使用范围
@Target({ElementType.TYPE})
// 注解保留策略
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceExpose {
}
代码结构
在前面搭建环境时,我们创建了一个 Maven 工程。现在,我们继续在工程中创建一个名为 annotation 的包,并把刚才写完的两个注解类代码放进去。
目前框架的代码结构如下:
├── easy-rpc-spring-boot-starter
│ ├── pom.xml
│ ├── src
│ │ └── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── leixiaoshuai
│ │ │ └── easyrpc
│ │ │ ├── annotation
│ │ │ │ ├── ServiceExpose.java
│ │ │ │ └── ServiceReference.java
│ │ └── resources
小结
前面带领大家写完了 RPC 框架中两个核心的 Java 自定义注解类:@ServiceReference 和 @ServiceExpose。客户端通常使用 @ServiceReference 来引用远程服务;服务端则使用 @ServiceExpose 来暴露自身的服务,便于客户端发现和使用。
定义自定义注解的步骤非常简单:
- 使用
@interface 关键字声明注解类。
- 在注解类前面添加所需的元注解,如
@Target、@Retention 等。
相信大家也发现了,仅仅定义好注解类之后,它并不能立即生效。例如,在一个类上添加 @ServiceReference 注解,我们期望服务启动后这个类能自动暴露自己,但实际上什么也不会发生。这是为什么呢?
其实,注解本身只是一种标记(或称为元数据),它自身并不包含任何业务逻辑。如果你希望注解能实现预期的效果,就必须自己编写一段“驱动”代码。在这段代码中,你可以通过反射等方式扫描所有添加了该注解的地方,然后执行对应的处理逻辑。至于什么时候执行这段驱动代码,则需要结合注解的保留策略(@Retention)来决定,通常是在编译期或运行期执行。
明白了这个原理之后,要想使 @ServiceReference 和 @ServiceExpose 这两个注解实现对应的功能,就需要分别为它们编写驱动逻辑。这段代码是实现 开源实战 框架核心功能的关键,我们将在后续的章节中详细介绍,敬请期待后续更新。
希望这篇关于 Java 自定义注解的实践指南对你有所帮助。如果想深入探讨技术细节或查看更多实战教程,欢迎来 云栈社区 交流。