此文是Sping in Action 第4版 英文原版切面部分的讀書筆記,僅限交流使用,有不足之處,一定聽取修改。
系列目錄:
Spring AOP 筆記一(基礎(chǔ)概念,一個簡單切面)
Spring AOP 筆記二(環(huán)繞通知,切面中獲取參數(shù))
Spring AOP 筆記三(切面注解引入新的方法)
1. 注解引入(Annotating introductions)
在一些高級語言中,比如Rudy與Groovy,都有開放類的概念,能夠在不改變對象和類的代碼的前提下,為對象和類添加新的方法。然而對于Java,很不幸,他不是動態(tài)的,類一旦編譯完成,就很難再為他添加新的功能。
但是你可以稍微想一想我們再次之前寫的切面。雖然沒有為向?qū)ο笾刑砑有碌姆椒ǎ且呀?jīng)向其中添加了新的功能。既然我們能夠向現(xiàn)有的方法添加新的功能,為什么不能為一個對象添加新的方法那?實際上,使用名為 引入(introduction) 的AOP概念,就能夠?qū)崿F(xiàn)。
之前好像沒有提到切面實現(xiàn)的原理。切面是用到了叫做 代理 的設(shè)計模式。這里簡單比喻一下,代理就好像是藝人(被切入的bean的方法)盡心盡責(zé)的經(jīng)紀(jì)人[笑臉],在日常工作中,外部的通告或者節(jié)目邀請都會先送到經(jīng)紀(jì)人的手里,經(jīng)紀(jì)人會自己幫助藝人處理完一些必須事務(wù)(@before),,不需要藝人關(guān)系或者插手,然后經(jīng)紀(jì)人處理完后,再告知(調(diào)用)藝人,去演戲或者參加節(jié)目,例如下圖:
感興趣的同學(xué)可以深入學(xué)習(xí)一下。
于是乎當(dāng)我們想在對象中加入新的方法時,比如當(dāng)前的藝人擅長表演,而有些節(jié)目想讓他獻(xiàn)聲,此時經(jīng)紀(jì)人就可以找個會唱歌的藝人(一個新的接口,里面有我們想要新增得唱歌方法),當(dāng)欄目索要(調(diào)用)歌曲時,此時經(jīng)紀(jì)人就可以直接讓唱歌的藝人唱一首歌送個欄目,在欄目(調(diào)用者)那里,并不會知道是誰完成可唱歌這個任務(wù),如下圖:
這樣說應(yīng)該差不多能理解。
然后看一個小栗子:
我們先定義一個Singer接口,代表會唱歌的藝人。
/*
* 一個歌者
* Created by Henvealf on 2016/9/3.
*/
public interface Singer {
void sing();
}
然后新定義一個切面,類名為SingerIntroducer,歌手引入者,也就是經(jīng)紀(jì)人。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
/**
* 歌手引入者
* Created by Henvealf on 2016/9/3.
*/
@Aspectpublic
class SingerIntroducer {
@DeclareParents(value="com.mengxiang.concert.Performance+",
defaultImpl = BackSinger.class)
public static Singer singer;
}
可以發(fā)現(xiàn)他沒有前置before,后置after或環(huán)繞round通知,只有一個 @DeclareParents ,通過她,就能將 Singer 接口的實現(xiàn)引入到 Performance 的實現(xiàn)類中,即相當(dāng)于Performance 得到了新的方法。
詳細(xì)解釋一下:
value 指定了哪個類的bean將會被引入@DeclareParents 注解的接口。在上面的例子中,Performance 后面的加號表示的是所有 Performance的子類型,而不是 Performance 本身。
defaultImpl 屬性就指定了一個明確的 Singer 接口的實現(xiàn)類,只有接口是沒用的,所以就需要用他來提供引入一個接口的具體實現(xiàn)。
那最后 @DeclareParents 注解的 Singer 就是要被引入的接口(Singer)了。注意這里是靜態(tài)的。
所以很明顯,我們需要寫一個名為 BackSinger 的 Singer的實現(xiàn)類,幕后歌者:
/**
* 幕后歌者
* Created by Henvealf on 2016/9/3.
*/
public class BackSinger implements Singer{
public void sing() {
System.out.println("嘿嘿嘿~~~哈哈哈~~~你們?nèi)ツ睦锇。。。?);
}
}
最后將 Performance 的實現(xiàn)dancer 與切面 SingerIntroducer 注入到容器中。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import javax.sound.midi.Track;
/**
*
* Created by Henvealf on 2016/8/26.
*/
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.henvealf.learn.spring.concert")
public class ConcertConfig {
@Bean(name = "dancer")
public Performance dancer(){
return new Dancer();
}
@Bean
SingerIntroducer singerIntroducer(){
return new SingerIntroducer();
}
}
關(guān)于 dance 可以查看 Spring AOP 筆記一(基礎(chǔ)概念,一個簡單切面)
最后就是運行看看效果了。
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
*
*
Created by Henvealf on 2016/8/27.
*/
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext("com.mengxiang.concert");
Performance per = (Performance) context.getBean("dancer");
Singer singer = (Singer)per; singer.sing();
//或者這樣:
//((Singer)per).sing();
}
}
執(zhí)行結(jié)果:
嘿嘿嘿~~~哈哈哈~~~你們?nèi)ツ睦锇。。。?/p>
我們可以發(fā)現(xiàn),為了執(zhí)行在Performance中引入的sing()方法,需要先將其bean轉(zhuǎn)型為Singer類型才能執(zhí)行sing()方法,偽裝一下,不然被人發(fā)現(xiàn)可就鬧大發(fā)了,編譯器自己就不愿意。
本篇就結(jié)束嘍! 謝謝大家的支持!或許還有下一篇 !
