Spring之依赖注入源码解析(下)


本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。

课程内容:
1、@Autowired注入底层源码解析
2、@Value底层源码解析
3、@Qulifier底层源码解析
4、@Lazy底层源码解析
5、泛型注入底层源码解析
6、@Resource底层源码解析

上一节我们讲了寻找注入点,会找每一个属性上面是谁加了注解的,找准注入点,然后进行注入。
在对方法进行依赖注入的时候,一个方法可能有多个参数。
在这里插入图片描述
首先把入参的个数找到,然后构造一个数组,然后遍历这个参数在这里插入图片描述
把参数的信息找出来,参数的名字、类型。arguments存的就是bean
在这里插入图片描述
我们进入这个核心的方法
进入这个方法我们无论是方法还是字段我们进来就拿两样:名字和类型。
在这里插入图片描述
比如我们简简单单写一个demo,我们这有一个a方法,这个a方法里面有一个参数。我们当前把这个a方法所对应的method对象拿到,我们现在把他切换成为jdk1.7,我们拿到这个method对象了,我们想拿到当前这个方法的参数。在这个1.7里面没有这个反射的api拿到方法的参数的,我们可以很容易拿到这个参数的类型,但是我们想拿到这个a方法里面的abc就比较难。我们可能需要通过一些字节码之类的才能拿到。
在这里插入图片描述
其实我们改成1.8之后他就提供了一个api,就可以实现很容易拿到名字。
在这里插入图片描述

但是现在还拿不到名字,需要两步走,第一步:首先在pom.xml中进行jdk1.8的配置,除此之外还多一项配置。
在这里插入图片描述
第二步:还要改一下项目的jdk配置:
在这里插入图片描述
初始化参数名字的一个发现器。
在这里插入图片描述

这个发现器里面包含两种方法,一个是基于反射的方法一个是就是基于更底层的字节码层面的方法,还是可以基于更底层的方法拿到的。
在这里插入图片描述
继续看这个方法
在这里插入图片描述
我们先不管这个,先看核心的流程,如果我们的字段就是orderService,那么这个程序是怎么做的呢?
在这里插入图片描述
那么就会进到下面这个else方法里面来。下面这个方法里面有一个Lazy。就是判断当前这个属性上有没有写Lazy这个注解,或者方法参数里面有没有这个Lazy这个注解。
在这里插入图片描述
字段上有lazy注解
在这里插入图片描述
他是如何判断的呢?其实Lazy疏解可以写在一个类上,也可以写在一个属性上面,还可以写在一个方法的参数前面。如果我们写在一个属性前面的意思是会产生一个代理对象。
在这里插入图片描述
假设判断出来有了,就进入第一个方法
在这里插入图片描述
看这个接口的实现类。这个就是判断你这个属性有没有Lazy注解。下面这个判断你这个方法的参数前面有没有lazy注解。
在这里插入图片描述
如果有这个注解就返回一个代理对象,没有就返回null。
在这里插入图片描述
spring开始就会给这个orderService赋值赋的就是一个代理对象,那当使用这个orderService的a方法的时候,底层会怎么做?
在这里插入图片描述
首先会来执行这个方法。
在这里插入图片描述
我加了Lazy注解,并没有根据orderService的类型、名字去找bean,只是生成一个代理对象赋值给orderService,当我真正去使用他的时候去bean工厂里面根据类型、名字去找bean,再去执行当前正在执行的方法。这就是Lazy的一个效果,延迟嘛。
上面讲的是在字段上加的注解
下面是在一个方法的参数里面加注解
在这里插入图片描述
那么这个时候一开始找到的是谁,一开始找到的也是代理对象,也是传给了这个参数。是一样的。
如果没有这个Lazy注解就会进入下面这个方法
在这里插入图片描述
到底是如何找bean的呢?
其实是这块与缓存时有关系的。
在这里插入图片描述

我们首先把类型找出来这段时处理@value注解的。
在这里插入图片描述
就是字段前和方法的参数前都可以写@Value注解的
在这里插入图片描述
看你当前字段你有没有写@Vaule注解,点进去
在这里插入图片描述
看这个接口的实现方法
这个就是为了把@Value中的值给拿到。
在这里插入图片描述
按照正常的理解就是把这个值拿到,然后赋值给这个OrderService
在这里插入图片描述

============================================================================================

回到最开始看的哪个方法
在这里插入图片描述

