此文是Sping in Action 第4版 英文原版切面部分的讀書(shū)筆記微驶,僅限交流使用银舱,有不足之處屑咳,一定聽(tīng)取修改。
系列目錄:
Spring AOP 筆記一(基礎(chǔ)概念柑蛇,一個(gè)簡(jiǎn)單切面)
Spring AOP 筆記二(環(huán)繞通知芥挣,切面中獲取參數(shù))
Spring AOP 筆記三(切面注解引入新的方法)
1. 注解引入(Annotating introductions)
在一些高級(jí)語(yǔ)言中,比如Rudy與Groovy耻台,都有開(kāi)放類(lèi)的概念空免,能夠在不改變對(duì)象和類(lèi)的代碼的前提下,為對(duì)象和類(lèi)添加新的方法盆耽。然而對(duì)于Java蹋砚,很不幸,他不是動(dòng)態(tài)的征字,類(lèi)一旦編譯完成都弹,就很難再為他添加新的功能娇豫。
但是你可以稍微想一想我們?cè)俅沃皩?xiě)的切面匙姜。雖然沒(méi)有為向?qū)ο笾刑砑有碌姆椒ǎ且呀?jīng)向其中添加了新的功能冯痢。既然我們能夠向現(xiàn)有的方法添加新的功能氮昧,為什么不能為一個(gè)對(duì)象添加新的方法那框杜?實(shí)際上,使用名為 引入(introduction) 的AOP概念袖肥,就能夠?qū)崿F(xiàn)咪辱。
之前好像沒(méi)有提到切面實(shí)現(xiàn)的原理。切面是用到了叫做 代理 的設(shè)計(jì)模式椎组。這里簡(jiǎn)單比喻一下油狂,代理就好像是藝人(被切入的bean的方法)盡心盡責(zé)的經(jīng)紀(jì)人[笑臉],在日常工作中寸癌,外部的通告或者節(jié)目邀請(qǐng)都會(huì)先送到經(jīng)紀(jì)人的手里专筷,經(jīng)紀(jì)人會(huì)自己幫助藝人處理完一些必須事務(wù)(@before),,不需要藝人關(guān)系或者插手蒸苇,然后經(jīng)紀(jì)人處理完后磷蛹,再告知(調(diào)用)藝人,去演戲或者參加節(jié)目溪烤,例如下圖:
感興趣的同學(xué)可以深入學(xué)習(xí)一下味咳。
于是乎當(dāng)我們想在對(duì)象中加入新的方法時(shí),比如當(dāng)前的藝人擅長(zhǎng)表演檬嘀,而有些節(jié)目想讓他獻(xiàn)聲槽驶,此時(shí)經(jīng)紀(jì)人就可以找個(gè)會(huì)唱歌的藝人(一個(gè)新的接口,里面有我們想要新增得唱歌方法)鸳兽,當(dāng)欄目索要(調(diào)用)歌曲時(shí)捺檬,此時(shí)經(jīng)紀(jì)人就可以直接讓唱歌的藝人唱一首歌送個(gè)欄目,在欄目(調(diào)用者)那里贸铜,并不會(huì)知道是誰(shuí)完成可唱歌這個(gè)任務(wù)堡纬,如下圖:
這樣說(shuō)應(yīng)該差不多能理解。
然后看一個(gè)小栗子:
我們先定義一個(gè)Singer接口蒿秦,代表會(huì)唱歌的藝人烤镐。
/*
* 一個(gè)歌者
* Created by Henvealf on 2016/9/3.
*/
public interface Singer {
void sing();
}
然后新定義一個(gè)切面,類(lèi)名為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)他沒(méi)有前置before渡处,后置after或環(huán)繞round通知镜悉,只有一個(gè) @DeclareParents ,通過(guò)她,就能將 Singer 接口的實(shí)現(xiàn)引入到 Performance 的實(shí)現(xiàn)類(lèi)中医瘫,即相當(dāng)于Performance 得到了新的方法侣肄。
詳細(xì)解釋一下:
value 指定了哪個(gè)類(lèi)的bean將會(huì)被引入@DeclareParents 注解的接口。在上面的例子中醇份,Performance 后面的加號(hào)表示的是所有 Performance的子類(lèi)型稼锅,而不是 Performance 本身吼具。
defaultImpl 屬性就指定了一個(gè)明確的 Singer 接口的實(shí)現(xiàn)類(lèi),只有接口是沒(méi)用的矩距,所以就需要用他來(lái)提供引入一個(gè)接口的具體實(shí)現(xiàn)拗盒。
那最后 @DeclareParents 注解的 Singer 就是要被引入的接口(Singer)了。注意這里是靜態(tài)的锥债。
所以很明顯陡蝇,我們需要寫(xiě)一個(gè)名為 BackSinger 的 Singer的實(shí)現(xiàn)類(lèi),幕后歌者:
/**
* 幕后歌者
* Created by Henvealf on 2016/9/3.
*/
public class BackSinger implements Singer{
public void sing() {
System.out.println("嘿嘿嘿~~~哈哈哈~~~你們?nèi)ツ睦锇哮肚。毅整。。?);
}
}
最后將 Performance 的實(shí)現(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ǔ)概念悼嫉,一個(gè)簡(jiǎn)單切面)
最后就是運(yùn)行看看效果了。
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類(lèi)型才能執(zhí)行sing()方法,偽裝一下改含,不然被人發(fā)現(xiàn)可就鬧大發(fā)了情龄,編譯器自己就不愿意。
本篇就結(jié)束嘍捍壤! 謝謝大家的支持骤视!或許還有下一篇 !
