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

Bean生命周期底層原理

😄生命不息,寫作不止
🔥 繼續踏上學習之路,學之分享筆記
👊 總有一天我也能像各位大佬一樣
🏆 一個有夢有戲的人 @怒放吧德德
🌝分享學習心得,歡迎指正,大家一起學習成長!

bean.jpg

前言

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

流程

接下來會根據Bean生命周期一步一步去學習,spring在創建bean對象的過程中,還是做了許多的操作,從依賴注入,通過初始化以及前後操作,最後創建了bean對象放入Map單例池,對於多例是不放進去的。
image.png
本次實驗使用的pom依賴坐標如下

<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註解,在給這個屬性去賦值。

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

初始化前執行方法

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

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對象就不同。

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

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

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

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

@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切面

@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

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