0x00 AOP是什么
AOP (Aspect Oriented Programming)
面向切面編程憎亚,Spring是通過Java動態(tài)代理實現(xiàn)的燃乍。
AOP的目的是為了將組件和業(yè)務代碼分離開帘靡,讓日志领突、事務處理材彪、緩存碱蒙、性能統(tǒng)計、權限控制等等模塊跟實際的業(yè)務模塊解耦孕索。
這么說還是很籠統(tǒng)逛艰,Java的類中,屬性和方法才是主流搞旭,切面是什么散怖,完全沒這個概念。
那么以拍電影舉個栗子:
1.前期籌備肄渗,劇本確定镇眷、演員試鏡確定
############分割線a
2. 開拍、剪輯翎嫡、制作
############分割線b
3. 營銷欠动、宣傳、公映
1->2->3這個流程是拍電影的業(yè)務流程,但是如果我要拍花絮怎么辦具伍,這個流程不屬于現(xiàn)在的電影業(yè)務流程翅雏,我們需要安排一個零時的業(yè)務,那我們就要在############分割線a
分割線那里添加攝花絮的計劃人芽,分配一些人員望几。
那么分割線那里,就是切面啼肩。這些切面干什么橄妆,由切面代理去管理和運行,這就是AOP祈坠。
0x01 AOP的用途
來說說為什么要用AOP:
AOP中面向切面編程的這個切面就是針對流程的害碾。當某些服務與業(yè)務無關,比如日志系統(tǒng)赦拘,日志系統(tǒng)與現(xiàn)實業(yè)務應該是相互獨立的慌随,但是,在編寫代碼的時候我們需要這些日志來調試定位程序執(zhí)行的位置躺同。
這些日志代碼如果直接寫在程序里會擾亂原本的業(yè)務模塊阁猜,會影響到系統(tǒng)的持續(xù)集成
因此我們希望日志系統(tǒng)能夠獨立于業(yè)務代碼,但是每當業(yè)務執(zhí)行到某個位置時蹋艺,日志又會輸出剃袍。這就要借助于AOP了(想象一下,業(yè)務代碼被切開了捎谨,中間插入了日志執(zhí)行的標記民效,走到標記時就會輸出日志,這個標記就是切面)
當然涛救,還有業(yè)務中登錄狀態(tài)驗證畏邢,權限檢查,全局的事務處理检吆,共用的某些模塊舒萎,都可以用AOP來實現(xiàn),這是OOP的進步
0x02 代理模式
Java動態(tài)代理是基于代理模式的一種實現(xiàn)蹭沛,可以查詢一下代理模式臂寝,這里將動態(tài)代理的對應關系圖貼一下
我們通過代理接口Subject去執(zhí)行RealSubject的subjectMethod()方法,而java的動態(tài)代理是摊灭,通過InvocationHandler中invoke()方法去調用Proxy事先生成好實例對象咆贬。說的有點繞口,配合看圖斟或。
再不明白:可以看我的:(FS計劃)Java里的動態(tài)代理模式和代理方法增強
或者:看別人的詳細介紹
0x03 Spring里的動態(tài)代理
因為Spring中擁有了DI的高級特性,所以在Spring中實現(xiàn)動態(tài)代理是可以依靠配置Bean來實現(xiàn)的集嵌。Spring中的動態(tài)代理依賴下面幾個接口和類
import org.springframework.aop.BeforeAdvice;//前置通知
import org.springframework.aop.AfterAdvice;//后置通知
import org.springframework.aop.ThrowsAdvice;//異常通知
import org.aopalliance.intercept.MethodInterceptor;//環(huán)繞通知
import org.springframework.aop.IntroductionAdvisor;//引介通知
可以寫一個例子來看看
- 接口
package com.zing.aoptest;
public interface IRocketLaunching {
boolean launch();
}
- 目標對象
package com.zing.aoptest;
public class RocketLaunchingImpl implements IRocketLaunching {
@Override
public boolean launch() {
System.out.println("倒計時:987654321 發(fā)射萝挤!");
return false;
}
}
- 織入類
package com.zing.aoptest;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class PrepareForLaunching implements MethodBeforeAdvice {
public void before(Method method, Object[] params, Object obj) throws Throwable {
System.out.println("給火箭家燃料\n");
}
}
- 代理對象
交給Spring 來做
<!--前置增強類-->
<bean id="beforeLaunch" class="com.zing.aoptest.PrepareForLaunching"></bean>
<!--目標類-->
<bean id="launchingControl" class="com.zing.aoptest.RocketLaunchingImpl"></bean>
<!--工廠-->
<!--optimize為true表示使用CGLib御毅,false為Java動態(tài)代理。默認為false-->
<!--當織入對象需要使用單例模式的時候怜珍,optimize建議為true端蛆,這樣比較節(jié)省資源-->
<!--target表示需要代理的類,會為這個目標織入前置增強和后置增強-->
<bean id="rocketProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="launchingControl"></property>
<property name="proxyInterfaces" value="com.zing.aoptest.IRocketLaunching"></property>
<property name="interceptorNames" value="beforeLaunch"></property>
<property name="proxyTargetClass" value="true"></property>
</bean>
- 測試一下
package com.zing.aoptest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;
public class AOPTest {
@Test
public void AopTest() {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-dao.xml");
RocketLaunchingImpl rocketLaunching = (RocketLaunchingImpl) context.getBean("rocketProxy");
rocketLaunching.launch();
}
}
測試結果
0x04 Spring 里的AOP簡單介紹
Spring的AOP是基于JDK的代理模式和CGLib為技術基礎酥泛,完成織入橫切代碼的一項功能今豆。
朕要去擼一個AOP的代碼例子:
例子好了,但是依舊沒搞清楚AOP配置的語法柔袁,先不急
放上例子
package com.zing.aspect_oriented_test;
/**
* 方法切面
*/
public class SubjectService implements InterfaceSubjectService {
@Override
public void AspectMethod(String str) {
System.out.println("yo yo yo!\t" + str);
}
}
package com.zing.aspect_oriented_test;
/**
* 對切面的處理呆躲,之前與之后
*/public class AspectTarget {
public void beforeYouTalk() {
System.out.println("************beforeYouTalk, I know everything,so do not lie!");
}
private void afterYouTalk() {
System.out.println("************afterYouTalk, ha ha ha,good boy");
}
}
我們知道了切面和切面處理方法,接下來告訴Spring捶索,切面位置和處理方法
在XML配置文件中將兩個bean配置到IoC容器中
<bean id="aspectService" class="com.zing.aspect_oriented_test.SubjectService"></bean>
<bean id="aspect" class="com.zing.aspect_oriented_test.AspectTarget"></bean>
再配置切面和切面方法
<aop:config>
<aop:pointcut id="pointCut" expression="execution(* com.zing...*.*(..))"></aop:pointcut>
<aop:aspect ref="aspect">
<aop:before pointcut-ref="pointCut" method="beforeYouTalk"></aop:before>
<aop:after pointcut="execution(* com.zing..*.*(..))" method="afterYouTalk"></aop:after>
</aop:aspect>
</aop:config>
上面的配置可能不太明白插掂,因為用的是AspectJ語法,* com.zing..*.*(..)
表示com.zing
包下的所有子包的類與方法
例子擼完腥例,寫個Junit測試一下
package com.zing.aspect_oriented_test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by zing on 16/4/23.
*/
public class AspectJunitTest {
@Test
public void AopTest(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
InterfaceSubjectService subjectService = applicationContext.getBean("aspectService",InterfaceSubjectService.class);
subjectService.AspectMethod("Monster");
}
}
運行結果
************beforeYouTalk, I know everything,so do not lie!
yo yo yo! Monster
************afterYouTalk, ha ha ha,good boy
代碼沒問題辅甥,(全程注意你引得包不要出問題,可以參考我之前的小白文IDEA 構建SpringMVC+MAVEN項目)下面聊一下AOP的配置
吐槽:覺得XML配置好煩人
這篇再寫就累贅了燎竖,算了璃弄,這個Spring的動態(tài)代理的寫法并不常用,配置寫起來也異常麻煩构回,后面會詳細介紹AOP的常規(guī)使用方法夏块,敬請期待。