dubbo有哪些负载均衡算法?怎么实现的负载均衡算法?dubbo有几层

常见的有LVS、Nginx和HAProxy,者者介绍分别如下:

dubbo超时时间设置 dubbo超时时间设置优先级dubbo超时时间设置 dubbo超时时间设置优先级


dubbo超时时间设置 dubbo超时时间设置优先级


LVS:使用集群技术和Linux作系统实现一个高性能、高可用的,它具有很好的可伸缩性(Scalability)、可靠性(Reliability)和可管理性(Maability),感谢章文嵩博士为我们提供如此强大实用的开源软件。

LVS的特点是:

1、抗负载能力强、是工作在网络4层之上仅作分发之用,没有流量的产生,这个特点也决定了它在负载均衡软件里的性能最强的;

2、配置性比较低,这是一个缺点也是一个优点,因为没有可太多配置的东西,所以并不需要太多接触,大大减少了人为出错的几率;

3、工作稳定,自身有完整的双机热备方案;

4、无流量,保证了均衡器IO的性能不会收到大流量的影响;

5、应用范围比较广,可以对所有应用做负载均衡;

6、软件本身不支持正则处理,不能做动静分离。

Nginx的特点是:

1、工作在网络的7层之上,可以针对应用做一些分流的策略;

2、Nginx对网络的依赖非常小;

3、Nginx安装和配置比较简单,测试起来比较方便;

4、可以承担高的负载压力且稳定,一般能支撑超过几万次的并发量;

5、Nginx可以通过端口检测到内部的故障,比如根据处理网页返回的状态码、超时等等;

6、Nginx仅能支持和Email;

HAProxy的特点是:

1、HAProxy是支持虚拟主机的;

2、能够补充Nginx的一些缺点比如Session的保持,Cookie的等工作;

3、支持检测后端的出问题的检测会有很好的帮助;

4、它跟LVS一样,本身仅仅就只是一款负载均衡软件;

5、HAProxy可以对Mysql读进行负载均衡,对后端的MySQL进行检测和负载均衡,不过在后端的MySQL sles数量超过10台时性能不如L二、TCC补偿性事务解决方案VS;

6、HAProxy的算法多;主要是将Dubbo中的应用信息、注册信息、协议信息等设置到变量中。有个方法值得注意的是 isDelay 方法当返回true时,表示无需延迟导出;返回false时,表示需要延迟导出。

dubbo使用zookeeper连接,zookeeper宕机后怎么处理

InitializingBean

1,配置文件同步2,主从切换3,分布式队列4,分布式锁5,其他在以前的文章里面有写过使用zookeeper原生的api,zk变化,那么本篇我们就来看下,如何使用curator来完成,代码如下:packagecom.qin.curator.zk;importjax.sound.midi.Patch;importorg.apache.curator.RetryPolicy;importorg.apache.curator.framework.CuratorFramework;importorg.apache.curator.framework.CuratorFrameworkFactory;importorg.apache.curator.framework.CuratorFrameworkFactory.Builder;importorg.apache.curator.framework.api.CuratorWatcher;importorg.apache.curator.framework.recipes.cache.NodeCache;importorg.apache.curator.framework.recipes.cache.NodeCacheListener;importorg.apache.curator.framework.recipes.cache.PathChildrenCache;importorg.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;importorg.apache.curator.framework.recipes.cache.PathChildrenCacheListener;importorg.apache.curator.retry.ExponentialBackoffRetry;importorg.apache.curator.utils.ZKPaths;importorg.apache.zookeeper.WatchedEvent;/使用curatorzookeeper@authorqindongliang/publicclassCuratorWatch{staticCuratorFrameworkzkclient=null;staticStringnameSpace="php";static{Stringzkhost="192.168.46.22:2181";//zk的hostRetryPolicyrp=newExponentialBackoffRetry(1000,3);//重试机制Builderbuilder=CuratorFrameworkFactory.builder().connectString(zkhost).connectionTimeoutMs(5000).sessionTimeoutMs(5000).retryPolicy(rp);builder.namespace(nameSpace);CuratorFrameworkzclient=builder.build();zkclient=zclient;zkclient.start();//放在这前面执行zkclient.newNamespaceAwareEnsurePath(nameSpace);}publicstaticvoidmain(String[]args)throwsException{watch();Thread.sleep(Long.MAX_VALUE);}/变化/publicstaticvoidwatch()throwsException{PathChildrenCachecache=newPathChildrenCache(zkclient,"/zk",false);cache.start();System.out.println("开始/zk..");PathChildrenCacheListenerplis=newPathChildrenCacheListener(){@OverridepublicvoidchildEvent(CuratorFrameworkclient,PathChildrenCacheEven)throwsException{switch(nt.getType()){caseCHILD_ADDED:{System.out.println("Nodeadded:"+ZKPaths.getNodeFromPath(nt.getData().getPath()));break;}caseCHILD_UPDATED:{System.out.println("Nodechanged:"+ZKPaths.getNodeFromPath(nt.getData().getPath()));break;}caseCHILD_REMOVED:{System.out.println("Noderemoved:"+ZKPaths.getNodeFromPath(nt.getData().getPath()));break;}}}};//注册cache.getListenable().addListener(plis);}}运行后的控制台打印:18:33:07.722[main]INFOo.a.c.f.imps.CuratorFrameworkImpl-Starting18:33:07.727[main]DEBUGo.a.curator.CuratorZookeeper-Starting18:33:07.727[main]DEBUGorg.apache.curator.ConnectionState-Starting18:33:07.727[main]DEBUGorg.apache.curator.ConnectionState-reset18:33:07.734[main]INFOorg.apache.zookeeper.ZooKeeper-environment:zookeeper.version=3.4.6-1569965,builton02/20/201409:09GMT18:33:07.734[main]INFOorg.apache.zookeeper.ZooKeeper-environment:host.name=QINDONGLIANG.dhgatecn.msf18:33:07.734[main]INFOorg.apache.zookeeper.ZooKeeper-environment:ja.version=1.7.0_0418:33:07.734[main]INFOorg.apache.zookeeper.ZooKeeper-environment:ja.vendor=OracleCorporation18:33:07.734[main]INFOorg.apache.zookeeper.ZooKeeper-environment:ja.home=D:Jajdk1.7.0_04jre18:33:07.734[main]INFOorg.apache.zookeeper.ZooKeeper-environment:ja.class.path=D:eclipseworkspace2ywopzkbin;D:eclipseworkspace2ywopzklibcurator-client-2.6.0.jar;D:eclipseworkspace2ywopzklibcurator-examples-2.6.0.jar;D:eclipseworkspace2ywopzklibcurator-framework-2.6.0.jar;D:eclipseworkspace2ywopzklibcurator-recipes-2.6.0.jar;D:eclipseworkspace2ywopzklibcurator-test-2.6.0.jar;D:eclipseworkspace2ywopzklibcurator-x-discovery-2.6.0.jar;D:eclipseworkspace2ywopzklibcurator-x-discovery-server-2.6.0.jar;D:eclipseworkspace2ywopzklibcurator-x-rpc-2.6.0.jar;D:eclipseworkspace2ywopzkliblog4j-1.2.15.jar;D:eclipseworkspace2ywopzklibzookeeper-3.4.5.jar;D:eclipseworkspace2ywopzklibcommons-io-2.1.jar

1、配置文件同步

2、主从切换

3、分布式队列

4、分布式锁

Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录树方式的数据存储,但是 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和你存储的数据的状态变化。

通过这些数据状态的变化,从而可以达到基于数据的集群管理,后面将会详细介绍 Zookeeper 能够解决的一些典型问题,这里先介绍一下,Z// 查找常用的日志框架ookeeper 的作接口和简单使用示例。 常用接口列表

客户端要连接 Zookeeper 可以通过创建 org.apache.zookeeper. ZooKeeper 的一个实例对象,然后调用这个类提供的接口来和交互。

前面说了 ZooKeeper 主要是用来维护和一个目录树中存储的数据的状态,所有我们能够作 ZooKeeper 的也和作目录树大体一样,如创建一个目录,给某个目录设置数据,获取某个目录的所有子目录,给某个目录设置权限和这个目录的状态变化。

如何获取dubbo上注册的referencebean

Dubbo的rpc报文分为header和body两部分。我们这边只需要关注body部分。构造逻辑如下

开源的dubbo已支持4种组件作为注册中心,我们部门使用的zookeeper做为注册中心,由于就瓶颈来说不会出现在注册中心,风险较低,未做特别的研究或比较。

zookeeper,集群中部署奇数个,由于zookeeper挂掉一半的机器集群就不可用,所以部署4台和3台的集群都是在挂掉2台后集群不可用

redis

multicast,广播受到网络结构的影响,一般本地不想搭注册中心的话使用这种调用

dubbo简易注册中心首先启动Nacos,按照上篇文章的步骤,启动Nacos服务和项目,访问Nacos的web页面。确保项目中的服务都注册到注册中心当中了。在application.yml同级目录下添加bootstrap.yml,在Spring boot项目中bootstrap.yml会比application.yml优先初始化,所以我们需要在bootstrap.yml中引入Nacos指定的配置文件即可(上篇文章中已经把Nacos作为配置中心的配置写入了application.yml,现在只需要把它从applicaiton.yml中剪切出来即可, 其中的spring:application:name会作为Nacos中新增配置时的Data ID,需要留意 ),再新增属性gorup进行分组测试,如下图

对于zookeeper客户端,dubbo在2.2.0之后默认使用zkclient,2.3.0之后提供可选配置Curator,提到这个点的原因主要是因为zkclient发现一些问题:①在修改时间后zk会抛出日志错误之类的异常然后容器(我们使用resin)挂掉了,也不能确定就是zk的问题,接入dubbo之前无该问题②dubbo使用zkclient不传入连接zookeeper等待超时时间,使用默认的Integer.MAX_VALUE,这样在zookeeper连不上的情况下不报错也无法启动;目前我们准备寻找其他解决方案,比如使用curator试下,还没正式投入。

如何开启Dubbo框架内部的日志

在dubbo框架内所有的日志输出都是通过 LoggerFactory这个静态工厂类来获得Logger的对象实体,并且抽离了一个LoggerAdapter用于对接第三方日志框架,所以就有了JDKLoggerAdapter,Log4jLoggerAdapter,SLF4JLoggerAdapter等一些实现子类,分别对接了不同Log第三方实现。既然dubbo能够支持这么多log实现,那么这些实现在dubbo中优先级是在呢么样的呢?这里的优先级是只未配置指定的logger提供方的情况下,由dubbo框架自己选择。优先级如下:

上面说的有和没有是指你的项目classpath下面有没有对应的jar包,如果有则表示支持对应的日志实现。下面粘贴出Dubbo选择日志提供方的代码:

[ja] view plaincopy

static GenericImplFilter中有很多其他逻辑,比如泛化调用使用的序列化协议,正常接口走泛化调用的模式,我们只需要设置attachment的那部分。{

String logger = System.getProperty("dubbo.application.logger");

if ("slf4j".equals(logger)) {

setLoggerAdapter(new Slf4jLoggerAdapter());

} else if ("jcl".equals(logger)) {

setLoggerAdapter(new JclLoggerAdapter());

} else if ("log4j".equals(logger)) {

setLoggerAdapter(new Log4jLoggerAdapter());

} else if ("jdk".equals(logger)) {

} else {

try {

setLoggerAdapter(new Log4jLoggerAdapter());

} catch (Throwable e1) {

try {

setLoggerAdapter(new Slf4jLoggerAdapter());

} catch (Throwable e2) {

try {

setLoggerAdapter(new JclLoggerAdapter());

} catch (Throwable e3) {

}}

}}

[html] view plaincopy

value="[%d{MMdd HH:mm:ss SSS} %-5p] [%t] %c{3} - %m%n" />

Dubbo的分布式事务怎么解决

setLoggerAdapter(new JdkLoggerAdapter());

目前比较多的解决方案有几个:

GenericFilter就是用来做这件事情。

一、结合MQ消息中间件实现的可靠消息最终一致性

三、努力通知型方案

种方案:可靠消息最终一致性,需要业务系统结合MQ消息中间件实现,在实现过程中需要保证消息的成功发送及成功消费。即需要通过业务系统控制MQ的消息状态

第二种方案:TCC补偿性,分为三个阶段TRYING-CONFIRMING-CANCELING。每个阶段做不同的处理。

TRYING阶段主要是对业务系统进行检测及资源预留

CONFIRMING阶段是做业务提交,通过TRYING阶段执行成功后,再执行该阶段。默认如果TRYING阶段执行成功,CONFIRMING就一定能成功。

CANCELING阶段是回对业务做回滚,在TRYING阶段中,如果存在分支事务TRYING失败,则需要调用CANCELING将已预留的资源进行释放。

第三种方案:努力通知xing型,这种方案主要用在与第三方系统通讯时,比如:调用微信或支付宝支付后的支付结果通知。这种方案也是结合MQ进行实现,例如:通过MQ发送请求,设置通知次数。达到通知次数后即不再通知。

微服务初体验(二):使用Nacos作为配置中心并集成Dubbo

在使用上,不管哪种配置方式,我们都需要配置generic=true

接着打开Nacos的服务的web页面,打开配置管理->配置列表,点击右侧新增按钮,进行新增。

Data ID: bootstrap.yml配置文件中spring:application:name对应的名称 ;

Group:指定分组(便于不同环境下的项目配置管理,因为笔者这里属于测试,所以填写的是和上文中的配置文件中group对应的test一致);

描述:针对于该配置的描述二、你的项目当前使用的是非Log4j来提供日志输出 这种情况,默认是看不到dubbo的日志输出的,除非出现异常,被你当前系统的日志框架拦截住了。我这里就拿当前使用最多的日志框架logback来做示例。我们知道logback天生和slf4j进行了集成,所以要在项目里面使用logback,调用slf4j暴露的接口就可以。所以要把dubbo的日志输出切换到logback,也就变成了切换到slf4j了。一下列举出几种情况来切换。;

配置格式:配置文件的格式,要和Data ID中的后缀格式一致(这里笔者用的是yml,那么下面就选择yaml,注意该位置也可以选择properties,但是必须和上面bootstrap.yml文件中的file-extension的值相匹配);

配置内容:具体的配置内容(这里笔者将项目中的application.yml中的配置全部拷贝至其中);

测试启动consumer服务,在application.yml中为空的时候,项目启动端口还是如Nacos配置中的9011,说明项目依赖Nacos的配置中心成功,其他服务如法炮制即可:

新增一个测试Controller,然后加上@RefreshScope注解,表明该Controller中的配置数据为自动刷新 。

编辑Nacos中的配置文件consumer新增相关参数type: test,访问Controller,返回test。效果如下图:

将Nacos中consumer.yml文件的type: test修改为type: prod,在不重启项目的情况下重新访问对应的controller,效果如下图:

因为Dubbo是属于各个服务之间都要公用的依赖,所以将其引入cloud-common当中,详细的版本可以去 mvnreitory 搜索合适自己项目的

引入依赖后需要编写消费者服务中的配置文件,将Dubbo服务注册至Nacos,新增如下内容,其中subscribed-servs指的是生产者服务,prot:-1指的是端口随机,registry:address:指的是Dubbo对应的注册中心那这里就应该设置为Nacos

接下来新增接口服务,项目类型为Men项目,在项目中新增一个接口。并在cloud-provider(生产者)和cloud-consumer(消费者)pom.xml文件中都引入该模块

在生产者实际服务中实现该接口对应的方法

在服务消费者的Controller中引入该Serv,并在该Serv上加入@Reference注解,注意在引入jar包的时候选择带有Dubbo的,不要使用Jdk原生的

dubbo version: 2.6.0, current host: 192.168.245.1

}上面这段静态块是在LoggerFactory里面,说明只要LoggerFactory类一加载就会去选择对应的日志提供方。大家可能会发现对日志的提供方其实是可以通过配置来指定的,因为静态块一开始是从当前jvm环境中获取dubbo.application.logger,这个参数是同ja -Ddubbo.application.logger=xxxx去指定的,如果是放在容器里面,就需要配置在容器启动的jvm参数里面。上面介绍了dubbo中日志相关的实现。下面讲讲在项目总怎么来让dubbo能够在项目里面输出日志。

在Dubbo的配置文件中,可以通过 dubbo.protocol.host 属性来设置当前主机的IP地址。可以在 dubbo.properties 或者 dubbo.xml 中进行配置。具体作步骤如下:

在配置文件中加入如下配置:

dubbo.protocol.host=your_ip_address

将 your_ip_address 替换为当前主机的IP地址。

保存配置文件并重新启动Dubbo服务。

这样就可以将Dubbo服务绑定到指定的IP地址上,供客户端调用。

dubbo泛化调用使用及原理解析

dubbo.protocol.threadpool = cached

通常我们想调用别人的dubbo服务时,我们需要在项目中引入对应的jar包。而泛化调用的作用是,我们无需依赖相关jar包,也能调用到该服务。

这个特性一般使用在类项目中,在业务开发中基本不会使用。

设我现在要调用下面的接口服务

在xml文件做以下配置

然后注入使用

在两种调用方式中,我们都需要使用被调用接口的字符串参数生成GenericServ,通过GenericServ的$invoke间接调用目标接口的接口。

可以看到泛化调用的一个复杂性在于$invoke的第三个参数的组装,下面介绍几种复杂入参的调用方式

首先丰富提供者接口

与入参相似,虽然$invoke的返回定义为Object,实际上针对不同类型有不同的返回。

泛化调用和直接调用在消费者者端,在 使用上的区别 是,我们调用服务时使用的接口为GenericServ,方法为$invoker。在 底层的区别 是,消费者端发出的rpc报文发生了变化。

设置generic=true后,RefereceConfig的inteceClass会被强制设置为GenericServ

这也使得我们的

生成的是GenericServ的只是我们使用方式上的变化,更为核心的是,底层发送的rpc报文发生了什么变化。

那么我们通过直接调用与泛化调用ByeServ的bye方法在报文上有啥区别呢?

我一开始以为报文中的path是GenerServ,其实并没有,path就是我们调用的目标方法。

path来源???todo

而报文中的方法名,方法参数类型以及具体参数,还是按照GenerServ的$invoke方法入参传递的。

这么个二合一的报文,发送到提供者那边,它估计也会很懵逼,我应该怎么执行?

所以针对泛化调用报文还会把generic=true放在attchment中传递过去

具体逻辑在GenericImplFilter中。

知道消费者端报文发生了什么变化,那么接下来就去看提供者端如何处理这个改造后的报文。

总结一下ReferenceConfig中inteceClass和inteceName的区别?(这道面试题好像不错)

inteceClass用于指定生成的接口

inteceName用于指定发送rpc报文中的path(告诉服务端我要调用那个服务)

消费者泛化调用的rpc报文传递到提供者还不能直接使用,虽然path是对的,但是实际的方法名,参数类型,参数要从rpc报文的参数中提取出来。

在提供者这边,针对泛化调用的逻辑全部封装到了GenericFilter,解耦的非常好。

注意第4个条件,一开始很疑惑,后来发现rpc报文中的path是目标接口的,这边invoker.getIntece()返回的肯定就是实际接口了

这边有个疑问,为什么这边还要再次反序列化一次,netty不是有decoder么??

嗯,你别忘了,针对一个POJO你传过来是一个Map,从Map转换为POJO需要这边进一步处理。

这边需要注意一下!!针对接口的泛化调用,抛出的异常都会经过GenericException包装一下。

从功能上来看,泛化调用提供了在没有接口依赖情况下进行的解决方案,丰富框架的使用场景。

从设计上来看,泛化调用的功能还是通过扩展的方式实现的,侵入性不强,值得学习借鉴。

dubbo 中使用 ThreadLocal 隐患

$invoke的三个参数分别为,方法名,方法参数类型数组,方法参数数组。

在 Dubbo 中使用 ThreadLocal ,如果采用默认的设置,每次 Dubbo 调用结束,Dubbo 处理响应线程并不会被销毁, 而是归还到线程池中。而从 ThreadLocal 源码可以看出,每次我们设置的值其实会存在位于 Thread 中 ThreadLocalMap 变量中。

这就导致,下次如果 Dubbo 处理响应恰好继续使用到这个线程,该线程就能调用到上次响应中设置在 ThreadLocal 设置的值。这就引起内存泄露,可能还会导致业务上异常。其实并不止在 Dubbo 中,该案例还会发生在 web项目中,只要相关使用线程池的,都有可能发生。

dubbo 默认采用单一长连接加线程池方式处理调用。

默认采取 Dispatcher=all 的分发策略,所有消息都派发到线程池,包括请求,响应,连接,断开,心跳等。

线程池在缺省配置为固定大小线程池,启动时建立线程,不关闭,一直持有。

使用 Threadlocal,我们需要注意几点:

dubbo.protocol.threads = 5000

调整线程池类型配置是

调整处理编写消费者服务中测试Dubbo调用的接口,进行测试,测试结果如下图:方式配置是

dubbo.protocol.dispatcher = message

或者

Dubbo与Spring的融合机制

调整线程池大小配置是

我们都知道Dubbo可以与Spring进行融合,那是怎么进行融合的呢?

指定Spring扫描路径

我先介绍一下文档中是如何实现与Spring融合的,然后再从底层分析一下。

Serv注解暴露服务

增加应用配置信息

Reference注解引用服务

增加应用配置信息

调用服务

上面是整体融合Spring的案例,接下来分析 Serv 注解和 Reference 注解是怎么实现的。

当用户使用注解 @DubboComponentScan 时,会激活 DubboComponentScanRegister ,同时生成 ServAnnotationBeanPostProcessor 和 ReferenceAnnotationBeanPostProcessor , ServAnnotationBeanPostProcessor 处理器实现了 BeanDefinitionRegistryPostProcessor 接口,Spring容器会在所有Bean注册之后回调 tProcessBeanDefinitionRegistry 方法。

在这个方法里先提取用户配置的扫描包名称,然后委托Spring对所有符合包名的class文件做字节码分析,然后扫描Dubbo的注解@Serv作为过滤条件,将扫描的服务创建 BeanDefinitionHolder ,用于生成 ServBean 定义,注册 ServBean 的定义并做数据绑定和解析。

这时我们注册了 ServBean 的定义,但是还没有实例化。

ServcBean 的结构如下:

只包含 afterPropertiesSet() 方法,继承该接口的类,在初始化Bean的时候会执行该方法。在构造方法之后调用。

ApplicationContextAware

Spring容器会检测容器中的所有Bean,如果发现某个Bean实现了 ApplicationContextAware 接口,Spring容器会在创建该Bean之后,自动调用该Bean的 setApplicationContextAware() 方法,调用该方法时,会将容器本身作为参数传给该方法。

ApplicationListener

当Spring容器初始化之后,会发布一个ContextRefreshedEvent,实现ApplicationListener接口的类,会调用 onApplicationEvent() 方法。

重要的接口主要是这几个,那么执行的先后顺序是怎样的呢?

如果某个类实现了ApplicationContextAware接口,会在类初始化完成后调用setApplicationContext()方法进行作

首先会执行 ApplicationContextAware 中的 setApplicationContextAware() 方法。

这里主要是将Spring的上下文引用保存到 SpringExtensionFactory 中,里面有个set,保存所有的Spring上下文。这里实现了Dubbo与Spring容器的相连,在SPI机制中利用 ExtensionLoader.getExtension 生成扩展类时,会有一个依赖注入的过程,即调用 injectExtension() 方法,它会通过反射获取类的所有方法,然后遍历以set开头的方法,得到set方法的参数类型,再通过ExtensionFactory寻找参数类型相同的扩展类实例。

如果某个类实现了InitializingBean接口,会在类初始化完成后,并在setApplicationContext()方法执行完毕后,调用afterPropertiesSet()方法进行作

然后会调用 InitializingBean 的 afterPropertiesSet() 方法。

会调用 ApplicationListene 中的 onApplicationEvent 方法。

此时 ServBean 开始暴露。 具体的暴露流程之前已经介绍容量。

在Dubbo中处理 ReferenceBean 是通过 ReferenceAnnotionBeanPostProcessor 处理的,该类继承了 InstantiationAwareBeanPostProcessor ,用来解析@Reference注解并完成依赖注入。

InstatiationAwareBeanPostProcessor

tProcessBeforeInstantiation 方法: 在实例化目标对象执行之前,可以自定义实例化逻辑,如返回一个对象。

tProcessAfterInitialization 方法:Bean实例化完成后执行的后处理作,所有初始化逻辑、装配逻辑之前执行。

tProcessPropertyValues 方法: 完成其他定制的一些依赖注入和依赖检查等,可以增加属性和属性值修改。

新版本出现了改动,采用 AnnotationInjectedBeanPostProcessor 来处理。

AnnotationInjectedBeanPostProcessor 是 ReferenceAnnotationBeanPostProcessor 的父类,它实现InstantiationAwareBeanPostProcessorAdapter的tProcessPropertyValues方法,这个是实例化的后置处理,这个方式是在注入属性时触发,就是要在注入@Reference的接口时候,要将接口封装成的实例注入到Spring容器中.

主要分为两步:

1) 获取类中标注的@Reference注解的字段和方法。

2)反射设置字段或方法对应的引用

最重要的是第二步,通过 inject 方法进行反射绑定。

里面最主要的就是对生成的ReferenceBean设置一个对象。

服务引用的触发时机有两个:

一种是ReferenceBean初始化的时候;另一种是ReferenceBean对应的服务被注入到其他类中时引用。