如果拿到的时String,首先进行占位符的填充,加入写的时@Value(“${ding}”),在spring中有一个Environment,这个包括了properties的内容,首先就要通过注解引进来
在这里插入图片描述
其实这个properties里面也就是key、value。Environment还有一些操作系统的环境变量,那里面也是key、value。还有可以通过下面这种方式指定环境变量。
在这里插入图片描述

我们所说的占位符的填充就是那你现在拿你指定的@Value(“${ding}”)里面的ding作为key,去Environment找value。
我们现在看一下运行结果,假设我们不写引入spring.properties这个文件。
在这里插入图片描述

我们现在注入肯定会报错,因为现在这个@Value(“${zhouyu}”)是一个${zhouyu}的字符串,如果先进行占位符的填充,没有填充到,因为我们现在prperties还没起到作用,我们操作系统里面以及jvm里面都没有zhouyu这个key的value,那么他就要按照字符串的形式赋值给OrderService,但是这个属性的类型是OrderService,是赋值不了的,因为没有类型转化器,会报错,所以先写成String才可以
在这里插入图片描述
结果是${zhouyu},是因为他会先去填充,但是有没有东西填充,因为这个properties是没有起作用的。
在这里插入图片描述
如果我们把这个properties文件加进去我们就可以看到我们想要的结果。
在这里插入图片描述
因为·我们properties这块配置的是XXX
在这里插入图片描述
同样我们也可以以这种方式指定一个
在这里插入图片描述
然后这个地方打印出来的结果就是test(前提是那个引入properties的地方先注释掉)
在这里插入图片描述
如果两种方式都配置的话,-D的优先级更高。
============================================================================================
占位符的填充之后就会进行spring表达式的解析,我们回到原来的分析的方法中。
在这里插入图片描述
我们可以不写$这个符号,可以写成#这个符号,#这个符号的意思是spring表达式。
这样写的意思是你去spring容器中找叫zhouyu的bean,这里面指的是bean的名字。按照现在这样去运行是会报错的,因为确实没有这个bean
在这里插入图片描述

你这样写就不会报错了。
在这里插入图片描述
这样就是一个spring表达式了,也是@Value的一个功能。这个地方的源码是很难的,没有必要完全的搞懂。
============================================================================================
类型转换器如果转换成功直接返回,如果没有成功则返回错误。
在这里插入图片描述

假设我们这什么都没有写,就是一个@Value(“123”)的一个字符串,我们就想把这个123赋值给这个属性,默认情况下肯定是赋值不了的,但是spring有这个类型转换器。只要能有一个类型转换器把123转换成OrderService就可以。
在这里插入图片描述
把这个@Autowired注解去掉也是可以的,根据我们上一节课的分析,只要属性上有@Value、@Inject、@Autowired注解中得任一个就会认为是一个注入点。
============================================================================================
接下来我们继续看,如果就是最普通得情况上面什么也没有写。
在这里插入图片描述
又是如何去找bean的。现根据类型去找bean,就是这个Orderservice,如果找到多个再根据名字找。假设我们这样写的。
在这里插入图片描述
我们看一下有几个Orderservice的bean。
第一个:
在这里插入图片描述
第二个:
在这里插入图片描述
现在spring容器里应该是又两个OrderService的bean的。
我们现在这样写运行代码结果是没有任何问题的。
在这里插入图片描述
你这个地方写一个List是一样的效果。
在这里插入图片描述
以上两种方式的意思是我把所有的OrderService的bean都找出来。赋值给这个Map,key就是每一个map的bean,value就是对应的bean对象。
我们看一下源码:
在这里插入图片描述
如果是Map,就把Map里的key找出来,如果key的类型不是string就返回null,你的key一定的要是string类型。也会把value的值找出来。
在这里插入图片描述
这个方法是真真正正的根据类型去找bean,把bean的名字以及符合这个bean的对象返回出来,然后最终返回。
在这里插入图片描述
加入是泛型呢?要知道这个泛型最后显示的是Object。其实最后展示的就是所有的bean,
在这里插入图片描述
但是由于之前讲的@Autowired有一个required这个属性默认是true,如果没有相应的bean就会报错。
在这里插入图片描述

如果把把泛型改成Object。其实最后展示的就是所有的bean。
在这里插入图片描述
最后的结果是:
在这里插入图片描述
如果我们写不是Map、List等这种复合的类型的,我们就直接返回。
在这里插入图片描述
================================================================================

如果是普通的类型,不是List、Map、之类的。
在这里插入图片描述
根据类型找bean。找到的bean中Map里面string是名字,Object不一定是bean对象,有可能是class
在这里插入图片描述
如果我们找到了多个bean
在这里插入图片描述
只有一个bean
在这里插入图片描述
============================================================================================
然后我们看找到多个bean的情况。
先根据类型找,找到了多个bean,进一步进行过滤,根据名字找。
在这里插入图片描述
把多个名字拿出来判断。
在这里插入图片描述
但是在进行名字判断还有一些其他的判断。
我们现在根据OrderService这个类型去找,容器里面我们有两个。先找这两个bean上面有没有primary注解。如果多个bean加了primary注解就报错,抛异常。
除此之外,我们还可以定义优先级。利用Priority注解。这个注解有一个不好的地方就是不能写在方法上面,只能写在类上。不然就报错。
在这里插入图片描述

为达到以上效果,我们可以重新写一下,改一改原来的代码使得这个容器里面有两个bean的这种情况。
①、先创建一个interface接口
在这里插入图片描述
②、先来实现一个这个接口
在这里插入图片描述
③、再来实现一下
在这里插入图片描述
④、那么这个时候我们根据OrderInterface去找bean,就能找到两个。
在这里插入图片描述
⑤、那么此刻根据名字来找肯定是找不到的,因为有两个,不知道是哪一个就会报错。
在这里插入图片描述
⑥、如果在上面加上Primary就不会报错了。
在这里插入图片描述
⑦、还有一种数字越小优先级越高,就是Primary后面可以加上“(数字)”。
回到原来的方法,在这块就是判断优先级的一个过程。
在这里插入图片描述
============================================================================================
我们回到最原始的地方,
在这里插入图片描述

开始的时候会根据类型找到多个bean,然后看是否存在primary,
在这里插入图片描述
如果存在看看优先级,取优先级最高的。
在这里插入图片描述
如果上面这个图里面的两步骤都没有,才会根据名字去判断的。
在这里插入图片描述
============================================================================================
我们再来看一下为什么那个Map里面那个value的object返回的不是bean而是class的情况。
当我们根据OrderService去找的时候,找到了两个bean与其匹配。如果我们最终找到的是OrderService,那这个OrderService1有没有必要在我目前属性注入的时候创建bean对象。意思就是我根据这个类型去找和我当前匹配的类型有哪些,并不是要把所有的bean对象都实例化出来,因为这两个类型都是OrderService,有可能我们只需要两个中的一个啊,另外一个最起码我在给这个属性赋值的时候我不需要OrderService1创建出来一个对象。因为你创建出来,我又用不到。
在这里插入图片描述
在根据类型找的时候这个Map里面key肯定是bean的名字,那他的value,如果我先遍历OrderService,如果你已经创建好一个对象了,那直接把这个对象当作value,如果你还没有创建好,我可以根据你这个bean的beanDefinition判断出来你的type和我的是不是匹配的,我只需要把你的class存进来就好了,这个value就是class。
去BeanFactory里面根据某一个类型去找bean的时候,不是非得把所有的bean都创建好了才能知道你的类型和当前要找的类型是不是匹配的,我们根据你这个bean的BeanDefinition记录的类型判断就可以。
总之就是你这个bean创建好了,这个value就是你的bean,如果没有创建好。就是你这个OrderService的class。

最终还是要进行判断的,这个时候如果判断出来是class,是因为需要把这个value值赋值给属性的,是需要进行创建的。

