前言
最近因?yàn)橥?a href="/article/128006.html">bean配置的問(wèn)題導(dǎo)致生產(chǎn)環(huán)境往錯(cuò)誤的redis實(shí)例寫(xiě)入大量的數(shù)據(jù),差點(diǎn)搞掛redis。經(jīng)過(guò)快速的問(wèn)題定位,發(fā)現(xiàn)是同事新增一個(gè)redis配置文件,并且配置的redissentinelconfiguration的id是一樣的,然后在使用@autowired注入bean的時(shí)候因?yàn)?a href="/article/79719.html">spring bean覆蓋的機(jī)制導(dǎo)致讀取的redis配置不是原來(lái)的。
總結(jié)起來(lái),有兩點(diǎn)問(wèn)題:
- 為什么相同bean id的bean會(huì)被覆蓋
- @autowired注解不是按照bytype的方式進(jìn)行注入的嗎
代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
public class userconfiguration { private int id; private string name; private string city; public int getid() { return id; } public void setid( int id) { this .id = id; } public string getname() { return name; } public void setname(string name) { this .name = name; } public string getcity() { return city; } public void setcity(string city) { this .city = city; } } |
userclient:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class userclient { private userconfiguration configuration; public userclient(userconfiguration configuration) { this .configuration = configuration; } public string getcity() { return configuration.getcity(); } } |
beans.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?xml version= "1.0" encoding= "utf-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http: //www.springframework.org/schema/beans http: //www.springframework.org/schema/beans/spring-beans.xsd"> <bean id= "userconfiguration" class = "com.rhwayfun.springboot.starter.rest.userconfiguration" > <property name= "id" value= "${user1.id}" /> <property name= "name" value= "${user1.name}" /> <property name= "city" value= "${user1.city}" /> </bean> <bean id= "userclient" class = "com.rhwayfun.springboot.starter.rest.userclient" autowire= "byname" > <constructor-arg ref= "userconfiguration" /> </bean> </beans> |
beans2.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?xml version= "1.0" encoding= "utf-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http: //www.springframework.org/schema/beans http: //www.springframework.org/schema/beans/spring-beans.xsd"> <bean id= "userconfiguration" class = "com.rhwayfun.springboot.starter.rest.userconfiguration" > <property name= "id" value= "${user2.id}" /> <property name= "name" value= "${user2.name}" /> <property name= "city" value= "${user2.city}" /> </bean> <bean id= "userclient2" class = "com.rhwayfun.springboot.starter.rest.userclient" > <constructor-arg ref= "userconfiguration" /> </bean> </beans> |
application.properties:
1
2
3
4
5
6
7
|
user1.id= 1 user1.name=bean1 user1.city=hangzhou user2.id= 2 user2.name=bean2 user2.city=shanghai |
applition:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@springbootapplication public class application{ @autowired userclient userclient2; @postconstruct public void init() { string city = userclient2.getcity(); system.out.println(city); } public static void main(string[] args) throws interruptedexception { springapplication.run(application. class , args); thread.sleep( long .max_value); } } |
運(yùn)行程序,你會(huì)發(fā)現(xiàn)不管注入的userclient2還是userclient1,輸出的結(jié)果都是shanghai。但是我們想實(shí)現(xiàn)的是,注入userclient1的時(shí)候輸出的應(yīng)該是hangzhou,注入userclient2的時(shí)候輸出的應(yīng)該是shanghai。這也是導(dǎo)致開(kāi)頭說(shuō)的問(wèn)題的源頭所在。要實(shí)現(xiàn)這個(gè)效果很簡(jiǎn)單,userconfiguration換一個(gè)名字就可以了。
但是,為什么換個(gè)名字就可以了呢,不同spring配置文件相同bean id的bean為什么不會(huì)分別創(chuàng)建呢?原因就在于spring 對(duì)具有相同bean id的實(shí)例做了覆蓋處理。你可以理解為一個(gè)map,key是bean id,value就是class,那么當(dāng)兩次put相同id的bean的時(shí)候自然就被覆蓋了。
我們先回憶下bean的生命周期:
- 實(shí)例化
- 填充屬性
- 調(diào)用beannameaware的setbeanname方法
- 調(diào)用beanfactoryaware的setbeanfactory方法
- 調(diào)用applicationcontextaware的setapplicationcontext方法
- 調(diào)用beanpostprocessor的預(yù)初始化方法
- 調(diào)用initializingbean的afterpropertiesset方法
- 調(diào)用自定義的初始化方法
- 調(diào)用beanpostprocessor的初始化方法
- 實(shí)例化完畢
問(wèn)題出在注冊(cè)bean定義的時(shí)候,我們可以控制臺(tái)看到以下輸出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
overriding bean definition for bean 'userconfiguration' with a different definition: replacing [generic bean: class [com.rhwayfun.springboot.starter.rest.userconfiguration]; scope=; abstract = false ; lazyinit= false ; autowiremode= 0 ; dependencycheck= 0 ; autowirecandidate= true ; primary= false ; factorybeanname= null ; factorymethodname= null ; initmethodname= null ; destroymethodname= null ; defined in file [/users/chubin/ideaprojects/spring-boot-learning-examples/ spring-boot-starter-rest/target/classes/beans.xml]] with [generic bean: class [com.rhwayfun.springboot.starter.rest.userconfiguration]; scope=; abstract = false ; lazyinit= false ; autowiremode= 0 ; dependencycheck= 0 ; autowirecandidate= true ; primary= false ; factorybeanname= null ; factorymethodname= null ; initmethodname= null ; destroymethodname= null ; defined in file [/users/chubin/ideaprojects/spring-boot-learning-examples /spring-boot-starter-rest/target/classes/beans2.xml]] |
就是說(shuō)beans.xml中配置的userconfiguration被beans2.xml配置的userconfiguration實(shí)例覆蓋了。那么自然我們得到的結(jié)果是shanghai了。
spring bean覆蓋
經(jīng)過(guò)上面的分析,我們已經(jīng)知道是因?yàn)楸桓采w的導(dǎo)致的,那么怎么體現(xiàn)的呢?遇到解決不了的問(wèn)題,看源碼往往能得到答案:
這段代碼的邏輯就是,如果不允許具有相同bean id的實(shí)例存在就拋出異常,而這個(gè)值默認(rèn)是true,也就是允許存在相同的bean id定義。
@autowired注解實(shí)現(xiàn)機(jī)制
bean覆蓋的問(wèn)題解決了,那么還有一個(gè)問(wèn)題,為什么使用@autowired注入userclient沒(méi)有報(bào)錯(cuò)呢,明明配置了兩個(gè)類型的bean啊。@autowired不是按照bytype注入的嗎。
你確定嗎?不完全正確。
因?yàn)锧autowired是spring提供的注解,我們可以看到是如何注入的代碼,在autowiredannotationbeanpostprocessor.autowiredmethodelement.inject()
方法中。
1.解析依賴
2.獲取候選bean、決定最終被被注入的最優(yōu)bean
3.最優(yōu)bean的決策過(guò)程:1)判斷時(shí)候有@primary注解;2)如果沒(méi)有,得到最高優(yōu)先級(jí)的bean,也就是是否有實(shí)現(xiàn)了org.springframework.core.ordered
接口的bean(優(yōu)先級(jí)比較,可以通過(guò)注解@order(0)
指定,數(shù)字越小,優(yōu)先級(jí)越高);3)如果仍然沒(méi)有,則根據(jù)屬性名裝配
優(yōu)先級(jí)定義:
1
2
3
4
5
6
7
8
9
10
11
|
/** * useful constant for the highest precedence value. * @see java.lang.integer#min_value */ int highest_precedence = integer.min_value; /** * useful constant for the lowest precedence value. * @see java.lang.integer#max_value */ int lowest_precedence = integer.max_value; |
至此,我們就能理解為什么@autowired能夠通過(guò)屬性名注入不同的bean了。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:http://blog.csdn.net/u011116672/article/details/78074246