SOFA

Scalable Open Financial Architecture

是蚂蚁金服自主研发的金融级分布式中间件,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。

1、前言

在 SOFABoot 环境下,SOFARPC 提供三种方式给开发人员发布和引用 RPC 服务:

  1. XML 方式(配置)

  2. Annotation 方式(注解)

  3. 编程 API 方式(动态)

编程 API 方式与Spring 的 ApplicationContextAware 类似。XML的方式依赖于在xml中引入 SOFA 命名空间,利用 Bean 的生命周期管理,进行 Bean 的注入。相比这两种方式,通过 Annotation 方式发布 JVM 服务更加灵活方便,只需要在实现类上加 @SofaService@SofaRefernce 注解即可进行服务的发布和引用。

本文针对 SOFARPC 在注解的支持和使用原理、源码两部分进行一一介绍。

2、注解原理解析

2.1、注解是什么

注解又称为元数据,可以对代码中添加信息,这是一种形式化的方法,可以在稍后的某个时刻非常方便地使用这些数据。这个时刻可能是编译时,也可能是运行时。

注解是 JDK1.5 版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。注解的本质就是一个继承了 Annotation 接口的接口。一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。

一般常用的注解可以分为三类:

  1. Java自带的标准注解,包括@Override(标明重写某个方法)、@Deprecated(标明某个类或方法过时)和@SuppressWarnings(标明要忽略的警告);

  2. 元注解,元注解是用于定义注解的注解;

  3. 自定义注解,可以根据自己的需求定义注解;

2.2、元注解

元注解是用于修饰注解的注解,通常用在注解的定义上。JAVA 中有以下几个元注解:

  1. @Target:注解的作用目标,也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的,有以下几种类型:

    ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上

  • ElementType.FIELD:允许作用在属性字段上

  • ElementType.METHOD:允许作用在方法上

  • ElementType.PARAMETER:允许作用在方法参数上

  • ElementType.CONSTRUCTOR:允许作用在构造器上

  • ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上

  • ElementType.ANNOTATION_TYPE:允许作用在注解上

  • ElementType.PACKAGE:允许作用在包上

@Retention:指定了被修饰的注解的生命周期,分以下三种类型:

  • RetentionPolicy.SOURCE:该注解只保留在一个源文件当中,当编译器将源文件编译成class文件时,它不会将源文件中定义的注解保留在class文件中。

  • RetentionPolicy.CLASS:该注解只保留在一个class文件当中,当加载class文件到内存时,虚拟机会将注解去掉,从而在程序中不能访问。

  • RetentionPolicy.RUNTIME:该注解在程序运行期间都会存在内存当中。此时,我们可以通过反射来获得定义在某个类上的所有注解。

@Documented:当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。

@Inherited:解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。

@Override 为例子:

当编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的方法签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。

@Override仅被编译器可知,编译器在对 java 文件进行编译成字节码的过程中,一旦检测到某个方法上被修饰了该注解,就会去匹对父类中是否具有一个同样方法签名的函数,否则不能通过编译。

2.3、注解解析方式

解析一个类或者方法的注解通常有两种形式,一种是编译期直接的扫描,一种是运行期反射。

2.3.1、编译器的扫描

指的是编译器在对 java 代码编译字节码的过程中会检测到某个类或者方法被一些注解修饰,这时它就会对于这些注解进行某些处理。典型的就是注解 @Override,一旦编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的方法签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。

这一种情况只适用于那些编译器已经熟知的注解类,比如 JDK 内置的几个注解,而你自定义的注解,编译器是不知道你这个注解的作用的。

2.3.1、运行期反射

首先对虚拟机的几个注解相关的属性表进行介绍,先大体了解注解在字节码文件中是如何存储的。虚拟机规范定义了一系列和注解相关的属性表,也就是说,无论是字段、方法或是类本身,如果被注解修饰了,就可以被写进字节码文件。属性表有以下几种:

  • RuntimeVisibleAnnotations:运行时可见的注解

  • RuntimeInVisibleAnnotations:运行时不可见的注解

  • RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解

  • RuntimeInVisibleParameterAnnotations:运行时不可见的方法参数注解

  • AnnotationDefault:注解类元素的默认值