在这里插入图片描述
============================================================================================
还有些比较偏门的知识。
就是NullBean这种情况。最后显示的结果不是null。因为beanName还是zhouyu,value就是NullBean。
在这里插入图片描述
那spring是怎么处理的呢?
在这里插入图片描述
============================================================================================
我们再回到那个核心的方法findAutowiredCandidates,根据类型去找bean,找到多个bean之后后面是怎么筛选的。这是我们前面讲的
在spring里面那到底是如何根据类型去找到这些bean的呢?那么我们看一下这个方法。
在这里插入图片描述
进入这个方法之后想一下,BeanFactory里面有哪些东西是记录bean的类型的:单例池、BeanDefinitionMap。其实可以根据这两个类型去找哪些bean是符合的。然后我们先要有这个思路,再去看这个源码。
这个只会把名字给返回
在这里插入图片描述
这个时候要考虑bean工厂与父bean工厂。先到自己的bean工厂根据类型找到名字,然后再在父bean工厂里面根据类型找到名字。然后再把二者找到的名字合起来。
在这里插入图片描述
然后这块mergeNamesWithParent相当于是递归。
我们还是看getBeanNamesForType这个方法。
在这里插入图片描述
进入这个接口,在该接口中进去看实现方法。
在这里插入图片描述
进来之后细看一下:
在这里插入图片描述
遍历beanDefinition。
在这里插入图片描述
我们拿到 这个bean需要看这个核心代码match这块进行比对是不是所需要的类型。
如果不是一个FactoryBean,就是一个普通的bean
在这里插入图片描述
判断某一个bean的类型与传进来的bean的类型是不是同一个类型,首先就是看单例池里面有没有。
这个方法会考虑当前这个bean传过来的名字是怎么写的,要考虑很多种情况,你传进来的名字是不是FactoryBean,传进来的名字带不带&这个符号,如果是遍历的beanDefinition里面得到的名字是不会带有&这个的。但是他会兼容来考虑会不会带有这个&符号。
在这里插入图片描述
如果一个bean是FactoryBean,那么这个FactoryBean是什么类型呢?
我们定义一个FactoryBean的时候要调用两个方法,
在这里插入图片描述
这样设计的目的是什么?spring单独设置一个这样的方法的目的是:因为我有一个类型,我要判断当前这个bean是不是我要的这个类型,怎么判断呢?直接生成这个对象吗,不是。最好是先别实例化,因为你生成了万一我不用就浪费了,而是直接提供了一个API,不用生成这个对象。
所以当我们从单例池拿到一个对象,你是FactoryBean,直接调用下面这个方法。
在这里插入图片描述
在这个方法里面直接调用FactoryBean里面的getObjectType方法。
在这里插入图片描述
如果遍历的单例bean里面没有这个bean。接下来只能用mbd
在这里插入图片描述
============================================================================================
spring在进行依赖注入的时候他需要根据当前的类型去找到到底是有哪些bean是之匹配的,就是把spring里面那些bean,那些bean的名字去遍历,去单例池里面去拿beanDefinition。去进行判断,最终看你的类型是不是和我是匹配的,最终会直接返回一个结果,这个结果就是符合要求的bean的名字。
在这里插入图片描述
这时候下面又涉及到一个Map,这个Map是和spring启动是有关系的。
在这里插入图片描述

这个Map里面存的是某个类型你对应的bean是什么,这个和单例池不太一样。spring启动的时候就会往这个Map里面存东西。
在这里插入图片描述
Map里面存的是某一个类型对应的bean对象是什么。
在这里插入图片描述
在外面你也可以直接指定某个类型对应的bean对象是什么。
在这里插入图片描述
现在的目录是:
在这里插入图片描述

在spring里面既然支持一个这样的Map,现在就去spring容器中找有哪些bean是这个类型的。
在这里插入图片描述

如果直接存到这个Map里面的话,Map里面有没有我要的这个类型,把这个bean对象加到我这个集合里面来。其实通过这个方式是没有bean的名字的。
在这里插入图片描述
spring容器启动所存的东西,只是把一个类型的bean对象存起来了,其实这个bean对象是一直没有bean的名字的,所以beanDefinition、单例池里面都是没有那个对象的,比如applicationContext都没有,他只会存在resolvableDependencies里面。
现在举一个例子就是自己注入自己
在这里插入图片描述
假设再定义一个bean
在这里插入图片描述
现在spring容器里面有两个UserService的bean。
现在相当于自己注入自己,那注入的到底是谁呢?
在这里插入图片描述
现在再在下面写一个toString方法
在这里插入图片描述
按照我们正常的分析,我们先根据类型去找,找到两个,然后最终根据名字去找,根据名字找到自己,因为另一个UserService是叫UserService1.
在进行依赖注入的时候他不是先考虑的自己,是先考虑别人,当依赖注入不是自己的时候先进行执行。
在这里插入图片描述
============================================================================================
根据类型找到了与之匹配的,但是这些bean可不可以进行依赖注入呢?
在这里插入图片描述
比如我们以OrderService为例。
把这个OrderService单纯的定义为一个类,
在这里插入图片描述
在AppConfig里面还是定义两个bean

表示当前这个bean可不可以进行依赖注入,默认是true

在这里插入图片描述
那么这个到底能不能依赖注入在哪地方实现的呢?
在这里插入图片描述
你传给我一个bean的名字,我找到你的beanDefinition,找到之后判断这个属性是true还是false。
如果是true就可以进行依赖注入,如果不是就不能。
真正的类是这个类
在这里插入图片描述
判断这个类能不能依赖注入,不是直接去判断这个属性的,是先判断父类能不能依赖注入,如果是不能依赖注入就是false就直接返回了。
在这里插入图片描述
右一是根据属性判断能不能依赖注入,中间是与泛型相关的能不能进行依赖注入,左一是有一个Qualifiers进行判断的,分别有自己的责任。这就是责任链模式。
============================================================================================
我们先来看这个泛型的能不能进行依赖注入。
先举个例子,创建一个BaseService。
在这里插入图片描述
创建一个类,把他定义成一个bean
在这里插入图片描述
这个里面虽然没有属性但是父类里面有。
在这里插入图片描述

在这里插入图片描述

因为是泛型,会找到很多个。要进行筛选。

当进行判断的时候要知道真正的类型才能进行判断。
在Userservice继承BaseService下这个o就是OrderService。拿到类型之后可以再进行筛选。
通过反射机制可以拿到O,S对应关系
在这里插入图片描述
O就是OrderService,S就是stockService。
先根据类型去找bean,一开始会找到多个,进行筛选,多个中先进行autowireCandidate是否等于true进行判断进行第一次筛选。
再进行判断你这个属性是不是泛型,如果是泛型,再去看这个多个bean里面的泛型真正对应的bean是不是相当,相当于是第二层判断,也是第二次筛选。
基于以上两个判断完成后会再进行Qualifiers进行判断的。

在进行依赖注入筛选的时候是有6个判断的。
第一个判断就是这个属性。

在这里插入图片描述
第二个判断是,是不是泛型的这个判断。
第三个判断就是Qualifiers
第四个判断primary这个判断
第五个判断就是优先级最高的判断
第六个判断就是你的名字。
============================================================================================

============================================================================================
上节课我们讲了Spring中的自动注入(byName,byType)和@Autowired注解的工作原理以及源码分析,那么今天这节课,我们来分析还没讲完的,剩下的核心的方法:

@Nullable
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;

该方法表示,传入一个依赖描述(DependencyDescriptor),该方法会根据该依赖描述从BeanFactory中找出对应的唯一的一个Bean对象。

下面来分析一下DefaultListableBeanFactory中resolveDependency()方法的具体实现,具体流程图:
https://www.processon.com/view/link/5f8d3c895653bb06ef076688

findAutowireCandidates()实现

根据类型找beanName的底层流程:https://www.processon.com/view/link/6135bb430e3e7412ecd5d1f2
对应执行流程图为:https://www.processon.com/view/link/5f8fdfa8e401fd06fd984f20

  1. 找出BeanFactory中类型为type的所有的Bean的名字,注意是名字,而不是Bean对象,因为我们可以根据BeanDefinition就能判断和当前type是不是匹配,不用生成Bean对象
  2. 把resolvableDependencies中key为type的对象找出来并添加到result中
  3. 遍历根据type找出的beanName,判断当前beanName对应的Bean是不是能够被自动注入
  4. 先判断beanName对应的BeanDefinition中的autowireCandidate属性,如果为false,表示不能用来进行自动注入,如果为true则继续进行判断
  5. 判断当前type是不是泛型,如果是泛型是会把容器中所有的beanName找出来的,如果是这种情况,那么在这一步中就要获取到泛型的真正类型,然后进行匹配,如果当前beanName和当前泛型对应的真实类型匹配,那么则继续判断
  6. 如果当前DependencyDescriptor上存在@Qualifier注解,那么则要判断当前beanName上是否定义了Qualifier,并且是否和当前DependencyDescriptor上的Qualifier相等,相等则匹配
  7. 经过上述验证之后,当前beanName才能成为一个可注入的,添加到result中

关于依赖注入中泛型注入的实现

首先在Java反射中,有一个Type接口,表示类型,具体分类为:

  1. raw types:也就是普通Class
  2. parameterized types:对应ParameterizedType接口,泛型类型
  3. array types:对应GenericArrayType,泛型数组
  4. type variables:对应TypeVariable接口,表示类型变量,也就是所定义的泛型,比如T、K
  5. primitive types:基本类型,int、boolean

大家可以好好看看下面代码所打印的结果:

public class TypeTest<T> {

	private int i;
	private Integer it;
	private int[] iarray;
	private List list;
	private List<String> slist;
	private List<T> tlist;
	private T t;
	private T[] tarray;

	public static void main(String[] args) throws NoSuchFieldException {

		test(TypeTest.class.getDeclaredField("i"));
		System.out.println("=======");
		test(TypeTest.class.getDeclaredField("it"));
		System.out.println("=======");
		test(TypeTest.class.getDeclaredField("iarray"));
		System.out.println("=======");
		test(TypeTest.class.getDeclaredField("list"));
		System.out.println("=======");
		test(TypeTest.class.getDeclaredField("slist"));
		System.out.println("=======");
		test(TypeTest.class.getDeclaredField("tlist"));
		System.out.println("=======");
		test(TypeTest.class.getDeclaredField("t"));
		System.out.println("=======");
		test(TypeTest.class.getDeclaredField("tarray"));

	}

	public static void test(Field field) {

		if (field.getType().isPrimitive()) {
			System.out.println(field.getName() + "是基本数据类型");
		} else {
			System.out.println(field.getName() + "不是基本数据类型");
		}

		if (field.getGenericType() instanceof ParameterizedType) {
			System.out.println(field.getName() + "是泛型类型");
		} else {
			System.out.println(field.getName() + "不是泛型类型");
		}

		if (field.getType().isArray()) {
			System.out.println(field.getName() + "是普通数组");
		} else {
			System.out.println(field.getName() + "不是普通数组");
		}

		if (field.getGenericType() instanceof GenericArrayType) {
			System.out.println(field.getName() + "是泛型数组");
		} else {
			System.out.println(field.getName() + "不是泛型数组");
		}

		if (field.getGenericType() instanceof TypeVariable) {
			System.out.println(field.getName() + "是泛型变量");
		} else {
			System.out.println(field.getName() + "不是泛型变量");
		}

	}

}

Spring中,但注入点是一个泛型时,也是会进行处理的,比如:

@Component
public class UserService extends BaseService<OrderService, StockService> {

	public void test() {
		System.out.println(o);
	}

}

public class BaseService<O, S> {

	@Autowired
	protected O o;

	@Autowired
	protected S s;
}
  1. Spring扫描时发现UserService是一个Bean
  2. 那就取出注入点,也就是BaseService中的两个属性o、s
  3. 接下来需要按注入点类型进行注入,但是o和s都是泛型,所以Spring需要确定o和s的具体类型。
  4. 因为当前正在创建的是UserService的Bean,所以可以通过userService.getClass().getGenericSuperclass().getTypeName()获取到具体的泛型信息,比如com.zhouyu.service.BaseService<com.zhouyu.service.OrderService, com.zhouyu.service.StockService>
  5. 然后再拿到UserService的父类BaseService的泛型变量: for (TypeVariable<? extends Class<?>> typeParameter : userService.getClass().getSuperclass().getTypeParameters()) {
    System.out.println(typeParameter.getName());
    }
  6. 通过上面两段代码,就能知道,o对应的具体就是OrderService,s对应的具体类型就是StockService
  7. 然后再调用oField.getGenericType()就知道当前field使用的是哪个泛型,就能知道具体类型了

@Qualifier的使用

最后来看一下这个Qualifiers
举个例子
在这里插入图片描述

在这里插入图片描述
那么最终属性注入的结果是什么。
就是上面都是a的。
到底有什么用。举例子:
定义两个注解:

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("random")
public @interface Random {
}
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("roundRobin")
public @interface RoundRobin {
}

定义一个接口和两个实现类,表示负载均衡:

public interface LoadBalance {
	String select();
}
@Component
@Random
public class RandomStrategy implements LoadBalance {

	@Override
	public String select() {
		return null;
	}
}
@Component
@RoundRobin
public class RoundRobinStrategy implements LoadBalance {

	@Override
	public String select() {
		return null;
	}
}

使用:

@Component
public class UserService  {

	@Autowired
	@RoundRobin
	private LoadBalance loadBalance;

	public void test() {
		System.out.println(loadBalance);
	}

}

这个时候使用的是@RoundRobin。

@Resource

@Resource注解底层工作流程图:
https://www.processon.com/view/link/5f91275f07912906db381f6e

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值