AOP前景提要
提出問題
①代碼混亂:越來越多的非業務需求(日誌和驗證等)加入後,原有的業務方法急劇膨脹。每個方法在處理核心邏輯的同時還必須兼顧其他多個關注點。
②代碼分散: 以日誌需求爲例,只是爲了滿足這個單一需求,就不得不在多個模塊(方法)裏多次重複相同的日誌代碼。如果日誌需求發生變化,必須修改所有模塊。
動態代理
- 代理設計模式的原理:使用一個代理將對象包裝起來,然後用該代理對象取代原始對象。任何對原始對象的調用都要通過代理。代理對象決定是否以及何時將方法調用轉到原始對象上。
- 代理的方式
- 基於接口的動態代理:JDK動態代理
Proxy:所有動態代理類的父類,專門用戶生成代理類或者代理對象
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
用於生成代理類的Class對象.
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
用於生成代理對象
InvocationHandler :完成動態代理的整個過程.
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
附上代碼:
public interface ArithmeticCalculator {
public int add(int i,int j);
public int sub(int i,int j);
public int mul(int i,int j);
public int div(int i,int j);
}
public class ArithmeticCalculatorImpl implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
int result = i + j ;
return result ;
}
@Override
public int sub(int i, int j) {
int result = i - j ;
return result ;
}
@Override
public int mul(int i, int j) {
int result = i * j ;
return result ;
}
@Override
public int div(int i, int j) {
int result = i / j ;
return result ;
}
}
創建InvocationHandler類,實現InvocationHandler接口,這個類中持有一個被代理對象的實例target。InvocationHandler中有一個invoke方法,所有執行代理對象的方法都會被替換成執行invoke方法。再在invoke方法中執行被代理對象target的相應方法。當然,在代理過程中,我們在真正執行被代理對象的方法前加入自己其他處理。這也是Spring中的AOP實現的主要原理,這裏還涉及到很重要的關於java反射方面的基礎知識。
public class ArithmeticCalculatorProxy {
//目標對象
private ArithmeticCalculator target ;
public ArithmeticCalculatorProxy(ArithmeticCalculator target) {
this.target = target ;
}
//獲取代理對象的方法
public Object getProxy() {
//代理對象
Object proxy ;
/**
* loader: ClassLoader對象。 類加載器對象. 幫我們加載動態生成的代理類。
*
* interfaces: 接口們. 提供目標對象的所有的接口. 目的是讓代理對象保證與目標對象都有接口中想同的方法.
*
* h: InvocationHandler類型的對象.
*/
ClassLoader loader = target.getClass().getClassLoader();
Class [] interfaces = target.getClass().getInterfaces();
//執行newProxyInstance不會調用invoke
proxy = Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
/**
* invoke: 代理對象調用代理方法, 會回來調用invoke方法。
*
* proxy: 代理對象 , 在invoke方法中一般不會使用.
*
* method: 正在被調用的方法對象.
*
* args: 正在被調用的方法的參數.
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//將方法的調用轉回到目標對象上.
//獲取方法的名字
String methodName = method.getName();
//記錄日誌
System.out.println("LoggingProxy==> The method " + methodName+" begin with "+ Arrays.asList(args));
Object result = method.invoke(target, args); // 目標對象執行目標方法. 相當於執行ArithmeticCalculatorImpl中的+ - * /
//記錄日誌
System.out.println("LoggingProxy==> The method " + methodName +" ends with :" +result );
return result ;
}
});
return proxy ;
}
}
JDK動態代理的另外一種方式
public class ArithmeticCalculatorProxy2 {
//動態代理: 目標對象 如何獲取代理對象 代理要做什麼
//目標對象
private ArithmeticCalculator target ;
public ArithmeticCalculatorProxy2(ArithmeticCalculator target) {
this.target = target ;
}
//獲取代理對象的方法
public Object getProxy() throws Exception {
//代理對象
Object proxy ;
ClassLoader loader = target.getClass().getClassLoader();
Class [] interfaces = target.getClass().getInterfaces();
Class proxyClass = Proxy.getProxyClass(loader, interfaces);
//Class 創建對象? newInstance()
Constructor con =
proxyClass.getDeclaredConstructor(InvocationHandler.class);
proxy = con.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//將方法的調用轉回到目標對象上.
//獲取方法的名字
String methodName = method.getName();
//記錄日誌
System.out.println("LoggingProxy2==> The method " + methodName+" begin with "+ Arrays.asList(args));
Object result = method.invoke(target, args); // 目標對象執行目標方法. 相當於執行ArithmeticCalculatorImpl中的+ - * /
//記錄日誌
System.out.println("LoggingProxy2==> The method " + methodName +" ends with :" +result );
return result ;
}
});
return proxy ;
}
}
創建代理對象並進行測試
public static void main(String[] args) {
//將動態生成的proxy保存下來
Properties properties = System.getProperties();
properties.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//目標對象
ArithmeticCalculator target =new ArithmeticCalculatorimpl();
//獲取代理對象
Object obj=new Proxy(target).getProxy();
ArithmeticCalculator arithmeticCalculator=(ArithmeticCalculator)obj;
System.out.println(arithmeticCalculator.getClass().getName());
System.out.println(arithmeticCalculator.add(1,2));
}
測試結果如下
生成的Proxy
package com.atguigu.spring.aop.proxy;
import com.atguigu.spring.aop.proxy.ArithmeticCalculator;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy00 extends Proxy implements ArithmeticCalculator {
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m3;
private static Method m6;
private static Method m5;
private static Method m0;
public $Proxy00(InvocationHandler arg0) throws {
super(arg0);
}
public final boolean equals(Object arg0) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{arg0})).booleanValue();
//這裏又返回了InvocationHandler中的invoke方法
} catch (RuntimeException | Error arg2) {
throw arg2;
} catch (Throwable arg3) {
throw new UndeclaredThrowableException(arg3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error arg1) {
throw arg1;
} catch (Throwable arg2) {
throw new UndeclaredThrowableException(arg2);
}
}
public final int mul(int arg0, int arg1) throws {
try {
return ((Integer)super.h.invoke(this, m4, new Object[]{Integer.valueOf(arg0), Integer.valueOf(arg1)})).intValue();
} catch (RuntimeException | Error arg3) {
throw arg3;
} catch (Throwable arg4) {
throw new UndeclaredThrowableException(arg4);
}
}
public final int add(int arg0, int arg1) throws {
try {
return ((Integer)super.h.invoke(this, m3, new Object[]{Integer.valueOf(arg0), Integer.valueOf(arg1)})).intValue();
} catch (RuntimeException | Error arg3) {
throw arg3;
} catch (Throwable arg4) {
throw new UndeclaredThrowableException(arg4);
}
}
public final int sub(int arg0, int arg1) throws {
try {
return ((Integer)super.h.invoke(this, m6, new Object[]{Integer.valueOf(arg0), Integer.valueOf(arg1)})).intValue();
} catch (RuntimeException | Error arg3) {
throw arg3;
} catch (Throwable arg4) {
throw new UndeclaredThrowableException(arg4);
}
}
public final int div(int arg0, int arg1) throws {
try {
return ((Integer)super.h.invoke(this, m5, new Object[]{Integer.valueOf(arg0), Integer.valueOf(arg1)})).intValue();
} catch (RuntimeException | Error arg3) {
throw arg3;
} catch (Throwable arg4) {
throw new UndeclaredThrowableException(arg4);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error arg1) {
throw arg1;
} catch (Throwable arg2) {
throw new UndeclaredThrowableException(arg2);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.atguigu.spring.aop.proxy.ArithmeticCalculator").getMethod("mul",
new Class[]{Integer.TYPE, Integer.TYPE});
m3 = Class.forName("com.atguigu.spring.aop.proxy.ArithmeticCalculator").getMethod("add",
new Class[]{Integer.TYPE, Integer.TYPE});
m6 = Class.forName("com.atguigu.spring.aop.proxy.ArithmeticCalculator").getMethod("sub",
new Class[]{Integer.TYPE, Integer.TYPE});
m5 = Class.forName("com.atguigu.spring.aop.proxy.ArithmeticCalculator").getMethod("div",
new Class[]{Integer.TYPE, Integer.TYPE});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException arg1) {
throw new NoSuchMethodError(arg1.getMessage());
} catch (ClassNotFoundException arg2) {
throw new NoClassDefFoundError(arg2.getMessage());
}
}
}
結束語
代理對象能否轉換成目標對象的類型?
代理對象調用代理方法,爲什麼會執行 InvocationHandler中的invoke 方法?