Spring AOP的底層實現原理


Spring的兩大核心之一就是AOP,AOP:面向切面編程。在說原理之前,得先知道一些 AOP的專業術語。

AOP的專業術語

連接點(JoinPoint):增強執行的位置(增加代碼的位置),Spring只支持方法;
切點(PointCut):具體的連接點;一般可能通過一個表達式來描述;
增強(Advice):也稱為消息,指的是增加的額外的代碼,Spring中,增強除了包含代碼外,還包含位置信息;


Spring中一共有四種增強:

  • MethodBeforeAdvice:前置增強
  • MethodInterceptor:環繞增強
  • ThrowsAdvice:異常增強
  • AfterReturingAdvice:返回值增強

引介(Introduction):特殊的增強,動態為類增加方法
織入(Weaving):將增強加入到目標類的過程,織入分為三種時期

  • 編譯器:AspectJ
  • 類加載
  • 運行期:jdk動態代理(實現接口),CGlib(子類,不能用final)

目標對象(Target):原始對象
代理對象(Proxy):加入了增強的對象,是生成的;
切面(Aspect):切點+增強

接下來我要說的就是在運行期間織入的兩種實現方式

JDK動態代理(實現接口)

什么是代理模式呢?

代理模式有三個角色,分別是

  • 抽象角色:接口
  • 目標角色:實現類
  • 代理角色:實現接口(InvocationHandler),並引用目標角色

代理模式與裝飾者模式的區別
類圖(結構)基本一樣,但目的不同,裝飾模式的前提是已經所有的類,並進行組裝;
而使用代理模式時,我們不能直接訪問目標角色或沒有權限訪問時,可以使用代理模式

代理模式分為兩種

  • 靜態代理:需要為每個目標角色,創建一個對應的代理角色;類的數量會急劇膨脹
  • 動態代理:自動為每個目標角色生成對應的代理角色
  1. 接下來就是jdk動態代理的代碼:

實現jdk動態代理的前提是所有的目標類都必須要基於一個統一的接口

創建統一的接口
[java] view plain copy
package com.dao;
/**
* 為目標類定義統一的接口SleepDao
* @author XuXQ
*
*/
public interface SleepDao {
  public void sleep();
}

定義目標對象
[java] view plain copy
package com.daoImpl;
import com.dao.SleepDao;
/**
* 目標類
* @author XuXQ
*
*/
public class SleepDaoImpl implements SleepDao {

@Override
public void sleep() {
System.out.println("本大人要睡覺了");
}

}

創建代理角色

[java] view plain copy
package com.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 創建的類得需要實現InvocationHandler,並重寫invoke()方法
* InvocationHandler 是代理實例的調用處理程序 實現的接口
* 即:MyInvocationHandler 是代理實例的調用處理程序
* @author XuXQ
*
*/
public class MyInvoctionHandler implements InvocationHandler {
Object object=null;//目標對象

public MyInvoctionHandler(Object object) {
super();
this.object = object;
}

/**
* proxy=代理對象
* method=被調用方法的方法名
* args=被調用方法的參數
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
System.out.println("睡覺前要脫衣服啊");
Object obj=method.invoke(object, args);//obj為目標對象調用方法的返回

System.out.println("睡着了當然得做個美夢啊");
return obj;
}

}


[java] view plain copy
編寫測試用例
[java] view plain copy
package com.handler;
[java] view plain copy
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.dao.SleepDao;
import com.daoImpl.SleepDaoImpl;
public class Test1 {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void test() {
//目標類必須基於統一的接口
SleepDao s=new SleepDaoImpl();
ClassLoader classLoader=s.getClass().getClassLoader();
MyInvoctionHandler myInvoctionHandler=new MyInvoctionHandler(s);

//Proxy為InvocationHandler實現類動態創建一個符合某一接口的代理實例

SleepDao sd=(SleepDao) Proxy.newProxyInstance(classLoader,
s.getClass().getInterfaces(), myInvoctionHandler);

//相當於調用代理角色的Invoke()
sd.sleep();
}

}

結果:

 由結果可以看到成功的將增強織入到了目標對象中了

 

此處使用到了jdk的動態代理,invocationHandler,proxy,以及classloader在另外兩篇文章有詳解

InvocationHandler和Proxy(Class)的動態代理機制詳解

http://www.cnblogs.com/shoshana-kong/p/9041485.html

ClassLoader工作機制

http://www.cnblogs.com/shoshana-kong/p/9042013.html

2.CGlib代理

[java] view plain copy
package com.cglibproxy;
/**
* 和JDK動態代理不同,不需要創建統一的接口
* @author XuXQ
*
*/
public class Base {
public void sleep(){
System.out.println("本大少要睡覺啦");
}
}<strong>
</strong>

創建cglib的代理對象
[java] view plain copy
package com.cglibproxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;


//用來生成代理對象
public class CglibProxy implements MethodInterceptor {

public Object getProxy(Object object){
Enhancer e=new Enhancer();//創建代理對象類
e.setSuperclass(object.getClass());//聲明代理對象的父類是誰(是目標
對象)
e.setCallback(this);//設置回調函數,即調用intercept()
return e.create();//返回創建的代理對象
}
/**
<span style="white-space:pre;"> </span> * proxy=代理對象,也是目標對象的子類
<span style="white-space:pre;"> </span> * args=方法參數
<span style="white-space:pre;"> </span> */
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
System.out.println("你個大懶豬,竟然睡覺前不脫衣服,嫌棄o");
Object object=arg3.invokeSuper(proxy, args);
System.out.println("起床啦,要不然得遲到了哦");
return null;
}

}

編寫測試用例
[java] view plain copy
package com.cglibproxy;

import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;

public class Test {

@Before
public void setUp() throws Exception {
}

@After
public void tearDown() throws Exception {
}

@org.junit.Test
public void test() {
CglibProxy proxy=new CglibProxy();
Base base=(Base) proxy.getProxy(new Base());
base.sleep();
}

}

結果:

 


由結果同樣可以看到成功的將增強織入到了目標對象中了

此處涉及cglib相關知識

Cglib及其基本使用

 

http://www.cnblogs.com/shoshana-kong/p/9041834.html

 

總結
當然我們以后寫代碼都是基於Aspect,直接寫注解的,當然這並不代表這我們不需要知 道AOP
底層的實現原理,至於注解用的是哪種實現方式,使用配置來解決的,這里就不詳解了 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM