aop 實現原理(jdk動態代理和動態生成class)
阿新 • • 發佈:2019-02-19
java中可以通過jdk提供的 Proxy.newProxyInstance靜態方法來建立動態代理物件,下面先來看看這個方法的實現
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //InvocationHandler不能為空,因為對代理物件的所有方法呼叫實際上都會委託到InvocationHandler的invoke方法, //這個我們後面通過檢視產生的代理類的原始碼便會一目瞭然 if (h == null) { throw new NullPointerException(); } //這個是核心的地方,通過提供的ClassLoader和interface列表來產生代理類,具體的實現可以參考getProxyClass這個方法的實現, //真正的工作是由sun.misc.ProxyGenerator這個類來完成的,可以google檢視具體的邏輯.在我們的程式中通過設定 //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")可以檢視產生的類檔案 Class cl = getProxyClass(loader, interfaces); //因為代理類繼承了Proxy類.而Proxy中定義了建構函式protected Proxy(InvocationHandler h),所以可以反射得到Constructer例項 //建立代理物件 try { Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }
下面通過個例子來說明下:
先來定義一個介面,jdk的動態代理基於介面來建立代理類,不能基於類的原因是java不支援多重繼承,而代理類都會繼承Proxy類(個人理解).
/**
* Subject
*
* @author Kevin Fan
* @since 2013-9-13 下午2:43:33
*/
public interface Subject {
void pub(String key, String content);
String sub(String key);
}
再來一個具體的實現,在代理模式中可以叫它的例項可以叫target,這個是真正執行操作的物件
/** * SimpleSubject * * @author Kevin Fan * @since 2013-9-13 下午2:45:03 */ public class SimpleSubject implements Subject { private Map<String, String> msg = new ConcurrentHashMap<String, String>(); public void pub(String key, String content) { System.out.println("pub msg: key is " + key + ", content is " + content); msg.put(key, content); } public String sub(String key) { if (msg.containsKey(key)) { String ret = msg.get(key); System.out.println("sub msg: key is " + key + ", result is " + ret); return ret; } return null; } }
好,接下來我們來寫個動態代理工廠,根據 不同的target來建立動態代理物件[java] view plain copy print?
/**
* SubjectProxyFactory
*
* @author Kevin Fan
* @since 2013-9-13 下午2:47:24
*/
public class SubjectProxyFactory {
//TODO: cache
public static Subject getSubject(final Subject realSubject) {
return (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), new Class[] { Subject.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("\naction before method invocation....");
Object retVal = method.invoke(realSubject, args);
System.out.println("action after method invocation....\n");
return retVal;
}
});
}
}
可以看到這是一個簡單的實現,只是在真實物件執行前後各打一句資訊..
接下來用一個 main函式來把這些結合起來/**
* Demo
*
* @author Kevin Fan
* @since 2013-9-13 下午2:50:28
*/
public class Demo {
public static void main(String[] args) {
//設定此係統屬性,以檢視代理類檔案
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//建立真實物件
Subject subj = new SimpleSubject();
subj.pub("name", "kevin.fan");
subj.sub("name");
//建立代理物件
Subject proxy = SubjectProxyFactory.getSubject(subj);
proxy.pub("hobby", "r&b music");
proxy.sub("name");
}
}
pub msg: key is name, content is kevin.fan
sub msg: key is name, result is kevin.fan
action before method invocation....
pub msg: key is hobby, content is r&b music
action after method invocation....
action before method invocation....
sub msg: key is name, result is kevin.fan
action after method invocation....
可以看到在呼叫代理物件的方法時,新增的額外動作已經生效,接下來我們看下生成的代理類的程式碼..[java] view plain copy print?
import com.aliyun.demo.kevin.coder.lang.proxy.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//這裡很清楚了,代理類繼承了Proxy類,並且實現了Proxy.newProxyInstance這個方法中傳入的介面
public final class $Proxy0 extends Proxy
implements Subject
{
//這些方法在下面的static init block中進行初始化
private static Method m4;
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
static
{
try
{
m4 = Class.forName("com.aliyun.demo.kevin.coder.lang.proxy.Subject").getMethod("sub", new Class[] { Class.forName("java.lang.String") });
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.aliyun.demo.kevin.coder.lang.proxy.Subject").getMethod("pub", new Class[] { Class.forName("java.lang.String"), Class.forName("java.lang.String") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
//建構函式,接收一個 InvocationHandler作為引數,這就是為什麼Proxy.newProxyInstance方法裡可以
//通過InvocationHandler例項作為引數來反射獲取Constructer例項
public $Proxy0 paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//下面通過這個來看下代理物件中方法是怎樣呼叫的
public final String sub(String paramString)
throws
{
try
{
//全部是通過呼叫InvocationHandler的invoke方法,傳入對應的方法和引數
return (String)this.h.invoke(this, m4, new Object[] { paramString });
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void pub(String paramString1, String paramString2)
throws
{
try
{
this.h.invoke(this, m3, new Object[] { paramString1, paramString2 });
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
}
二 動態生成class檔案,newProxyInstance核心工具的一個示例。
import java.io.File;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class JavaCompilerUtil {
private static JavaCompiler javaCompiler;
private JavaCompilerUtil() {
};
private static JavaCompiler getJavaCompiler() {
if (javaCompiler == null) {
synchronized (JavaCompilerUtil.class) {
if (javaCompiler == null) {
// 講道理,根據JavaCompiler 的獲取方式來看,應該是採用了單例模式的,但是這裡為了順便複習一下單例模式,以及確保一下單例吧
javaCompiler = ToolProvider.getSystemJavaCompiler();
}
}
}
return javaCompiler;
}
//傳入真實的java檔案,需要替換成複製實現類的格式(反射原生的介面方法作為變數儲存,注入InvocationHandler介面型別物件方法,實現類方法替換成呼叫invoke
public static boolean CompilerJavaFile(String sourceFileInputPath,
String classFileOutputPath) {
// 設定編譯選項,配置class檔案輸出路徑
Iterable<String> options = Arrays.asList("-d", classFileOutputPath);
StandardJavaFileManager fileManager = getJavaCompiler()
.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager
.getJavaFileObjectsFromFiles(Arrays.asList(new File(
sourceFileInputPath)));
return getJavaCompiler().getTask(null, fileManager, null, options,
null, compilationUnits).call();
}
}