【Spring系列】- Bean生命週期底層原理

語言: CN / TW / HK

Bean生命週期底層原理

😄生命不息,寫作不止

🔥 繼續踏上學習之路,學之分享筆記

👊 總有一天我也能像各位大佬一樣

🏆 一個有夢有戲的人 @怒放吧德德

🌝分享學習心得,歡迎指正,大家一起學習成長!

bean.jpg

前言

上次學到動手模擬Spring底層實現,簡單學習了一下Spring,對spring有所瞭解,接著就來分析spring中bean的生命週期的步步流程。

流程

接下來會根據Bean生命週期一步一步去學習,spring在建立bean物件的過程中,還是做了許多的操作,從依賴注入,通過初始化以及前後操作,最後建立了bean物件放入Map單例池,對於多例是不放進去的。

image.png

本次實驗使用的pom依賴座標如下

xml <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.15</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.15</version> </dependency>

依賴注入

首先是根據無參構造方法去獲取物件,通過這個類獲取所有欄位,在來判斷是否有Autowired註解,在給這個屬性去賦值。

java UserService userService1 = new UserService(); for (Field field : userService1.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { field.set(userService1, ???); } }

初始化前執行方法

通過物件獲取所有方法,我們在需要執行的方法上使用PostConstruct註解,然後就只需要遍歷這些方法,去判斷是否含有這個註解,在使用invoke去執行方法。

java for (Method method : userService1.getClass().getMethods()) { if (method.isAnnotationPresent(PostConstruct.class)) { method.invoke(userService1, null); } }

初始化

       除了使用PostConstruct註解去執行方法,還有一種方法是通過去實現InitializingBean介面,並且需要實現其未實現的方法afterPropertiesSet。

       那麼物件是如何知道在spring中會有afterPropertiesSet這個方法呢?可以通過反射判斷是否有這個方法,有的話就直接執行。在spring中,他是採用去判斷物件是否有實現InitializingBean這個類,有的話會強轉成這個類,再去執行這個類的方法

推斷構造方法底層原理

       如果有多個構造方法,回去尋找是否有無參的,找到了,就直接使用,沒找到就會報錯。如果是使用了多個構造方法,可以使用Autowired去告訴spring需要使用那個構造方法。如果在構造方法裡需要一個bean物件,那麼spring會去map單例池中去查詢相應的bean物件,如果沒找到,就會去建立bean物件,但如果是多例bean的話,就不需要查詢,直接建立一個物件。 當我們使用構造方法獲取bean物件時,一般是通過類上使用Component註解去定義一個首位字母小寫的bean物件,也可以是通過Bean註解,去建立不同bean名的相同型別的bean物件。

        如下程式碼,在配置中新增兩個bean物件,包括類上自己生成的一共三個bean物件。分別為{roleServiceroleService1roleService2}。這三個的型別一樣,但是物件是不同的,名字不同,bean物件就不同。

```java @ComponentScan("com.lyd") public class ApplicationConfig {

@Bean
public RoleService roleService1() {
    return new RoleService();
}

@Bean
public RoleService roleService2() {
    return new RoleService();
}

} ``` 當使用其中一個beanName都是可以的

java @Component public class UserService { private RoleService roleService; public UserService(RoleService roleService1) { this.roleService = roleService1; } public void test(){ System.out.println(roleService); } } 但是如果使用的不是上面三個其中之一,就會報錯。但是能看到他找到了三個。 image.png

AOP - 動態代理

使用AOP就是需要使用代理物件,然而代理物件與第一次的物件是不一樣的。 首先定義一個aop切面

java @Aspect @Component public class AopAspect { @Before("execution(public void com.lyd.service.UserService.test())") public void beanBefore(JoinPoint joinPoint) { System.out.println("before"); } } 在配置類中標上註解 @EnableAspectJAutoProxy 開啟切面,這樣切面就會實現了。再來debug呼叫userService的test方法,可以觀察到,獲得的物件是CGLIB代理的物件 image.png 並且能看到裡面的roleService是沒有值的。但是從執行結果來看,先走了切面,最後的roleService是有值的。 image.png 然而這都是因為代理類是父子級關係來實現的。接下來一步一步分析。 image.png        spring通過代理物件,就是為了能夠先執行切面方法,在來執行原本物件的方法。首先,spring會生成一個物件:UserServiceProxy,這個就是UserService物件的代理物件。我們用java面向物件思想來思考,代理物件會繼承UserService類,他內部重寫了父類的test方法,在裡面去執行切面的邏輯,接著通過super.test呼叫父類方法。這個方法雖然是可以實現,但是在spring中卻不是這樣的。 image.png

在Spring中,代理物件裡面還會定義一個父類物件UserService  target,這個物件最終會賦值這個類生成的物件,也就是bean生命週期最開始遇到的那個物件。Spring通過呼叫target.test()實現。說白了還是使用了最原來的那個物件去執行的方法。

image.png

本文正在參加「金石計劃 . 瓜分6萬現金大獎」

👍創作不易,如有錯誤請指正,感謝觀看!記得點贊哦!👍