spring中的Bean是如何創(chuàng)建的?
帶著這個(gè)問題,從一個(gè)簡(jiǎn)單的例子開始一探究竟。
public class TestSpring {
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();
}
}
這是一個(gè)基于JavaConfig方式來創(chuàng)建spring容器,spring容器創(chuàng)建完之后就開始嘗試獲取UserService對(duì)象,接著調(diào)用userService的test方法,很簡(jiǎn)單的一個(gè)例子。
先貼出UserService的代碼:
@Service
@LogAround(bizTag = "UserService")
public class UserService implements BeanNameAware,InitializingBean {
@Autowired
private OrderService orderService;
private User adminUser;
private String beanName;
/**
* 構(gòu)造函數(shù)
*/
public UserService(){
System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)");
}
/**
* BeanNameAware自動(dòng)給UserService設(shè)置beanName
* @param beanName
*/
@Override
public void setBeanName(String beanName) {
System.out.println("aware自動(dòng)設(shè)值-BeanNameAware");
this.beanName = beanName;
}
/**
* 初始化前
*/
@PostConstruct
public void loadAdminUser(){
System.out.println("初始化前-PostConstruct");
User user = new User();
user.setId(1L);
user.setName("admin");
this.adminUser = user;
}
/**
* 初始化
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化-afterPropertiesSet");
}
public String getBeanName(){
System.out.println(beanName);
return beanName;
}
public User getAdminUser() {
return adminUser;
}
public void test(){
System.out.println("test");
}
}
該如何入手分析UserService這個(gè)Bean是如何創(chuàng)建的呢?
第一步,先想下spring幫我們創(chuàng)建的Bean和我們自己在程序中new UserService()有什么區(qū)別?可能區(qū)別有很多,比如spring幫我們創(chuàng)建的bean是由spring管理的,默認(rèn)是單例的等。但是不管怎么樣spring要想創(chuàng)建Bean,第一步肯定是要拿到構(gòu)造函數(shù)通過反射的方式在Spring容器中先new出這么一個(gè)對(duì)象,我們暫且叫它普通對(duì)象,此時(shí)和我們自己在程序中new UserService()并沒有什么區(qū)別,就是創(chuàng)建一個(gè)普通的對(duì)象。
那第二歩會(huì)做什么呢?此時(shí)這個(gè)普通對(duì)象里面的屬性是沒有值的。bean和new出的對(duì)象還有一個(gè)區(qū)別就是spring會(huì)幫我們的bean中的屬性做注入(依賴注入),這樣bean中的屬性才會(huì)有值,第二歩就是會(huì)對(duì)@Autowired注解修飾的屬性進(jìn)行依賴入。
依賴注入之后,還會(huì)aware接口設(shè)值,有初始化前,初始化和初始化后的動(dòng)作,這些步驟都做完之后才會(huì)生成一個(gè)真正的Bean而不是普通的對(duì)象。
實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),run一下上面的main方法,先看下bean創(chuàng)建的主要步驟后在逐個(gè)解析。

如上圖:
- 創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)
- 對(duì)@Autowired注解的屬性進(jìn)行賦值(依賴注入)
- aware自動(dòng)設(shè)值-BeanNameAware
- 初始化前-PostConstruct
- 初始化-afterPropertiesSet
- 其實(shí)還有一個(gè)初始后的動(dòng)作
Bean的創(chuàng)建流程
通過上面的分析和代碼的運(yùn)行結(jié)果可以大致的看出spring中bean的創(chuàng)建有以下幾個(gè)主要流程:

- 利用該類的構(gòu)造方法來實(shí)例化得到一個(gè)對(duì)象(但是如果一個(gè)類中有多個(gè)構(gòu)造方法, Spring則會(huì)進(jìn)行選擇,這個(gè)叫做推斷構(gòu)造方法,下文在詳細(xì)介紹)
- 得到一個(gè)對(duì)象后,Spring會(huì)判斷該對(duì)象中是否存在被@Autowired注解了的屬 性,把這些屬性找出來并由Spring進(jìn)行賦值(依賴注入)
- 依賴注入后,Spring會(huì)判斷該對(duì)象是否實(shí)現(xiàn)了BeanNameAware接口、 BeanClassLoaderAware接口、BeanFactoryAware接口,如果實(shí)現(xiàn)了,就表示當(dāng)前 對(duì)象必須實(shí)現(xiàn)該接口中所定義的setBeanName()、setBeanClassLoader()、 setBeanFactory()方法,那Spring就會(huì)調(diào)用這些方法并傳入相應(yīng)的參數(shù)
- Aware回調(diào)后,Spring會(huì)判斷該對(duì)象中是否存在某個(gè)方法被@PostConstruct注解 了,如果存在,Spring會(huì)調(diào)用當(dāng)前對(duì)象的此方法(初始化前),上面的代碼中初始化了一個(gè)adminUser的數(shù)據(jù)。
- 緊接著,Spring會(huì)判斷該對(duì)象是否實(shí)現(xiàn)了InitializingBean接口,如果實(shí)現(xiàn)了,就 表示當(dāng)前對(duì)象必須實(shí)現(xiàn)該接口中的afterPropertiesSet()方法,那Spring就會(huì)調(diào)用當(dāng)
前對(duì)象中的afterPropertiesSet()方法(初始化) -
最后,Spring會(huì)判斷當(dāng)前對(duì)象需不需要進(jìn)行AOP,如果不需要那么Bean就創(chuàng)建完 了,如果需要進(jìn)行AOP,則會(huì)進(jìn)行動(dòng)態(tài)代理并生成一個(gè)代理對(duì)象做為Bean(初始化 后)
關(guān)于第6步控制臺(tái)沒辦法打出日志,因?yàn)槌跏己笊婕暗絪pring的源碼操作,但是可以通過斷點(diǎn)看一下,提一句例子中UserService是被LogAspect切面切的。
AOP最終生成代理對(duì)象
如果UserService類@LogAround(bizTag = "UserService")注解去掉,就說明UserService類不需要AOP,那最終生成的Bean就是構(gòu)造函數(shù)所得到的對(duì)象。
有AOP的話那么Bean就是UserService的代理類所實(shí)例化得到的對(duì)象。
這樣,一個(gè)Bean就創(chuàng)建完了,創(chuàng)建完之后呢?
如果當(dāng)前Bean是單例Bean,那么會(huì)把該Bean對(duì)象存入一個(gè)Map<String, Object>,Map的key為beanName,value為Bean對(duì)象。這樣下次getBean時(shí)就可 以直接從Map中拿到對(duì)應(yīng)的Bean對(duì)象了。(實(shí)際上,在Spring源碼中,這個(gè)Map就 是單例池)
如果當(dāng)前Bean是原型Bean,那么后續(xù)沒有其他動(dòng)作,不會(huì)存入一個(gè)Map,下次 getBean時(shí)會(huì)再次執(zhí)行上述創(chuàng)建過程,得到一個(gè)新的Bean對(duì)象。
推斷構(gòu)造方法
至此,我們清楚了Bean的創(chuàng)建流程,那如果UserService中有多個(gè)構(gòu)造函數(shù)呢?第一步還能順利的創(chuàng)建一個(gè)普通對(duì)象嗎?這里面涉及到一個(gè)概念推斷構(gòu)造方法,就是spring會(huì)去推斷用哪個(gè)構(gòu)造方法來創(chuàng)建出普通對(duì)象。
做幾個(gè)小實(shí)驗(yàn):
public UserService(){
System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)");
}
public UserService(OrderService orderService){
System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)有orderService參數(shù)");
}
public UserService(OrderService orderService,String beanName){
System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)有orderService和beanName參數(shù)");
}
初始有這三個(gè)構(gòu)造方法,一個(gè)是無參構(gòu)造方法,一個(gè)是只有一個(gè)參數(shù),一個(gè)是有兩個(gè)參數(shù)。
場(chǎng)景1:和初始情況一樣,Bean能夠正常創(chuàng)建,并且打印"創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)"
場(chǎng)景2:注釋掉第1個(gè)構(gòu)造方法,創(chuàng)建Bean的時(shí)候就報(bào)異常了No default constructor found具體如下
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in file [D:\work\project\test\target\classes\com\test\service\UserService.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.test.service.UserService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.test.service.UserService.<init>()
場(chǎng)景3:注釋掉第1個(gè)和第3個(gè)構(gòu)造方法,能創(chuàng)建成功并打印"創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)有orderService參數(shù)"
場(chǎng)景4:注釋掉第1個(gè)和第2個(gè)構(gòu)造方法,創(chuàng)建失敗,第二個(gè)參數(shù)String類型沒地方拿到值。
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in file [D:\work\project\test\target\classes\com\test\service\UserService.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
以上幾個(gè)場(chǎng)景總結(jié)下:
- 如果一個(gè)類只有一個(gè)構(gòu)造方法,那么沒得選擇,只能用這個(gè)構(gòu)造方法。但是有參的構(gòu)造方法,參數(shù)必須是spring的Bean這樣spring才能拿到進(jìn)行賦值。
- 如果一個(gè)類存在多個(gè)構(gòu)造方法,Spring不知道如何選擇,就會(huì)看是否有無參的構(gòu) 造方法,因?yàn)闊o參構(gòu)造方法本身表示了一種默認(rèn)的構(gòu)造方法。
-
如果都沒有構(gòu)造方法,就是用默認(rèn)的無參構(gòu)造方法來創(chuàng)建。
其實(shí)多個(gè)構(gòu)造函數(shù),也可以手動(dòng)指定告訴spring用哪個(gè)構(gòu)造函數(shù)來創(chuàng)建,那就是加了@Autowired注解
指定構(gòu)造方法
我們通常說的依賴注入,
屬性注入
@Autowired
private OrderService orderService;
這邊的構(gòu)造方法
public UserService(OrderService orderService){
System.out.println("創(chuàng)建普通對(duì)象-構(gòu)造函數(shù)有orderService參數(shù)");
}
就是構(gòu)造函數(shù)注入
依賴注入流程
不管是屬性注入還是構(gòu)造方法注入,能提供的信息只有兩個(gè)一個(gè)是類型OrderService ,一個(gè)是名字orderService。那到底是根據(jù)類型注入的還是根據(jù)名字注入的呢?
假設(shè)根據(jù)名字注入的那剛好有一個(gè)其他類型的Bean名字也叫orderService那注入的時(shí)候豈不是會(huì)類型不匹配異常。比如說剛好有一個(gè)OrderBaseService類但是beanName也叫orderService,如果根據(jù)名字注入的話拿到的是OrderBaseService對(duì)象顯然類型不匹配。所以注入通常是先根據(jù)類型來查找的:
- 先根據(jù)入?yún)㈩愋驼?,如果只找到一個(gè)不用管name,那就直接用來作為入?yún)?/li>
- 如果根據(jù)類型找到多個(gè),則再根據(jù)入?yún)⒚謥泶_定唯一
- 最終如果沒有找到,則會(huì)報(bào)錯(cuò),無法創(chuàng)建當(dāng)前Bean對(duì)象
代理對(duì)象生成
代理對(duì)象通常是AOP的時(shí)候會(huì)生成代理對(duì)象還有一種就是開啟事務(wù)的時(shí)候也會(huì)生成代理對(duì)象。否則的話Bean都是直接根據(jù)構(gòu)造函數(shù)生成對(duì)象在進(jìn)行依賴注入和初始化等流程。
AOP代理對(duì)象生成
AOP就是進(jìn)行動(dòng)態(tài)代理,在創(chuàng)建一個(gè)Bean的過程中,Spring在最后一步會(huì)去判斷當(dāng)前正在 創(chuàng)建的這個(gè)Bean是不是需要進(jìn)行AOP,如果需要?jiǎng)t會(huì)進(jìn)行動(dòng)態(tài)代理。
如何判斷當(dāng)前Bean對(duì)象需不需要進(jìn)行AOP:
- 找出所有的切面Bean
- 遍歷切面中的每個(gè)方法,看是否寫了@Before、@After等注解
- 如果寫了,則判斷所對(duì)應(yīng)的Pointcut是否和當(dāng)前Bean對(duì)象的類是否匹配
- 如果匹配則表示當(dāng)前Bean對(duì)象有匹配的的Pointcut,表示需要進(jìn)行AOP
利用cglib進(jìn)行AOP的大致流程:
- 生成代理類UserServiceProxy,代理類繼承UserService
- 代理類中重寫了父類的方法,比如UserService中的test()方法
- 代理類中還會(huì)有一個(gè)target屬性,該屬性的值為被代理對(duì)象(也就是通過 UserService類推斷構(gòu)造方法實(shí)例化出來的對(duì)象,進(jìn)行了依賴注入、初始化等步驟的 對(duì)象)
- 代理類中的test()方法被執(zhí)行時(shí)的邏輯如下:
a. 執(zhí)行切面邏輯(@Before)
b. 調(diào)用target.test()
當(dāng)我們從Spring容器得到UserService的Bean對(duì)象時(shí),拿到的就是UserServiceProxy所生 成的對(duì)象,也就是代理對(duì)象。
UserService代理對(duì)象.test()--->執(zhí)行切面邏輯--->target.test(),注意target對(duì)象不是代理 對(duì)象,而是被代理對(duì)象。
事務(wù)代理對(duì)象生成
Spring事務(wù) 當(dāng)我們?cè)谀硞€(gè)方法上加了@Transactional注解后,就表示該方法在調(diào)用時(shí)會(huì)開啟Spring事 務(wù),而這個(gè)方法所在的類所對(duì)應(yīng)的Bean對(duì)象會(huì)是該類的代理對(duì)象。
Spring事務(wù)的代理對(duì)象執(zhí)行某個(gè)方法時(shí)的步驟:
- 判斷當(dāng)前執(zhí)行的方法是否存在@Transactional注解
- 如果存在,則利用事務(wù)管理器(TransactionMananger)新建一個(gè)數(shù)據(jù)庫連接
- 修改數(shù)據(jù)庫連接的autocommit為false
- 執(zhí)行target.test(),執(zhí)行程序員所寫的業(yè)務(wù)邏輯代碼,也就是執(zhí)行sql
- 執(zhí)行完了之后如果沒有出現(xiàn)異常,則提交,否則回滾
注意:Spring事務(wù)是否會(huì)失效的判斷標(biāo)準(zhǔn):某個(gè)加了@Transactional注解的方法被調(diào)用時(shí),要判 斷到底是不是直接被代理對(duì)象調(diào)用的,如果是則事務(wù)會(huì)生效,如果不是則失效。
總結(jié)
Spring中Bean的創(chuàng)建過程其實(shí)就是從一個(gè)普通對(duì)象蛻變成Bean的一個(gè)過程,蛻變包括依賴注入,初始化等步驟。最后在看下這個(gè)類是否有被AOP或開啟事務(wù)有的話會(huì)額外生成代理對(duì)象作為Bean。