java.lang.reflect.AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,程序通过反射获取了某个类的 AnnotatedElemen t对象之后,利用 Java 的反射机获取程序代码中的注解,然后根据预先设定的处理规则解析处理相关注解以达到主机本身设定的功能目标。

本质上来说,反射机制就是注解使用的核心,程序可以调用该对象的以下方法来访问 Annotation信息:

  • getAnnotation:返回指定的注解

  • isAnnotationPresent:判定当前元素是否被指定注解修饰

  • getAnnotations:返回所有的注解

  • getDeclaredAnnotation:返回本元素的指定注解

  • getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的

3、SOFARPC 源码解析

3.1、注解说明

com.alipay.sofa.runtime.api.annotation.SofaReference 为例子(SofaService 类似),源码如下:

基于元注解的含义,可以了解到:

  1. @SofaReference 生命周期为 RetentionPolicy.RUNTIME,代表永久保存,可以反射获取;

  2. 注解的作用目标 ElementType.FIELD,ElementType.METHOD,说明允许作用在方法和属性字段上;

  3. RPC 的绑定方式有 JVM、BOLT、REST 三种;

  4. 默认服务绑定关系为 JVM 方式;

3.2、服务发布与引用解析

通过 ServiceAnnotationBeanPostProcesso 类中postProcessAfterInitializationpostProcessBeforeInitialization 方法分别进行服务的发布和引用,其中通过反射对于注解的解析步骤大体相似,主要包含:

  1. 获取 SofaService.class、SofaReference.class 指定注解

  2. 获取的 SOFA 引用的类型,默认为 void

  3. 获取的 SOFA 引用的 uniqueId

3.2.1、总体流程

首先看下服务发布和引用整体流程图,主要包含注解解析、组件生成、组件注册几个步骤,后面对每个步骤进行更加详细的解释。

3.2.2、服务发布

@SofaService的目标是将一个类注册到 SOFA Context 中。发布到 SofaRuntimeContext 的过程其实就是把服务组件对象塞到 ConcurrentMap<ComponentName, ComponentInfo> registry 对象中,当有其他地方需要查找服务组件的时候,可以通过 registry 进行查找。主要包含以下几个步骤:

  1. 会遍历 SOFA 绑定关系,通过 handleSofaServiceBinding 方法进行不同类型的 RPC Binding。

  2. 生成 ServiceComponent 服务组件对象。

  3. 调用 ServiceComponent 服务组件的 register、resolve、activate方法,逐一调用对应 BindingAdapter 对外暴露服务。

  4. 不同的 BindingAdapter,对应的 outBinding 服务处理策略不一样。对于 JvmBindingAdapter 直接返回空,因为服务不需要暴露给外部,当其他模块调用该服务,直接通过 registry 对象进行查找。其他 RPC BindingAdapter 则将服务信息推送到注册中心 Confreg。

  5. 将 ServiceComponent 注册到 sofa 的上下文sofaRuntimeContext 中。

3.2.3、服务引用

@SofaReference的目标则是将 SOFA Context 中的一个服务注册成为 Spring 中的一个bean。基于以上注解解析基础上,主要通过 ReferenceRegisterHelper.registerReference() 方法从SOFA上下文中,拿到服务对应的代理对象。在 registerReference() 方法内部,主要包含以下操作:

  1. 当注解的 jvmFirst() 为 true 时,会为服务自动再添加一个本地 JVM 的 binding,这样能够做到优先本地调用,避免跨机调用。

  2. 生成 ReferenceComponent 服务组件对象。

  3. 与 ServiceComponent 处理方式类似,ReferenceComponent 也会添加到 ConcurrentMap<ComponentName, ComponentInfo> registry对象中,分别执行组件的register、resolve、activate 三个方法。其中 register、resolve 方法主要是改变组件的生命周期,代理对象的生成就是在 activate 方法中完成的。

  4. ReferenceComponent 组件通过不同类型的 binding 生成不同类型的代理对象。如果只有一个binding,使用当前 binding 生成代理对象。如果有多个 binding,优先使用 jvm binding 来生成本地调用的代理对象,若本地代理对象不存在,使用远程代理对象。

  5. 对于JvmBindingAdapter 的 inBinding 方法,直接借助于动态代理技术进行生成代理对象,对于 RpcBindingAdapter 的 inBinding,在构造的过程存在向注册中心订阅的逻辑。

4、总结

通过 XML 的方式去配置 SOFA 的 JVM 服务和引用非常简洁,但是多了一定的编码工作量。

因此,除了通过 XML 方式发布 JVM 服务和引用之外,SOFA 还提供了 Annotation 的方式来发布和引用 JVM 服务。@SofaService 注解省去了<sofa:service> 声明,但 bean 的定义还是必须要有的。

SOFA 实际上是注册了一个BeanPostProcessor 来处理@SofaService@SofaReference注解。需要发布引用的对象属于当前 bean 的实例变量,使用 xml 的方式进行服务发布和引用,可以直接通过 Bean 生命周期的 InitializingBean#afterPropertiesSet 方法进行扩展。在工程中注解扫描是一个对所有 bean 的操作,只能通过实现 spring 的 beanpostprocessor 这个接口,另外有些属性可能在发布时需要用到。

因此使用注解的方式进行服务发布和引用,分别基于 Bean 生命周期的 BeanPostProcessor#postProcessAfterInitialization#postProcessBeforeInitialization方法进行扩展。

对比服务的发布和引用的两种常用方式,XML 是一种集中式的元数据,与源代码无绑定,注解是一种分散式的元数据,与源代码紧绑定。SOFARPC 初始的版本,并不支持通过注解进行 RPC 服务的发布和引用,需要使用 XML 的方式进行配置。后来在开源 SOFARPC 版本中增加这个功能的注解支持,对服务发布和引用做了一个使用方式的补充,而对于 XML 与注解的优劣取舍,大家可以根据团队的规范和个人的评估进行相应的使用。

5、参考文档

  • Java annotation:

    https://en.wikipedia.org/wiki/Java_annotation

  • SOFASTACK 服务发布/服务引用:

    http://www.sofastack.tech/sofa-rpc/docs/Publish-And-Reference


相关链接

SOFA 文档: http://www.sofastack.tech/

SOFA: https://github.com/alipay

SOFARPC: https://github.com/alipay/sofa-rpc

SOFABolt: https://github.com/alipay/sofa-bolt

《剖析 | SOFARPC 框架》系列历史文章

  • 【剖析 | SOFARPC 框架】之总体设计与扩展机制

  • 【剖析 | SOFARPC 框架】系列之链路追踪剖析

  • 【剖析 | SOFARPC 框架】系列之连接管理与心跳剖析

  • 【剖析 | SOFARPC 框架】系列之 SOFARPC 同步异步实现剖析

  • 【剖析 | SOFARPC 框架】系列之 SOFARPC 线程模型剖析

  • 【剖析 | SOFARPC 框架】系列之 SOFARPC 单机故障剔除剖析

  • 【剖析 | SOFARPC 框架】系列之 SOFARPC 泛化调用实现剖析

  • 【剖析 | SOFARPC 框架】系列之 SOFARPC 数据透传剖析

  • 【剖析 | SOFARPC 框架】系列之 SOFARPC 优雅关闭剖析

  • 【剖析 | SOFARPC 框架】系列之 SOFARPC 路由实现剖析

长按关注,获取分布式架构干货

欢迎大家共同打造 SOFAStack https://github.com/alipay

【剖析 | SOFARPC 框架】系列之 SOFARPC 注解支持剖析相关推荐

  1. Python爬虫之Scrapy框架系列(16)——深入剖析request和response类

    目录: Request和Response类: 1. 深入剖析Request类: 利用request.meta传递参数 拓展一:FormRequest类 2. 深入剖析Response类: Reques ...

  2. Python爬虫之Scrapy框架系列(18)——深入剖析中间件及实战使用

    目录: 1.下载中间件: (1)终端获取下载中间件状态信息的命令: (2)下载中间件的API: (3)中间件的项目应用:通过添加中间件设置UA代理及IP代理 ①在middlewares.py中间件文件 ...

  3. java sofa rpc_【剖析 | SOFARPC 框架】

    Scalable Open Financial Architecture 是蚂蚁金服自主研发的金融级分布式中间件,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践. 本文为& ...

  4. Spring框架系列之AOP思想

    微信公众号:compassblog 欢迎关注.转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1.AOP概述 (1).什么是 AOP AOP 为 Aspect Oriented Progra ...

  5. SpringMVC 框架系列之组件概述与配置详解

    在上一篇文章 SpringMVC 框架系列之初识与入门实例 的实例中,我们已经知道,SpringMVC 框架是一个 web 层的框架,本篇文章就详细解释一下 SpringMVC 框架具体文件的配置以及 ...

  6. 热门框架系列 (二) -- SpringMvc的父子容器,SpringBoot是否有父子容器?

    @TOC# 热门框架系列 记录在程序走的每一步___auth:huf 从新的篇章开始;篇章阅读需要先关注; 因为笔者想参与技术文章的评选.;需要一定的粉丝量; 粉丝量达到一定数量.所有文章阅读限制将会 ...

  7. 【重温SSM框架系列】15 - SSM系列博文总结【SSM杀青篇】

    SSM总结 Spring部分 1 - Spring快速入门(配置文件及API详解) 2 - Spring配置数据源连接池(手动创建与配置) 3 - Spring注解开发(注解代替xml文件配置) 4 ...

  8. CYQ.Data 数据框架系列索引

    2019独角兽企业重金招聘Python工程师标准>>> 索引基础导航: 1:下载地址:http://www.cyqdata.com/download/article-detail-4 ...

  9. 数据库分库分表(sharding)系列(五) 一种支持自由规划无须数据迁移和修改路由代码的Sharding扩容方案...

    为什么80%的码农都做不了架构师?>>>    版权声明:本文由本人撰写并发表于2012年9月份的<程序员>杂志,原文题目<一种支持自由规划的Sharding扩容方 ...

  10. Android注解支持(Support Annotations)

    注解支持(Support Annotations) Android support library从19.1版本开始引入了一个新的注解库,它包含很多有用的元注解,你能用它们修饰你的代码,帮助你发现bu ...

最新文章

  1. python中函数的作用域_Python中的函数作用域
  2. c++设计模式之状态模式
  3. php提交raw_PHP中如何POST提交raw数据?
  4. 一道小时候经常玩的数字游戏
  5. 衡量 mysql性能状态 参数 详解
  6. excel自定义函数添加和使用方法
  7. 推荐一个完美的计算机科学的视频集
  8. CentOS7目录配置
  9. uygurqa输入法android,uygurqa输入法app
  10. 【知识图谱】08KBQA问答系统(python+fuseki+jena)
  11. Openwrt 构建Hello ipk
  12. 【Java代码之美】 -- Java11新特性解读
  13. ora11g 安装报错ins_emagent.mk
  14. zyt-Linux云计算
  15. 测试用例(微信发朋友圈/评论/点赞/搜索/购物车)
  16. SOCKET 实现NAT 穿越
  17. 提升工作效率的软件及网站(不断更新中)
  18. 产品运营:如何激活沉默用户
  19. JAVA MemCache 史无前例的详细讲解
  20. 多麦克风做拾音的波束_语音交互:先从麦克风阵列聊起

热门文章

  1. 前端开发负责人修炼指北
  2. 终于把tomcat给搞定了
  3. 在社会上闯荡必须要牢记的
  4. [照片]51cto众生相
  5. systemtap打点方法
  6. centos 网络自动连接_自动连接最优信号 腾讯云?云兔解决物联网络连接问题
  7. yuv420和yuv420p的区别
  8. cgroup学习(五)—— create new cgroup
  9. P-Called-Party-ID 头域的应用说明
  10. SylixOS arm64 异常向量表