AOP 你看這一篇就夠了

網(wǎng)上很多人在介紹AOP時都這樣說:面向切面編程,通過預編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護的一種技術(shù)棚壁。個人認為這句話是錯誤齿尽。AOP和OOP一樣,是一種程序設(shè)計思想亮元,而非技術(shù)手段猛计。

程序設(shè)計有六大原則,其中第一原則就是單一職責原則爆捞。意思就是一個類只負責一件事情奉瘤。這與OOP的封裝特性相得益彰。在這個條件下煮甥,我們的程序會被分散到不同的類盗温、不同的方法中去。這樣做的好處是降低了類的復雜性成肘,提高了程序的可維護性卖局。但是同時,它也使代碼變得啰嗦了双霍。例如砚偶,我們要為方法添加調(diào)用日志,那就必須為所有類的所有方法添加日志調(diào)用洒闸,盡管它們都是相同的染坯。為了解決上述問題,AOP應運而生了顷蟀。

AOP旨在將橫切關(guān)注點與業(yè)務主體進行分類酒请,從而提高程序代碼的模塊化程度。橫切關(guān)注點是一個抽象的概念鸣个,它是指那些在項目中貫穿多個模塊的業(yè)務羞反。上個例子中日志功能就是一個典型的橫切關(guān)注點。

AOP的幾種實現(xiàn)方式

動態(tài)代理

動態(tài)代理是一種設(shè)計模式囤萤。它有以下特征:

我們不需要自己寫代理類昼窗。

運行期通過接口直接生成代理對象。

運行期間才確定代理哪個對象涛舍。

以下面這個例子為例澄惊,我們看一下動態(tài)代理的類圖結(jié)構(gòu)。

通常我們的APP都有一部分功能要求用戶登錄之后才能訪問。如修改密碼掸驱、修改用戶名等功能肛搬。當用戶打算使用這些功能時,我們一般要對用戶的登錄狀態(tài)進行判斷毕贼,只有用戶登錄了温赔,才能正常使用這些功能。而如果用戶未登錄鬼癣,我們的APP要跳轉(zhuǎn)到登錄頁陶贼。就以修改密碼為例我們看一下動態(tài)代理的類圖。

InvocationHandler是Java JDK提供的動態(tài)代理的入口待秃,用來對被代理對象的方法做處理拜秧。

代碼如下:

public static class LoginCheckHandler implements InvocationHandler { private static T proxy(Ssource, Class tClass) {return(T) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{tClass}, new LoginCheckHandler(source)); }? private Object mSource; LoginCheckHandler(Objectsource) { this.mSource =source; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if(!checkLogin()){ jumpToLoginActivity();returnnull; }returnmethod.invoke(mSource, args); } private booleancheckLogin(){ System.out.println("用戶未登錄");returnfalse; } private voidjumpToLoginActivity(){ System.out.println("跳轉(zhuǎn)到登錄頁"); } } public class Client {? public static void main(String[] args) { IUserSettingsource= new UserSetting(); IUserSetting iUserSetting = LoginCheckHandler.proxy(source,IUserSetting.class); iUserSetting.changePwd("new Password"); } }

經(jīng)過這樣封裝之后,檢查登錄跳轉(zhuǎn)登錄頁的邏輯作為橫切關(guān)注點就和業(yè)務主體進行了分離章郁。當有新的需求需要登錄檢查時枉氮,我們只需要通過LoginCheckHandler生成新的代理對象即可。

APT

APT(Annotation Processing Tool)是一種編譯期注解處理技術(shù)暖庄。它通過定義注解和處理器來實現(xiàn)編譯期生成代碼的功能嘲恍,并且將生成的代碼和源代碼一起編譯成.class文件。通過APT技術(shù)雄驹,我們將橫切關(guān)注點封裝到注解處理器中佃牛,從而實現(xiàn)橫切關(guān)注點與業(yè)務主體的分離。更詳細的介紹請移步

Android編譯期插樁医舆,讓程序自己寫代碼(一)

俘侠。

AspectJ

AspectJ就是一種編譯器,它在Java編譯器的基礎(chǔ)上增加了關(guān)鍵字識別和編譯方法蔬将。因此爷速,AspectJ可以編譯Java代碼。它還提供了Aspect程序霞怀。在編譯期間惫东,將開發(fā)者編寫的Aspect程序織入到目標程序中,擴展目標程序的功能毙石。開發(fā)者通過編寫AspectJ程序?qū)崿F(xiàn)AOP功能廉沮。更詳細的介紹請移步

Android編譯期插樁,讓程序自己寫代碼(二)

徐矩。

Transform + Javassist/ASM

Transform是Android Gradle提供的滞时,可以操作字節(jié)碼的一種方式。App編譯時滤灯,源代碼首先會被編譯成class坪稽,然后再被編譯成dex曼玩。在class編譯成dex的過程中,會經(jīng)過一系列 Transform 處理窒百。Javassist/ASM是一個能夠非常方便操作字節(jié)碼的庫黍判。我們通過它們可以修改編譯的.class文件。

橫切關(guān)注點

影響應用多處的功能(日志篙梢、事務样悟、安全)

增強(Advice)

增強定義了切面要完成的功能以及什么時候執(zhí)行這個功能。

Spring 切面可以應用 5 種類型的增強:

前置增強(Before) 在目標方法被調(diào)用前調(diào)用增強功能

后置增強(After) 在目標方法完成之后調(diào)用增強庭猩,不關(guān)注方法輸出是什么

返回增強(After-returning) 在目標方法成功執(zhí)行之后調(diào)用增強

異常增強(After-throwing) 在目標方法拋出異常后調(diào)用增強

環(huán)繞增強(Around) 在被增強的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義行為陈症,即包括前置增強和后置增強蔼水。

連接點(Join Point)

應用中每一個有可能會被增強的點被稱為連接點。

切點(Pointcut)

切點是規(guī)則匹配出來的連接點录肯。

切面(Aspect)

切面是增強和切點的結(jié)合趴腋,定義了在何時和何處完成其功能。

引入(Introduction)

引入允許我們向現(xiàn)有的類中添加新方法和屬性论咏∮啪妫可以在不修改現(xiàn)有的類的情況下,讓類具有新的行為和狀態(tài)厅贪。

織入(Weaving)

織入是把切面應用到目標對象中并創(chuàng)建新的代理對象的過程蠢护。在目標對象的生命周期里有多個點可以進行織入:

編譯器:切面在目標類編譯時織入。這種方式需要特殊的編譯器养涮。AspectJ 的織入編譯器就是以這種方式織入切面的葵硕。

類加載器:切面在目標類加載到 JVM 時被織入。這種方式需要特殊的類加載器(ClassLoader)贯吓,它可以在目標類被引入應用之前增強該目標類的字節(jié)碼懈凹。AspectJ5 的加載時織入(LTW)支持以這種方式織入。

運行期:切面在應用運行時的某個時刻被織入悄谐。一般情況下介评,在織入切面時,AOP 容器會為目標對象動態(tài)地創(chuàng)建一個代理對象爬舰。Spring AOP 就是以這種方式織入切面的们陆。

Spring 對 AOP 的支持

Spring 對 AOP 的支持在很多方面借鑒了 AspectJ 項目。目前 Spring 提供了 4 種類型的 AOP 支持:

基于代理的經(jīng)典 AOP

純 POJO 切面

@AspectJ 注解驅(qū)動的切面

注入式 AspectJ 切面

Spring AOP 構(gòu)建在動態(tài)代理基礎(chǔ)之上情屹,因此 Spring 對 AOP 的支持局限于方法攔截棒掠。

運行時增強

通過在代理中包裹切面,Spring 在運行期把切面織入到 Spring 管理的 bean 中屁商。代理類封裝了目標類烟很,并攔截被增強方法的調(diào)用颈墅,再把調(diào)用轉(zhuǎn)發(fā)給真正的目標 bean。在代理攔截到方法調(diào)用時雾袱,在調(diào)用目標 bean 方法之前恤筛,會執(zhí)行切面邏輯。

直到應用需要代理的 bean 時芹橡,Spring 才創(chuàng)建代理對象毒坛。如果使用 ApplicationContext 的話,在 ApplicationContext 從 BeanFactory 中加載所有 bean 的時候林说,Spring 才會創(chuàng)建被代理的對象煎殷。

方法級別的連接點

Spring 基于動態(tài)代理實現(xiàn) AOP,所以 Spring 只支持方法連接點腿箩。其他的 AOP 框架比如 AspectJ 與 JBoss豪直,都提供了字段和構(gòu)造器接入點,允許創(chuàng)建細粒度的增強珠移。

切點表達式

Spring AOP 中弓乙,使用 AspectJ 的切點表達式來定義切點。Spring 只支持 AspectJ 切點指示器(pointcut designator)的一個子集钧惧。

指示器

AspectJ 指示器描述arg( )限制連接點匹配參數(shù)為指定類型的執(zhí)行方法execution( )用于匹配連接點this指定匹配 AOP 代理的 bean 引用的類型target指定匹配對象為特定的類within( )指定連接點匹配的類型@annotation匹配帶有指定注解的連接點

編寫切點

package concert;public interface Performance { public void perform();}復制代碼復制代碼

Performance 類可以代表任何類型的現(xiàn)場表演暇韧,比如電影、舞臺劇等∨ǖ桑現(xiàn)在編寫一個切點表達式來限定 perform() 方法執(zhí)行時觸發(fā)的增強懈玻。

execution(* concert.Performance.perform(..))復制代碼復制代碼

每個部分的意義如下圖所示:

也可以引入其他注解對匹配規(guī)則做進一步限制。比如

execution(* concert.Performance.perform(..)) && within(concert.*)復制代碼復制代碼

within() 指示器限制了切點僅匹配 concert 包乾颁。

Spring 還有一個 bean() 指示器酪刀,允許我們在切點表達式中使用 bean 的 ID 表示 bean。

execution(* concert.Performance.perform(..)) && bean('woodstock')復制代碼復制代碼

以上的切點就表示限定切點的 bean 的 ID 為 woodstock 钮孵。

給自己的Java技術(shù)交流群打波廣告吧骂倘,想要學習Java架構(gòu)技術(shù)的朋友可以加我的群:710373545,群內(nèi)每晚都會有阿里技術(shù)大牛講解的最新Java架構(gòu)技術(shù)巴席。并會錄制錄播視頻分享在群公告中历涝,作為給廣大朋友的加群的福利——分布式(Dubbo、Redis漾唉、RabbitMQ荧库、Netty、RPC赵刑、Zookeeper分衫、高并發(fā)、高可用架構(gòu))/微服務(Spring Boot般此、Spring Cloud)/源碼(Spring蚪战、Mybatis)/性能優(yōu)化(JVM牵现、TomCat、MySQL)

使用注解創(chuàng)建切面

定義切面

在一場演出之前邀桑,我們需要讓觀眾將手機靜音且就座瞎疼,觀眾在表演之后鼓掌,在表演失敗之后可以退票壁畸。在觀眾類中定義這些功能。

@Aspectpublic class Audience {? @Pointcut("execution(* concert.Performance.perform(..)))") public voidperformance(){} @Before("performance()") public voidsilenceCellPhones() { System.out.println("Silencing cell phones"); } @Before("performance()") public voidtakeSeats() { System.out.println("Taking seats"); } @AfterReturning("performance()") public voidapplause() { System.out.println("CLAP CLAP CLAP!!!"); } @AfterThrowing("performance()") public voiddemandRefund() { System.out.println("Demanding a refund"); }}復制代碼復制代碼

@AspectJ 注解表名了該類是一個切面捏萍。 @Pointcut 定義了一個類中可重用的切點太抓,寫切點表達式時走敌,如果切點相同给赞,可以重用該切點片迅。 其余方法上的注解定義了增強被調(diào)用的時間,根據(jù)注解名可以知道具體調(diào)用時間驱闷。

到目前為止空另, Audience 仍然只是 Spring 容器中的一個 bean摄杂。即使使用了 AspectJ 注解,但是這些注解仍然不會解析映挂,因為目前還缺乏代理的相關(guān)配置咪辱。

如果使用 JavaConfig,在配置類的類級別上使用 @EnableAspectJAutoProxy 注解啟用自動代理功能专筷。

@Configuration@EnableAspectJAutoProxy@ComponentScanpublic class ConcertConfig { @Bean public Audienceaudience() {returnnew Audience(); } }復制代碼復制代碼

如果使用 xml 味咳,那么需要引入 <aop:aspectj-autoproxy> 元素。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="concert"/> <aop:aspectj-autoproxy/> <bean class="concert.Audience"/></beans>

環(huán)繞增強

環(huán)繞增強就像在一個增強方法中同時編寫了前置增強和后置增強。

@Aspectpublic class Audience { @Pointcut("execution(* concert.Performance.perform(..)))") public voidperformance(){} @Around("performance()") public void watchPerformance(ProceedingJoinPoint joinPoint) { try { System.out.println("Silencing cell phones"); System.out.println("Taking seats"); joinPoint.proceed(); System.out.println("CLAP CLAP CLAP!!!"); } catch (Throwable throwable) { System.out.println("Demanding a refund"); } }}

可以看到,這個增強達到的效果與分開寫前置增強與后置增強是一樣的辱姨,但是現(xiàn)在所有的功能都位于同一個方法內(nèi)。 注意該方法接收 ProceedingJoinPoint 作為參數(shù),這個對象必須要有,因為需要通過它來調(diào)用被增強的方法醇份。 注意,在這個方法中,我們可以控制不調(diào)用 proceed() 方法允趟,從而阻塞對增強方法的訪問恼策。同樣,我們也可以在增強方法失敗后潮剪,多次調(diào)用 proceed() 進行重試涣楷。

增強方法參數(shù)

修改 Perform#perform() 方法,添加參數(shù)

package concert;public interface Performance { public void perform(int audienceNumbers);}復制代碼復制代碼

我們可以通過切點表達式來獲取被增強方法中的參數(shù)抗碰。

@Pointcut("execution(* concert.Performance.perform(int)) && args(audienceNumbers)))") public void performance(int audienceNumbers){}復制代碼復制代碼

注意狮斗,此時方法接收的參數(shù)為 int 型, args(audienceNumbers) 指定參數(shù)名為 audienceNumbers 改含,與切點方法簽名中的參數(shù)匹配情龄,該參數(shù)不一定與增強方法的參數(shù)名一致迄汛。

引入增強

切面不僅僅能夠增強現(xiàn)有方法捍壤,也能為對象新增新的方法。 我們可以在代理中暴露新的接口鞍爱,當引入接口的方法被調(diào)用時鹃觉,代理會把此調(diào)用委托給實現(xiàn)了新接口的某個其他對象。實際上睹逃,就是一個 bean 的實現(xiàn)被拆分到多個類中了盗扇。 定義 Encoreable 接口,將其引入到 Performance 的實現(xiàn)類中沉填。

public interface Encoreable { void performEncore();}

創(chuàng)建一個新的切面

@Aspectpublic class EncoreableIntroducer { @DeclareParents(value ="concert.Performance+",defaultImpl = DefaultEncoreable.class) public static Encoreable encoreable;}

我們使用了 @Aspect 將 EncoreableIntroducer 標記為一個切面疗隶,但是它沒有提供前置、后置或環(huán)繞增強翼闹。通過 @DeclareParents 注解將 Encoreable 接口引入到了 Performance bean 中斑鼻。

@DeclareParents 注解由三部分組成:

value 屬性指定了哪種類型的 bean 要引入該接口。在上述代碼中猎荠,類名后面的 + 號表示是 Performance 的所有子類型坚弱,而不是它本身蜀备。

defaultImpl 屬性指定了為引入功能提供實現(xiàn)的類。

@DeclareParents 注解所標注的靜態(tài)屬性指明了要引入的接口荒叶。

同樣地碾阁,我們在 Spring 應用中將該類聲明為一個 bean:

<bean class="concert.EncoreableIntroducer" />

Spring 的自動代理機制將會獲取到它的聲明,并創(chuàng)建相應的代理些楣。然后將調(diào)用委托給被代理的 bean 或者被引入的實現(xiàn)脂凶,具體取決于調(diào)用的方法屬于被代理的 bean 還是屬于被引入的接口。

在 XML 中聲明切面

更新一下 Audience 類戈毒,將它的 AspectJ 注解全部移除艰猬。

public class Audience {? public voidsilenceCellPhones() { System.out.println("Silencing cell phones"); } public voidtakeSeats() { System.out.println("Taking seats"); } public voidapplause() { System.out.println("CLAP CLAP CLAP!!!"); } public voiddemandRefund() { System.out.println("Demanding a refund"); }}

聲明前置與后置增強

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:config> <aop:aspect ref="audience"> <aop:before pointcut="execution(* concert.Performance.perform(..))" method="silenceCellPhone"/> <aop:before pointcut="execution(* concert.Performance.perform(..))" method="takeSeats"/> <aop:after-returning pointcut="execution(* concert.Performance.perform(..))" method="applause"/> <aop:after-throwing pointcut="execution(* concert.Performance.perform(..))" method="demandRefund"/> </aop:aspect> </aop:config></beans>

如上所示,就將一個普通方法變?yōu)榱嗽鰪姟?大多數(shù)的 AOP 配置元素都必須在 <aop:config>元素的上下文內(nèi)使用埋市。元素名基本上都與注解名相對應冠桃。 這里,我們同樣將同一個切點表達式寫了四遍道宅,將它提取出來食听。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:config> <aop:aspect ref="audience"> <aop:pointcut id="performance" expression="execution(* concert.Performance.perform(..))"/> <aop:before pointcut-ref="performance" method="silenceCellPhone"/> <aop:before pointcut-ref="performance" method="takeSeats"/> <aop:after-returning pointcut-ref="performance" method="applause"/> <aop:after-throwing pointcut-ref="performance" method="demandRefund"/> </aop:aspect> </aop:config></beans>

注意,此時 <aop:pointcut> 標簽位于 <aop:aspect> 下層污茵,故只能在該切面中引用樱报。如果想要一個切點能夠被多個切面引用,可以將 <aop:aspect> 元素放在 <aop:config> 下第一層泞当。

環(huán)繞增強

定義環(huán)繞增強方法

public class Audience { public void performance(int audienceNumbers){} public void watchPerformance(ProceedingJoinPoint joinPoint) { try { System.out.println("Silencing cell phones"); System.out.println("Taking seats"); joinPoint.proceed(); System.out.println("CLAP CLAP CLAP!!!"); } catch (Throwable throwable) { System.out.println("Demanding a refund"); } }}

在 xml 中使用 <aop:around> 指定方法名與切點即可迹蛤。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:config> <aop:aspect ref="audience"> <aop:pointcut id="performance" expression="execution(* concert.Performance.perform(..))"/> <aop:around pointcut-ref="performance" method="watchPerformance"/> </aop:aspect> </aop:config></beans>

為增強傳遞參數(shù)

獲取參數(shù)主要就在于切點表達式。

<aop:pointcut id="performance" expression="execution(* concert.Performance.perform(int)) and args(audienceNumbers)"/>

這樣能在 xml 中定位到一個參數(shù)類型為 int 襟士,參數(shù)名為 audienceNumbers 的切點盗飒。 注意在 xml 中使用了 and 代替 && (在 XML 中, & 符號會被解析為實體的開始)陋桂。

引入增強

<aop:declare-parents types-matching="concert.Performance+" implement-interface="concert.Encoreable" default-impl="concert.DefaultEncoreable"/>

types-matching 指定了要匹配的類型逆趣,與注解中的 value 值功能相同。

注入 AspectJ 切面

AspectJ 切面提供了 Spring AOP 所不能支持的許多類型的切點嗜历。 切面很有可能依賴其他類來完成它們的工作宣渗。我們可以借助 Spring 的依賴注入把 bean 裝配進 AspectJ 切面中。

創(chuàng)建一個新切面梨州。

public aspect CriticAspect { private CriticismEngine criticismEngine; publicCriticAspect() { } pointcut performance():execution(* perform(..)); afterReturning() :performance() { System.out.println(criticismEngine.getCriticism()); } public voidsetCriticismEngine(CriticismEngine criticismEngine) { this.criticismEngine = criticismEngine; }}

注入的 CritismEngine 的實現(xiàn)類

public class CriticismEngineImple implements CriticismEngine { publicCriticismEngineImple() { } public StringgetCriticism() { int i = (int) (Math.random() * criticismPool.length);returncriticismPool[i]; }? private String[] criticismPool; public voidsetCriticismPool(String[] criticismPool) { this.criticismPool = criticismPool; }}

CriticAspect 主要作用是在表演結(jié)束后為表演發(fā)表評論痕囱。 實際上, CriticAspect 是調(diào)用了 CriticismEngine 的方法來發(fā)表評論暴匠。通過 setter 依賴注入為 CriticAspect 設(shè)置 CriticismEngine 鞍恢。

在配置文件中將 CriticismEngine bean 注入到 CriticAspect 中。

<bean class="om.springinaction.springidol.CriticAspect" factory-method="aspectOf"> <property name="criticismEngine" ref="criticismEngine"/> </bean>

一般情況下,Spring bean 由 Spring 容器初始化有序,但是 AspectJ 切面是由 AspectJ 在運行期創(chuàng)建的抹腿。所以在運行期間,AspectJ 創(chuàng)建好了 CriticAspect 實例旭寿,每個 AspectJ 都會提供一個靜態(tài)的 aspectOf() 方法警绩,返回切面的的單例。 使用 factory-method 調(diào)用 aspectOf() 方法向 CriticAspect 中注入 CriticismEngine 盅称。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肩祥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子缩膝,更是在濱河造成了極大的恐慌混狠,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疾层,死亡現(xiàn)場離奇詭異将饺,居然都是意外死亡,警方通過查閱死者的電腦和手機痛黎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門予弧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人湖饱,你說我怎么就攤上這事掖蛤。” “怎么了井厌?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵蚓庭,是天一觀的道長。 經(jīng)常有香客問我仅仆,道長器赞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任蝇恶,我火速辦了婚禮拳魁,結(jié)果婚禮上惶桐,老公的妹妹穿的比我還像新娘撮弧。我一直安慰自己,他們只是感情好姚糊,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布贿衍。 她就那樣靜靜地躺著,像睡著了一般救恨。 火紅的嫁衣襯著肌膚如雪贸辈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天肠槽,我揣著相機與錄音擎淤,去河邊找鬼奢啥。 笑死,一個胖子當著我的面吹牛嘴拢,可吹牛的內(nèi)容都是我干的桩盲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼席吴,長吁一口氣:“原來是場噩夢啊……” “哼赌结!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起孝冒,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤柬姚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后庄涡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體量承,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年穴店,在試婚紗的時候發(fā)現(xiàn)自己被綠了宴合。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡迹鹅,死狀恐怖卦洽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斜棚,我是刑警寧澤阀蒂,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站弟蚀,受9級特大地震影響蚤霞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜义钉,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一昧绣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧捶闸,春花似錦夜畴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至央碟,卻和暖如春税灌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工菱涤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留苞也,地道東北人。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓粘秆,卻偏偏與公主長得像墩朦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子翻擒,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

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

  • Spring致力于提供一種方法管理你的業(yè)務對象氓涣。在大量Java EE的應用中,隨處可見Spring陋气。今天我將簡單的...
    JAVA架構(gòu)師的圈子閱讀 1,324評論 0 16
  • 本章內(nèi)容: 面向切面編程的基本原理 通過POJO創(chuàng)建切面 使用@AspectJ注解 為AspectJ切面注入依賴 ...
    謝隨安閱讀 3,132評論 0 9
  • AOP實現(xiàn)可分為兩類(按AOP框架修改源代碼的時機): 靜態(tài)AOP實現(xiàn):AOP框架在編譯階段對程序進行修改巩趁,即實現(xiàn)...
    數(shù)獨題閱讀 2,310評論 0 22
  • **** AOP 面向切面編程 底層原理 代理Q魍妗!议慰! 今天AOP課程1蠢古、 Spring 傳統(tǒng) AOP2、 Spri...
    luweicheng24閱讀 1,359評論 0 1
  • 本文轉(zhuǎn)自黃勇 本文是《AOP 那點事兒》的續(xù)集别凹。 在上篇中草讶,我們從寫死代碼,到使用代理炉菲;從編程式Spring AO...
    樂百事52淑熙閱讀 324評論 0 1