2

你该学会自己写 Java 注解了

 1 year ago
source link: https://www.51cto.com/article/722512.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

你该学会自己写 Java 注解了

作者:雷小帅 2022-11-11 08:31:39
相信大家也发现了,注解类定义好之后并不能立马生效,比如在一个类上添加注解@ServiceReference,我们期望服务启动后这个类能自动暴露自己,但是你会发现什么也没发生,这是为什么呢?

​我们通过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与文档相关的,没有其他业务含义,这里不再赘述。

一个完整的注解类详细代码如下:


@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)类上使用,在其他地方使用是非法的,会编译失败。

完整的代码如下:

// 元注解,Spring 原生注解
@Component
// 约束注解使用范围
@Target({ElementType.TYPE})
// 注解保留策略
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceExpose {
}

在前面搭建环境时创建了一个maven​工程,我们继续在工程中创建一个 package并取名为:annotation,刚才写完的两个注解类代码放进去。

目前框架的代码结构如下:

├── easy-rpc-spring-boot-starter
│   ├── pom.xml
│   ├── src
│   │   └── main
│   │       ├── java
│   │       │   └── com
│   │       │       └── leixiaoshuai
│   │       │           └── easyrpc
│   │       │               ├── annotation
│   │       │               │   ├── ServiceExpose.java
│   │       │               │   └── ServiceReference.java
│   │       └── resources

前面带领大家写完了框架中核心的两个注解类:@ServiceReference​ 和 @ServiceExpose​,客户端常使用@ServiceReference​引用服务端;服务端常使用@ServiceExpose暴露自身的服务便于客户端发现和使用。

定义注解的步骤非常简单:

  • 使用@interface声明注解类
  • 注解类前面添加元注解,如:@Target、@Retention等

相信大家也发现了,注解类定义好之后并不能立马生效,比如在一个类上添加注解@ServiceReference,我们期望服务启动后这个类能自动暴露自己,但是你会发现什么也没发生,这是为什么呢?

其实注解仅仅只是一种标记的手段,自身并无业务逻辑,如果你希望注解实现预期效果,需要自己去写一段驱动代码,代码中可以通过反射方式扫描所有添加了注解的地方,然后执行对应的逻辑,至于什么时候执行这段驱动代码,需要结合注解的保留策略,一般是编译或者运行中执行。

明白了这个道理之后,要想使@ServiceReference​ 和 @ServiceExpose这两个注解实现对应的功能,需要分别写一段驱动程序,这段代码在后续的章节会详细介绍,我们接着往下看。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK