AOP(Aspect Oriented Programming):面向切面編程,指在程序運行期間動態的將某段代碼切入到指定方法指定位置進行運行的操做。如:性能監控、日誌記錄、權限控制等,經過AOP解決代碼耦合問題,讓職責更加單一。html
AOP技術它利用一種稱爲**「橫切」**的技術,剖解開封裝的對象內部,並將那些影響了多個類的公共行爲封裝到一個可重用模塊,並將其命名爲」Aspect」,即切面。所謂」切面」,簡單說就是那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減小系統的重複代碼,下降模塊之間的耦合度,並有利於將來的可操做性和可維護性java
爲了更好的理解aop的原理,咱們經過案例來一步步,首先,咱們先用spring框架,使用配置類註解方式注入bean,UserService做爲業務代碼:spring
@Service
public class UserService {
public void queryAll(){
System.out.println("業務代碼:查詢全部數據");
}
}
複製代碼
@Configuration
@ComponentScan("com.star")
public class AppConfig {
}
複製代碼
@Test
public void AOPTest(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
UserService bean = ac.getBean(UserService.class);
bean.queryAll();
}
複製代碼
咱們運行 AOPTest 方法,能夠看到控制檯打印出:數據庫
我們就在以上代碼的基礎上對功能進行加強。編程
如今咱們須要給業務代碼執行先後加上打印日誌,沒有aop的時候,我們能夠直接在 service 業務中增長相關方法進行加強:設計模式
@Service
public class UserService {
public void queryAll(){
System.out.println("before----業務代碼執行前打印日誌.....");
System.out.println("業務代碼:查詢全部數據");
System.out.println("after----業務代碼執行前打印日誌.....");
}
}
複製代碼
這樣一來,就把加強代碼和業務代碼放到了一塊兒,這是很不合理的,而且增長了耦合,不利於代碼的拓展。數組
所謂的動態代理,須要一個代理類,這個代理類是動態生成的,字節碼要用的時候就建立,要用的時候就加載,在不修改源碼的基礎上對方法進行加強。有兩種代理機制,一種是基於JDK的動態代理,另外一種是基於CGLib的動態代理,bean沒有接口時使用 CGLib 代理,bean有接口則使用 JDK 代理。因爲上面的案例中沒有使用接口,因此這裏用CGLib代理。markdown
有關動態代理能夠參考以前的博客:blog.csdn.net/One_L_Star/…框架
@Test
public void AOPTest1(){
final UserService bean = new UserService();
UserService cglibProducer = (UserService) Enhancer.create(bean.getClass(), new MethodInterceptor(){
/** * 做用:執行被代理對象的任何藉口方法都會通過該方法 * @param proxy:代理對象的引用 * @param method:當前執行的方法 * @param args:當前執行方法所需的參數 * @return:和被代理對象方法有相同的返回值 * @throws Throwable */
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("記錄日誌");
Object result = method.invoke(bean, args);
return result;
}
});
cglibProducer.queryAll();
}
複製代碼
執行後打印以下:ide
能夠看到,通過CGLib代理後,不修改業務代碼的基礎上,對方法進行了加強,而在spring aop 的底層,也是使用的動態代理,不過要遠遠複雜於上面的代碼,若是要深究,須要查看spring的源碼,這裏只講基本原理,源碼有點太費頭髮。
最後,我們來看看spring是如何加強的,AOP是一個標準規範,而爲了實現這個標準規範,有幾種方式:
這四種方式都是實現aop的方法,這裏講一下經過AspectJ提供的註解實現AOP,但在spring官網中,有AspectJ 的概念,主要是由於在spring2.x的時候,spring aop的語法過於複雜,spring想進行改進,而改進的時候就藉助了AspectJ 的語法、編程風格來完場aop的配置功能,這裏使用AspectJ 註解方式來實現。
在配置類中添加@EnableAspectJAutoProxy註解,開啓切面編程功能,添加後以下:
@Configuration
@ComponentScan("com.star")
@EnableAspectJAutoProxy
public class AppConfig {
}
複製代碼
使用@Aspect註解聲明一個切面,並使用@Before、@After等註解代表鏈接點
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.star.service..*.*(..))")
public void pointCut(){};
@Before("pointCut()")
public void logStart(){
System.out.println("查詢以前打印日誌....");
}
@After("pointCut()")
public void logEnd(){
System.out.println("查詢以後打印日誌....");
}
@AfterReturning("pointCut()")
public void logReturn(){
System.out.println("查詢以後正常返回....");
}
@AfterThrowing("pointCut()")
public void logException(){
System.out.println("查詢以後返回異常....");
}
}
複製代碼
@Test
public void AOPTest(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
UserService bean = ac.getBean(UserService.class);
bean.queryAll();
}
複製代碼
直接運行測試類,能夠看到對方法進行了加強
直接獲取一個代理對象 ,首先產生一個目標對象,而後對目標對象進行代理,返回代理對象,把目標對象放到了map中
在spring初始化的時候就已經完成了代理,也就是執行下面代碼的時候就完成了代理
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AopConfig.class);
複製代碼
術語的理解參考:yq.aliyun.com/articles/63…
在上面,咱們已經經過實例實現了經過AOP對方法進行加強,如今咱們來理解一下,首先,咱們必需要了解AOP的術語,這些術語在上面的AOP切面加強案例中都有體現,這裏結合案例來理解一下。
鏈接點是一個比較空泛的概念,就是定義了哪一些地方是能夠切入的,也就是全部容許你通知的地方。
切點就是定義了通知被應用的位置 (配合通知的方位信息,能夠肯定具體鏈接點)
通知(Advice):切入鏈接點的時機和切入鏈接點的內容稱爲通知,Spring切面能夠應用5種類型的通知:
前置通知(Before):在目標方法被調用以前調用通知功能;
後置通知(After):在目標方法完成以後調用通知,此時不會關心方法的輸出是什麼;
返回通知(After-returning):在目標方法成功執行以後調用通知;
異常通知(After-throwing):在目標方法拋出異常後調用通知;
環繞通知(Around):通知包裹了被通知的方法,在被通知的方法調用以前和調用以後執行自定義的行爲。
通知就定義了,須要作什麼,以及在某個鏈接點的何時作。 上面的切點定義了在哪裏作。
目標對象(Target):指的是被加強的對象,也就是被通知的對象,也就是真正的業務邏輯,在上面案例中,UserService就是目標對象
引介(Introduction):容許咱們向現有的類添加新方法屬性。經過引介,咱們能夠動態地爲該業務類添加接口的實現邏輯,讓業務類成爲這個接口的實現類。
織入(Weaving):織入是將通知添加到目標類具體鏈接點上的過程。AOP像一臺織布機,將目標類、通知或引介經過AOP這臺織布機完美無缺地編織到一塊兒。根據不一樣的實現技術,AOP有三種織入的方式:
編譯期織入,這要求使用特殊的Java編譯器。
類裝載期織入,這要求使用特殊的類裝載器。
動態代理織入,在運行期爲目標類添加通知生成子類的方式。
把切面應用到目標對象來建立新的代理對象的過程,Spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入。
切點的通知的結合,切面知道全部它須要作的事:什麼時候/何處/作什麼
原理實現參考:www.cnblogs.com/stateis0/p/…
【1】AOP的設計
【2】代理的建立
注意:建立代理對象時,同時會建立一個外層攔截器,這個攔截器就是 Spring 內核的攔截器。用於控制整個 AOP 的流程。
【3】代理的調用
如圖:
調用過程: