【Spring注解驅動開發】自定義組件如何注入Spring底層的組件?看了這篇我才真正理解了原理!!


寫在前面

最近,很多小伙伴出去面試都被問到了Spring問題,關於Spring,細節點很多,面試官也非常喜歡問一些很細節的技術點。所以,在 Spring 專題中,我們盡量把Spring的每個技術細節說清楚,將透徹。

關注 冰河技術 微信公眾號,回復 “ Spring注解 ” 關鍵字領取源碼。

如果文章對你有所幫助,歡迎大家留言、點贊、在看和轉發,大家的支持是我持續創作的動力!

概述

自定義組件要想使用Spring容器底層的一些組件(比如:ApplicationContext、BeanFactory等),此時,只需要讓自定義組件實現XxxAware接口即可。此時,Spring在創建對象的時候,會調用XxxAware接口定義的方法,注入相關的組件。

XxxAware接口概覽

其實,我們之前使用過XxxAware接口,例如,我們之前創建的Employee類,就實現了ApplicationContextAware接口,Employee類的源碼如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試ApplicationContextAware
 */
@Component
public class Employee implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

從Employee類的源碼可以看出,實現ApplicationContextAware接口的話,需要實現setApplicationContext()方法。在IOC容器啟動並創建Employee對象時,Spring會調用setApplicationContext()方法,並且會將ApplicationContext對象傳入到setApplicationContext()方法中,我們只需要在Employee類中定義一個ApplicationContext類型的成員變量來接收setApplicationContext()方法的參數,就可以使用ApplicationContext對象了。

其實,在Spring中,類似於ApplicationContextAware接口的設計有很多,本質上,Spring中類似XxxAware接口都繼承了Aware接口,我們來看下Aware接口的源碼,如下所示。

package org.springframework.beans.factory;
/**
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 */
public interface Aware {

}

可以看到,Aware接口是Spring 3.1版本中引入的接口,在Aware接口中,並未定義任何方法。

接下來,我們看看都有哪些接口繼承了Aware接口,如下所示。

XxxAware接口案例

接下來,我們就挑選幾個常用的XxxAware接口來進行簡單的說明。

ApplicationContextAware接口使用的比較多,我們先來說說這個接口,通過ApplicationContextAware接口我們可以獲取到IOC容器。

首先,我們創建一個Blue類,並實現ApplicationContextAware接口,在實現的setApplicationContext()中將ApplicationContext輸出,如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試ApplicationContextAware接口
 */
public class Blue implements ApplicationContextAware {
    private ApplicationContext applicationContext
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("傳入的ioc:" + applicationContext);
        this.applicationContext = applicationContext;
    }
}

我們也可以為Blue類同時實現幾個XxxAware接口,例如,使Blue類再實現一個BeanNameAware接口,我們可以通過BeanNameAware接口獲取到當前bean在Spring容器中的名稱,如下所示。

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * @author binghe
 * @version 1.0.0
 * @description 測試ApplicationContextAware接口
 */
public class Blue implements ApplicationContextAware, BeanNameAware {
    private ApplicationContext applicationContext
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("傳入的ioc:" + applicationContext);
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("當前bean的名字");
    }
}

接下來,我們再實現一個EmbeddedValueResolverAware接口,我們通過EmbeddedValueResolverAware接口能夠獲取到StringValue解析器。如下所示。

package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.util.StringValueResolver;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試ApplicationContextAware接口
 */
public class Blue implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("傳入的ioc:" + applicationContext);
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("當前bean的名字");
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        String resolveStringValue = resolver.resolveStringValue("你好${os.name} 年齡:#{20*18}");
        System.out.println("解析后的字符串為:" + resolveStringValue);
    }
}

接下來,我們需要在Blue類上標注@Component注解將Blue類添加到IOC容器中,如下所示。

@Component
public class Blue implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {

運行AutowiredTest類的testAutowired02()方法,輸出的結果信息如下所示。

postProcessBeforeInitialization...autowiredConfig=>io.mykit.spring.plugins.register.config.AutowiredConfig$$EnhancerBySpringCGLIB$$d3c83622@1c93084c
postProcessAfterInitialization...autowiredConfig=>io.mykit.spring.plugins.register.config.AutowiredConfig$$EnhancerBySpringCGLIB$$d3c83622@1c93084c
postProcessBeforeInitialization...personDao=>PersonDao{remark='1'}
postProcessAfterInitialization...personDao=>PersonDao{remark='1'}
postProcessBeforeInitialization...personDao2=>PersonDao{remark='2'}
postProcessAfterInitialization...personDao2=>PersonDao{remark='2'}
postProcessBeforeInitialization...personService=>PersonService{personDao=PersonDao{remark='2'}}
postProcessAfterInitialization...personService=>PersonService{personDao=PersonDao{remark='2'}}
postProcessBeforeInitialization...personController=>io.mykit.spring.plugins.register.controller.PersonController@48ae9b55
postProcessAfterInitialization...personController=>io.mykit.spring.plugins.register.controller.PersonController@48ae9b55
執行了Animal類的無參數構造方法
postProcessBeforeInitialization...animal=>io.mykit.spring.plugins.register.bean.Animal@c267ef4
執行了Animal類的初始化方法。。。。。
postProcessAfterInitialization...animal=>io.mykit.spring.plugins.register.bean.Animal@c267ef4
當前bean的名字:blue
解析后的字符串為:你好Windows 10 年齡:360
傳入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@5ecddf8f, started on Wed Aug 19 00:10:13 CST 2020
postProcessBeforeInitialization...blue=>io.mykit.spring.plugins.register.bean.Blue@55182842
postProcessAfterInitialization...blue=>io.mykit.spring.plugins.register.bean.Blue@55182842
Cat類的構造方法...
postProcessBeforeInitialization...cat=>io.mykit.spring.plugins.register.bean.Cat@76505305
Cat的postConstruct()方法...
postProcessAfterInitialization...cat=>io.mykit.spring.plugins.register.bean.Cat@76505305
調用了Dog的有參構造方法
postProcessBeforeInitialization...dog=>Dog{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
postProcessAfterInitialization...dog=>Dog{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
postProcessBeforeInitialization...employee=>io.mykit.spring.plugins.register.bean.Employee@74235045
postProcessAfterInitialization...employee=>io.mykit.spring.plugins.register.bean.Employee@74235045
postProcessBeforeInitialization...fish=>Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
postProcessAfterInitialization...fish=>Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
Fish{cat=io.mykit.spring.plugins.register.bean.Cat@76505305}
Cat的preDestroy()方法...
執行了Animal類的銷毀方法。。。。。

輸出結果中有如下信息。

當前bean的名字:blue
解析后的字符串為:你好Windows 10 年齡:360
傳入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@5ecddf8f, started on Wed Aug 19 00:10:13 CST 2020

說明正確的輸出了結果信息。

XxxAware原理

XxxAware的底層原理是由XxxAwareProcessor類實現的, 例如,我們這里以ApplicationContextAware接口為例,ApplicationContextAware接口的底層原理就是由ApplicationContextAwareProcessor類實現的。從ApplicationContextAwareProcessor類的源碼可以看出,其實現了BeanPostProcessor接口,本質上都是后置處理器。

class ApplicationContextAwareProcessor implements BeanPostProcessor

接下來,我們就以分析ApplicationContextAware接口的原理為例,看看Spring是怎么將ApplicationContext對象注入到Blue類中的。

我們在Blue類的setApplicationContext()方法上打一個斷點,如下所示。

接下來,我們以debug的方式來運行AutowiredTest類的testAutowired02()方法,

這里,我們可以看到,實際上ApplicationContext對象已經注入到Blue類中的setApplicationContext()方法中了。我們在IDEA的方法調用棧中選擇postProcessBeforeInitialization()方法,如下所示。

我們雙擊IDEA中的postProcessBeforeInitialization()方法的調用棧,會在IDEA中自動定位到postProcessBeforeInitialization()方法中,如下所示。

其實,postProcessBeforeInitialization()方法所在的類就是ApplicationContextAwareProcessor。postProcessBeforeInitialization()方法的邏輯比較簡單。

我們來看下在postProcessBeforeInitialization()方法中調用的invokeAwareInterfaces()方法,如下所示。

看到這里,大家是不是有種豁然開朗的感覺!Blue類實現了ApplicationContextAware接口后,Spring為啥會將ApplicationContext對象自動注入到setApplicationContext()方法中就不用說了吧!

其實就是這么簡單!

重磅福利

關注「 冰河技術 」微信公眾號,后台回復 “設計模式” 關鍵字領取《深入淺出Java 23種設計模式》PDF文檔。回復“Java8”關鍵字領取《Java8新特性教程》PDF文檔。回復“限流”關鍵字獲取《億級流量下的分布式限流解決方案》PDF文檔,三本PDF均是由冰河原創並整理的超硬核教程,面試必備!!

好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一起學習,一起進步!!

寫在最后

如果你覺得冰河寫的還不錯,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習高並發、分布式、微服務、大數據、互聯網和雲原生技術,「 冰河技術 」微信公眾號更新了大量技術專題,每一篇技術文章干貨滿滿!不少讀者已經通過閱讀「 冰河技術 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨干!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公眾號吧,每天更新超硬核技術干貨,讓你對如何提升技術能力不再迷茫!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM