4、屬性賦值和自動裝配

一、屬性賦值

  • 屬性賦值相關的注解

@Value
@PropertySource
springboot中的@ConfigurationProperties(prefix = "xxx")

二、自動裝配

  • 概念:Spring利用依賴注入(DI),完成對IOC容器中中各個組件的依賴關系賦值

1、@Autowired:自動注入

1)、默認優先按照類型去容器中找對應的組件:applicationContext.getBean(BookDao.class);找到就賦值
2)、如果找到多個相同類型的組件,再將屬性的名稱作為組件的id去容器中查找applicationContext.getBean("bookDao")
3)、@Qualifier("bookDao"):使用@Qualifier指定需要裝配的組件的id,而不是使用屬性名
4)、自動裝配默認一定要將屬性賦值好,沒有就會報錯;可以使用@Autowired(required=false);
5)、@Primary:讓Spring進行自動裝配的時候,默認使用首選的bean;也可以繼續使用@Qualifier指定需要裝配的bean的名字

2、@Resource(JSR250)和@Inject(JSR330)[java規范的注解]

1、@Resource:可以和@Autowired一樣實現自動裝配功能;默認是按照組件名稱進行裝配的;沒有能支持@Primary功能。
2、@Inject:需要導入javax.inject的包,和Autowired的功能一樣。沒有required=false的功能;

總結:

  • @Autowired:Spring定義的; @Resource、@Inject都是java規范
  • AutowiredAnnotationBeanPostProcessor解析完成自動裝配功能

三、自定義組件想要使用Spring容器底層的一些組件

  • 例如:ApplicationContext,BeanFactory,xxx

1、Aware接口

  • Aware是一個具有標識作用的超級接口,實現該接口的bean是具有被spring 容器通知的能力的,而被通知的方式就是通過回調。總之:直接或間接實現了這個接口的類,都具有被spring容器通知的能力。
  • 繼承接口:


    Aware接口繼承接口
  • 可以知道這些xxxAware接口都只有一個setxxx方法,目的就是給實現該接口的類的xxx屬性設置值。在spring中這些實現xxxAware接口的類是如何實現感應并設置xxx屬性的值的呢,答案就是在spring容器中在工廠類創建實例后使用instanceof判斷實例是否屬于xxxAware接口的實例,如果結果是true的話,那么spring容器類就會調用實例的setXxx()方法給實例的xxx屬性設置值。簡單來說就是實現這些 Aware接口的Bean在被初始之后,可以從Spring容器中取得一些相對應的資源,例如實現BeanFactoryAware接口的Bean在初始后,Spring容器將會注入BeanFactory的實例,而實現ApplicationContextAware接口的Bean,在Bean被初始后,將會被注入 ApplicationContext的實例等等。
  • 注意:

有六大類xxxAware是由spring容器自動注冊在容器中的,應用程序不需要再次的在容器中注入了,但是如果我們實現其他的xxxAware,還必須在容器中注入才能使用,從ApplicationContextAwareProcessor源碼可以解讀出,這六類特殊的xxxAware是:

  • @see org.springframework.context.EnvironmentAware
  • @see org.springframework.context.EmbeddedValueResolverAware
  • @see org.springframework.context.ResourceLoaderAware
  • @see org.springframework.context.ApplicationEventPublisherAware
  • @see org.springframework.context.MessageSourceAware
  • @see org.springframework.context.ApplicationContextAware

2、從實現到原理再到實現

  • 前面可知實現xxxAware接口主要是為了獲取spring的一些特性
    (1)自己實現一個接口獲取spring的特性,比如BeanFactoryAware
public class Animal implements BeanFactoryAware {
    private BeanFactory beanFactory;
 
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}
  • 注意:我們的Animal 類實現了aware接口,如果我們直接在應用中new一個Animal 的對象,當然是拿不到beanFactory變量的,我們必須在spring的配置文件中聲明我們的fruit對象才行,也就是說Animal 對象必須交給容器進行管理,容器幫你把各種aware接口中想要注入的對象設置到bean中。具體看容器管理aware接口的代碼實現,代碼在AbstractAutowireCapableBeanFactory的initializeBean方法中:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    invokeAwareMethods(beanName, bean);
                    return null;
                }
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }
// 開始Bean初始化前處理、初始化、初始化后處理
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }

        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

可以看出來,aware接口的各種處理是在屬性設置完成之后、bean初始化之前完成的。顯然的,如果我們直接new出來一個bean,這些框架性的特性是沒有使用到的。但是上面提到的六中特殊的xxxAware,比如ApplicationContextAware,可以完成自動化在容器中進行接口注入,原來在應用中創建上下文容器時會注冊一個BeanPostProcessor------ApplicationContextAwareProcessor,在這個類里面進行了context的注入,這樣我們就能能夠拿到bean中的context對象:

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        AccessControlContext acc = null;

        if (System.getSecurityManager() != null &&
                (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
                        bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
                        bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }

        if (acc != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    invokeAwareInterfaces(bean);
                    return null;
                }
            }, acc);
        }
        else {
            invokeAwareInterfaces(bean);
        }

        return bean;
    }

然后在AbstractApplicationContext.prepareBeanFactory方法中,進行對ApplicationContextAwareProcessor的注入。

    // Configure the bean factory with context callbacks.
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

3、自定義aware接口實現

  • 知道原理后我們要實現自定義aware接口實現,需要完成下面兩個步驟:
    1.實現我們aware接口的postprocessor,并在容器中注冊;
    2.bean實體類集成我們自定義的aware接口并實現
  • 具體案例實現:
    (1)自定義aware接口:
public interface AppleAware {
    void setApple(Apple a);
}

(2)自定義Processor實現BeanPostProcessor接口

public class AppleAwarePostProcessor implements BeanPostProcessor {
 
    private Apple a;
 
    public AppleAwarePostProcessor(Apple a) {
        this.a = a;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof AppleAware) {
            ((AppleAware) bean).setApple(a);
        }
        return bean;
    }
 
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        return bean;
    }
 
}

(3)實現AppleAware 接口完成自定義bean注入

public class Market implements AppleAware {
 
    private Apple a;
 
    @Override
    public void setApple(Apple a) {
         this.a = a;
    }
 
    public String getName() {
        return a.getName();
    }
 
}

(4)測試

  • 一定要把我們的BeanPostProcessor加入到當前容器中,這一點非常重要
public class TestAware {
    public static void main(String args[]) {
        ConfigurableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        BeanPostProcessor bpp = new AppleAwarePostProcessor((Apple)beanFactory.getBean("apple"));
    // 工廠對象中加入我們自定義的BeanPostProcessor
        beanFactory.addBeanPostProcessor(bpp);
        Market market = (Market) beanFactory.getBean("market");
        System.out.println(market.getName());
    }
}

參考:
https://www.cnblogs.com/RunForLove/p/5828916.html

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容