spring-core

bean: 應(yīng)用里被Spring IoC容器管理的對象叫做bean.Spring IoC容器負(fù)責(zé)bean的初始化韵丑,組裝和管理。bean以及它們之間的依賴關(guān)系反映在容器使用的配置元數(shù)據(jù)中壹粟。

BeanFactory: 提供了配置框架和基本功能。BeanFactory提供了一套高級的配置機制可以管理所有類型的對象西土。

ApplicationContext: BeanFactory的子類陆错。用來表示IoC容器并負(fù)責(zé)bean的初始化,配置剪侮,組裝拭宁。容器通過讀取配置元數(shù)據(jù)獲取有關(guān)要實例化,配置和組裝的對象的說明.

循環(huán)依賴問題

循環(huán)依賴:類A在構(gòu)造方法里依賴類B瓣俯,類B的構(gòu)造方法里需要類A杰标。此時就會導(dǎo)致循環(huán)依賴問題,Spring IoC容器在運行時檢測到后降铸,會拋出BeanCurrentlyInCreationException
一種解決辦法就是使用setter注入而不是構(gòu)造注入在旱,盡管推薦構(gòu)造注入,但是setter注入可以解決循環(huán)依賴推掸。

bean引入不同生命周期bean的解決方法

問題桶蝎,當(dāng)單例bean A需要非單例的bean B時,容器只會創(chuàng)建一次bean A谅畅,也就是只有一次機會設(shè)置屬性登渣。容器不能在每次bean A都需要bean B的時候?qū)嵗痓ean B。

一種解決方法就是不用IoC毡泻,通過實現(xiàn)ApplicationContextAware接口讓bean A知道容器胜茧,然后加入一個getBean("B")的方法來在bean A需要B的時候?qū)嵗H缦拢?/p>

package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class BManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("B", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

不過這樣不太好仇味,畢竟業(yè)務(wù)代碼和Spring框架耦合了呻顽。更高級的方式就是使用方法注入,從這可以了解更多this blog entry.

查找方法注入

查找方法注入是容器重載容器管理的bean上方法的能力丹墨,以返回容器中另一個命名bean的查找結(jié)果廊遍。Spring框架通過使用cglib庫中的字節(jié)碼生成來實現(xiàn)此方法注入,以動態(tài)生成覆蓋該方法的子類贩挣。

  • 為了spring動態(tài)創(chuàng)建子類能正常工作喉前,Spring容器將要繼承的類和要被重載的方法不能是final
  • 另一個關(guān)鍵的限制是查找方法不能用于工廠方法王财,特別是不能在配置類中使用@bean方法卵迂,因為在這種情況下容器不負(fù)責(zé)創(chuàng)建實例,因此不能創(chuàng)建運行時生成的子類在飛行中绒净。
    新的代碼:
package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    @Lookup("myCommand")
    protected abstract Command createCommand();
}
管理bean生命周期的行為

通過實現(xiàn)Spring的InitializingBeanDisposableBean接口對bean的生命周期做出不同的反應(yīng)见咒。不過更好的是使用@PostConstruct@PreDestroy注解管理初始化和銷毀。不用和Spring的接口產(chǎn)生耦合挂疆。還有xml的init-methoddestroy-method都可以解耦改览。

使用BeanPostProcessor自定義bean

The BeanPostProcessor接口定了回調(diào)方法讓你可以實現(xiàn)你自己或重載容器默認(rèn)的實例化邏輯哎垦,依賴解析邏輯等等。如果需要再Spring的容器完成實例化恃疯、配置化和初始化一個bean后加上自己的邏輯漏设,可以實現(xiàn)一個或者多個BeanPostProcessor做到。如果配置了多個BeanPostProcessor今妄,想要設(shè)置順序的話郑口,通過implement Ordered接口提供的Ordered屬性來設(shè)置執(zhí)行順序

通過索引提高啟動速度

雖然類路勁掃描很快,但依然可以在編譯期間創(chuàng)建一組靜態(tài)候選者提高大型應(yīng)用的啟動速度盾鳞,原理是當(dāng)ApplicationContext檢測到索引后犬性,會使用它而不是再掃描。
生成索引的方式是腾仅,在每個包含組件(component)的模塊下添加以下依賴

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.0.5.BUILD-SNAPSHOT</version>
        <optional>true</optional>
    </dependency>
</dependencies>

gradle添加的方式:

dependencies {
    compileOnly("org.springframework:spring-context-indexer:5.0.5.BUILD-SNAPSHOT")
}

這個過程會創(chuàng)建一個META-INF/spring.component的文件乒裆,會被包含在jar里。

限定符的方式注入對象
  • @Primary注解標(biāo)識為優(yōu)先注入的對象
  • @Qualifier 注解的方式指定要注入的
  • 使用泛型的方式
@Configuration
public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}
@Autowired
private Store<String> s1; // <String> 泛型限定符, 注入stringStore()

@Autowired
private Store<Integer> s2; // <Integer>泛型限定符, 注入integerStore()

// 注入所有的Store推励,只要他們的泛型是<Integer>
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;

ApplicationContext事件處理和自定義事件

ApplicationContext事件處理是通過ApplicationEventApplicationListener接口實現(xiàn)的鹤耍,當(dāng)實現(xiàn)了ApplicationListener接口的bean部署到context里,每當(dāng)ApplicationEvent獲取到了發(fā)布給ApplicationContext的事件验辞,相關(guān)的bean就會被通知稿黄。從本質(zhì)上來講,這是標(biāo)準(zhǔn)的觀察者模式跌造。
Spring提供了以下標(biāo)準(zhǔn)的event,ContextRefreshedEvent杆怕,ContextStartedEvent,ContextStoppedEvent,ContextStoppedEvent,RequestHandledEvent

自定義事件

首先定義一個event, 需要繼承Spring的ApplicationEvent

public class BlackListEvent extends ApplicationEvent {

    private final String address;
    private final String test;

    public BlackListEvent(Object source, String address, String test) {
        super(source);
        this.address = address;
        this.test = test;
    }

    // accessor and other methods...
}

要發(fā)布event的話壳贪,調(diào)用ApplicationEventPublisherpublishEvent()方法陵珍。然后相關(guān)的bean需要實現(xiàn)ApplicationEventPublisher。代碼如下:

public class EmailService implements ApplicationEventPublisherAware {

    private List<String> blackList;
    private ApplicationEventPublisher publisher;

    public void setBlackList(List<String> blackList) {
        this.blackList = blackList;
    }

    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void sendEmail(String address, String text) {
        if (blackList.contains(address)) {
            BlackListEvent event = new BlackListEvent(this, address, text);
            publisher.publishEvent(event);
            return;
        }
        // send email...
    }
}

Spring容器會在配置期間檢測到實現(xiàn)了ApplicationEventPublisherAwareEmailService類违施,然后自動調(diào)用setApplicationEventPublisher()互纯。實際上,傳入的是Spring容器自己醉拓。

想要接收到發(fā)送的event,創(chuàng)建一個實現(xiàn)了ApplicationListener接口的類并注冊為Spring bean伟姐。代碼如下:

public class BlackListNotifier implements ApplicationListener<BlackListEvent> {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    public void onApplicationEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

注意ApplicationListener被參數(shù)化為自定義的event,BlackListEvent收苏。這意味著onApplicationEvent()方法可以保持類型安全亿卤,避免不必要的向下轉(zhuǎn)換。你可以注冊多個event listener鹿霸,但是默認(rèn)的event listener接收events是同步的排吴。這就會導(dǎo)致publishEvent()被阻塞,直到所有的listeners處理完event懦鼠。單線程和同步的一個好處是可以共享事務(wù)钻哩。如果需要另個一event發(fā)布策略屹堰,可以參考Spring的ApplicationEventMulticaster接口。

注解的方式創(chuàng)建listeners
public class BlackListNotifier {

    private String notificationAddress;

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }

    @EventListener
    public void processBlackListEvent(BlackListEvent event) {
        // notify appropriate parties via notificationAddress...
    }
}

使用@EventListener后街氢,不需要實現(xiàn)指定的接口扯键,方法名也可以自定義。

如果想監(jiān)聽多個event珊肃,并且不想定義任何參數(shù)荣刑,可以在注解上指定event類型,如下:

@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
    ...
}

如果想要把發(fā)布的event的結(jié)果處理另一個event伦乔,只需要將方法返回類型改為想要被發(fā)布的event厉亏,當(dāng)然不支持異步,如下:

@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}

上面的代碼就會處理完BlackListEvent后發(fā)布一個新的ListUpdateEvent烈和。如果需要發(fā)布多個event爱只,只需要返回events的Collection

異步監(jiān)聽器

如果想要特定的listeners異步處理招刹,只需要簡單的加上一個@Async注解恬试,如下:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

使用異步event注意以下幾點:

  • 如果event listener拋出了異常,不會通知給調(diào)用者疯暑,check AsyncUncaughtExceptionHandler了解更多忘渔。
  • 這樣的event listener無法發(fā)送返回。如果需要缰儿,自己注入ApplicationEventPublisher手動發(fā)送畦粮。

如果有多個Listener,可以使用@Order注解指定順序乖阵,如下:

@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

如果想用泛型定義event的結(jié)構(gòu)宣赔。可以使用EntityCreatedEvent<T>瞪浸,<T>是創(chuàng)建的實體類型儒将。You can create the following listener definition to only receive EntityCreatedEvent for a Person:

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    ...
}

可能會遇到類型擦除的問題,具體可以參考使用ResolvableTypeProvider.

Spring AOP

讓我們從中心AOP和術(shù)語開始对蒲。這些術(shù)語不是Spring專屬的钩蚊,而AOP術(shù)語也不是很直觀。如果Spring使用它的術(shù)語可能更加令人疑惑蹈矮。

  • Aspect: 切面砰逻,關(guān)注跨越多個類的模塊。事務(wù)管理就是一個好例子泛鸟。在Spring AOP里蝠咆,aspects是通過被@Aspect注解的類實現(xiàn)马僻,或者基于schema實現(xiàn)的未辆。

  • Joint Point:一個程序執(zhí)行期間的點。比如方法處理或者異常處理。在Spring AOP里屈张,一個Joint Point總是代表一個方法的執(zhí)行瞭恰。

  • Advice:切面里特定的Joint Point采取的行動奠衔。不同類型的advice包含before脊另, around, after等。許多AOP框架里鉴逞,包含Spring遗菠,將advice作為攔截器的模型。圍繞著Joint Point維護一系列的攔截器华蜒。

  • Pointcuts: 切入點辙纬。匹配到j(luò)oint point的。Advice是與切入點表達(dá)式相關(guān)聯(lián)叭喜,并且運行任何匹配到j(luò)oint point的切入點(比如贺拣,指定名字的方法的執(zhí)行)。與切入點表達(dá)式匹配的連接點的概念是aop的核心捂蕴,Spring默認(rèn)使用AspectJ表達(dá)式語言譬涡。

  • Introduction: 為類型定義其他的方法或者字段。Spring AOP允許你為任意advised對象引入新的接口(包括相應(yīng)的實現(xiàn))啥辨。比如涡匀,你可以使用一個introduction讓bean實現(xiàn)IsModified接口。

  • Target Object: 被一個或者多個切面advised的對象溉知。也被稱為advised對象陨瘩。由于spring aop是使用運行時代理實現(xiàn)的,因此該對象將始終是被代理對象级乍。

  • AOP proxy: 有AOP框架創(chuàng)建的對象舌劳,用于實現(xiàn)aspect contract。在Spring框架里玫荣,AOP proxy是JDK動態(tài)代理或者CGLIB代理甚淡。

  • Weaving: 將aspect于其他應(yīng)用的類型或者對象連接起來,來創(chuàng)建一個advised對象捅厂。這個可能在編譯期(比如AspectJ編譯器)贯卦、加載期或者運行期完成。Spring AOP焙贷, 想起他純java AOP框架一樣撵割,在運行期執(zhí)行weaving。

advice的類型
  • Before advice: 在joint point前執(zhí)行的advice
  • After return advice: 在joint point執(zhí)行完成并返回后執(zhí)行advice盈厘。比如睁枕,方法返回了并且沒有拋異常
  • After throwing advice: 在方法拋出異常后執(zhí)行官边。
  • After (finally) advice: 不過joint point正常返回或異常都會執(zhí)行沸手。
  • Around advice: 方法調(diào)用時所有的情況都執(zhí)行外遇。around advice很全面。around advice可以在方法調(diào)用前和后執(zhí)行自定義行為契吉。也會在正常返回和拋出異常時執(zhí)行跳仿。
    雖然around advice包含了所有的joint point, 但Spring建議使用指定類型的advice。而不是直接使用around advice.
    從Spring 2.0開始捐晶,所有的advice參數(shù)都可以是合適的類型菲语,而不是一個Objects數(shù)組。

Spring AOP的能力和目標(biāo)

Spring AOP使用純Java實現(xiàn)的惑灵。所以不需要特別的復(fù)雜的處理山上。Spring AOP不需要控制類加載器層,因此適用于Servelt容器或者應(yīng)用服務(wù)器英支。

Spring AOP當(dāng)前僅支持方法執(zhí)行joint point(Spring beans上的advising的方法執(zhí)行)佩憾。字段攔截沒有被實現(xiàn),如果想攔截字段干花,可以考慮使用AspectJ妄帘。

Spring AOP的目標(biāo)和其他AOP框架不同,Spring AOP的目標(biāo)不是提供一個完整的AOP實現(xiàn)池凄。而是想要提供一個整合AOP和Spring IoC的方式來解決企業(yè)通過用的問題抡驼。
所以Spring AOP的功能是和Spring IoC容器一般是一起使用的。Aspect用正常的bean定義語法配置:這是與其他AOP框架最關(guān)鍵的區(qū)別肿仑。所以用Spring AOP致盟,不能簡單或者高效的處理細(xì)粒度的類:AspectJ在這種情況下很合適。Spring AOP永遠(yuǎn)不會與AspectJ競爭來提供一個全面的AOP解決方案尤慰。我們相信基于代理的框架勾邦,比如Spring AOP和成熟的AspectJ框架都是有價值的,兩者相互補充割择,而不是競爭眷篇。Spring的Spring AOP和Spring IoC無縫整合AspectJ,以便照顧到所有基于Spring應(yīng)用結(jié)構(gòu)的AOP使用荔泳。

AOP 代理

Spring AOP默認(rèn)為AOP代理使用Java動態(tài)代理蕉饼。這讓所有的接口都可以被代理。

Spring AOP也可以使用CGLIB代理玛歌。當(dāng)需要代理類而不是接口的時候昧港。如果一個對象類沒有實現(xiàn)一個接口,Spring AOP會使用CGLIB支子。但更好的是基于接口編程而不是類创肥。業(yè)務(wù)類通常會實現(xiàn)一個或者多個業(yè)務(wù)接口。

Spring組件掃面可能掃不到@Aspect注解,建議可以再加上個@Component注解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叹侄,一起剝皮案震驚了整個濱河市巩搏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趾代,老刑警劉巖贯底,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異撒强,居然都是意外死亡禽捆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門飘哨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胚想,“玉大人,你說我怎么就攤上這事芽隆《俪穑” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵摆马,是天一觀的道長臼闻。 經(jīng)常有香客問我,道長囤采,這世上最難降的妖魔是什么述呐? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任蕉毯,我火速辦了婚禮,結(jié)果婚禮上代虾,老公的妹妹穿的比我還像新娘进肯。我一直安慰自己棉磨,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布乘瓤。 她就那樣靜靜地躺著,像睡著了一般衙傀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上统抬,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天危队,我揣著相機與錄音,去河邊找鬼钙畔。 笑死,一個胖子當(dāng)著我的面吹牛刃鳄,可吹牛的內(nèi)容都是我干的钱骂。 我是一名探鬼主播叔锐,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼见秽!你這毒婦竟也來了愉烙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤解取,失蹤者是張志新(化名)和其女友劉穎步责,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禀苦,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蔓肯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了振乏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔗包。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖慧邮,靈堂內(nèi)的尸體忽然破棺而出调限,到底是詐尸還是另有隱情,我是刑警寧澤误澳,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布耻矮,位于F島的核電站,受9級特大地震影響忆谓,放射性物質(zhì)發(fā)生泄漏裆装。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一倡缠、第九天 我趴在偏房一處隱蔽的房頂上張望米母。 院中可真熱鬧,春花似錦毡琉、人聲如沸铁瞒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慧耍。三九已至身辨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芍碧,已是汗流浹背煌珊。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留泌豆,地道東北人定庵。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像踪危,于是被迫代替她去往敵國和親蔬浙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理贞远,服務(wù)發(fā)現(xiàn),斷路器蓝仲,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 什么是Spring Spring是一個開源的Java EE開發(fā)框架袱结。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,441評論 1 133
  • 感恩日記第十五天:1.感恩垚瑤媽媽中午炒的芹菜,腌制的小咸菜溢吻,味道美美噠棚饵,我又享受了一頓美味的午餐!謝謝硼砰!謝謝!謝...
    豐盛富足閱讀 108評論 0 0
  • 我現(xiàn)在的問題不是學(xué)習(xí)不好的困惑题翰,而是如何讓大學(xué)生活更有意義的困惑豹障。擺脫掉原來好好學(xué)習(xí)的想法,其實學(xué)習(xí)并不能帶給我...
    最愛曹格格閱讀 134評論 0 0
  • 關(guān)于幼兒班的印象血公,一直很模糊累魔,我記得就上了半年摔笤,就因為我天資聰穎破格升到了一年級吕世,沒辦法,無敵就是這么寂寞命辖。長大后...
    會飛的丶魚閱讀 126評論 0 2