Spring的兩大特性是IOC和AOP IOC負責將對象動態的注入到容器,從而達到一種須要誰就注入誰,何時須要就何時注入的效果。理解spring的ioc也很重要。 可是今天主要來和你們講講aop。 AOP 普遍應用於處理一些具備橫切性質的系統級服務,AOP 的出現是對 OOP 的良好補充,用於處理系統中分佈於各個模塊的橫切關注點,好比事務管理、日誌、緩存等等。java
AOP代理主要分爲靜態代理和動態代理,spring
AspectJ是靜態代理的加強,採用編譯時生成 AOP 代理類,所以也稱爲編譯時加強,具備更好的性能。 缺點:但須要使用特定的編譯器進行處理數組
Spring AOP使用的動態代理,運行時生成 AOP 代理類,所謂的動態代理就是說AOP框架不會去修改字節碼,而是在內存中臨時爲方法生成一個AOP對象,這個AOP對象包含了目標對象的所有方法,而且在特定的切點作了加強處理,並回調原對象的方法。 缺點:因爲 Spring AOP 須要在每次運行時生成 AOP 代理,所以性能略差一些。緩存
因爲aspectj的使用還須要使用特定的編譯器進行處理,處理起來有點麻煩。今天重要來說解Spring AOPbash
Spring AOP動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理。框架
下面就來用簡單的代碼來演示下jdk和cglib動態代理的實現原理。maven
/**
* Created by qcl on 2018/11/29
* desc: jdk動態aop代理須要實現的接口
*/
public interface JdkInterface {
public void add();
}
複製代碼
/**
* Created by qcl on 2018/11/29
* desc: 被代理的類,即目標類target
*/
public class JdkClass implements JdkInterface {
@Override
public void add() {
System.out.println("目標類的add方法");
}
}
複製代碼
-3 ,到了關鍵的一步,用咱們的MyInvocationHandler,實現InvocationHandler接口,而且實現接口中的invoke方法。仔細看invoke方法,就是在該方法中加入切面邏輯的。目標類方法的執行是由mehod.invoke(target,args)這條語句完成。ide
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Created by qcl on 2018/11/29
* desc:這裏加入切面邏輯
*/
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before-------切面加入邏輯");
Object invoke = method.invoke(target, args);//經過反射執行,目標類的方法
System.out.println("after-------切面加入邏輯");
return invoke;
}
}
複製代碼
/**
* Created by qcl on 2018/11/29
* desc:測試
*/
public class JdkTest {
public static void main(String[] args) {
JdkClass jdkClass = new JdkClass();
MyInvocationHandler handler = new MyInvocationHandler(jdkClass);
// Proxy爲InvocationHandler實現類動態建立一個符合某一接口的代理實例
//這裏的proxyInstance就是咱們目標類的加強代理類
JdkInterface proxyInstance = (JdkInterface) Proxy.newProxyInstance(jdkClass.getClass().getClassLoader(),
jdkClass.getClass()
.getInterfaces(), handler);
proxyInstance.add();
//打印加強過的類類型
System.out.println("=============" + proxyInstance.getClass());
}
}
複製代碼
執行上面測試類能夠獲得以下結果 spring-boot
能夠看到,目標類的add方法先後已經加入了自定義的切面邏輯,AOP攔截機制生效了。再看class com.sun.proxy.$Proxy0。這裏進一步證實__JDK動態代理的核心是InvocationHandler接口和Proxy類__/**
* Created by qcl on 2018/11/29
* desc:要被代理的類
*/
public class Base {
public void add(){
System.out.println("目標類的add方法");
}
}
複製代碼
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by qcl on 2018/11/29
* desc:這裏加入切面邏輯
*/
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("before-------切面加入邏輯");
methodProxy.invokeSuper(object, args);
System.out.println("after-------切面加入邏輯");
return null;
}
}
複製代碼
/**
* Created by qcl on 2018/11/29
* desc:測試類
*/
public class CglibTest {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Base.class);
//回調方法的參數爲代理類對象CglibProxy,最後加強目標類調用的是代理類對象CglibProxy中的intercept方法
enhancer.setCallback(proxy);
//此刻,base不是單車的目標類,而是加強過的目標類
Base base = (Base) enhancer.create();
base.add();
Class<? extends Base> baseClass = base.getClass();
//查看加強過的類的父類是否是未加強的Base類
System.out.println("加強過的類的父類:"+baseClass.getSuperclass().getName());
System.out.println("============打印加強過的類的全部方法==============");
FanSheUtils.printMethods(baseClass);
//沒有被加強過的base類
Base base2 = new Base();
System.out.println("未加強過的類的父類:"+base2.getClass().getSuperclass().getName());
System.out.println("=============打印增未強過的目標類的方法===============");
FanSheUtils.printMethods(base2.getClass());//打印沒有加強過的類的全部方法
}
}
複製代碼
下面是打印結果 工具
經過打印結果能夠看到public class FanSheUtils {
//打印該類的全部方法
public static void printMethods(Class cl) {
System.out.println();
//得到包含該類全部其餘方法的數組
Method[] methods = cl.getDeclaredMethods();
//遍歷數組
for (Method method : methods) {
System.out.print(" ");
//得到該方法的修飾符並打印
String modifiers = Modifier.toString(method.getModifiers());
if (modifiers.length() > 0) {
System.out.print(modifiers + " ");
}
//打印方法名
System.out.print(method.getName() + "(");
//得到該方法包含全部參數類型的Class對象的數組
Class[] paramTypes = method.getParameterTypes();
//遍歷數組
for (int i = 0; i < paramTypes.length; i++) {
if (i > 0) {
System.out.print(",");
}
System.out.print(paramTypes[i].getName());
}
System.out.println(");");
}
}
}
複製代碼
注意:上面用到了cglib.jar和asm.jar。在咱們的maven的pom.xml裏引入下面類庫便可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
複製代碼