類加載器與動态代理類:
一、類加載器:
在虛拟機運作時将class檔案加載到硬碟和記憶體中的工作就是由類加載器來完成的;JAVA虛拟中可以安裝多個類加載器,系統預設三個主要類加載器:
BootStrap,ExtClassLoader,AppLoader;每個類加載器負責加載特定位置的類;
類加載器也是Java類,是以其他類加載器也要被類加載器所加載,是以有一個類加載器必須不是類,這個就是BootStrap;
1、類加載器的委托機制:當需要加載一個類時目前現成的類加載器會首先向上傳遞加載請求,讓其父類加載器去加載這個類,其父類加載器也會向上傳遞請求直到請求BootStrap去加載;
如果Bootstrap無法加載到此類則向下轉發加載請求讓其子類加載器去加載,依此類推直到轉發到發起加載請求的類加載器自己去加載此類為止。若是無法加載則抛出異常
BootStrap ----------JRE/lib/rt.jar
ExtClassLoader ----------JRE/lib/Ext/*.jar
AppClassLoader ----------CLASSPATH指定的所有目錄下的所有JAR
自定義類加載器------------使用者指定的目錄下的類
2、自定義類加載器:
①使用者自己定義的類加載器必須繼承ClassLoader類
②覆寫findClass()方法(模闆設計模式,若是覆寫了loadClass方法,那麼使用者就必須自己定義向上傳遞加載請求等一系列功能的語句)
③覆寫defineClass()方法:用于将JAVA源代碼轉換為位元組碼
二、代理的概念與應用:
1、程式中的代理:
①要為已存在的多個具有相同接口的目标類的各個方法增加一些系統功能,例如異常處理,日志,計算方法的運作時間,事務管理等等;
②編寫一個與目标類具有相同接口的代理類,代理類的每個方法調用目标類的相同方法,并在調用方法時加上系統功能的代碼;
③如果采用工廠模式和配置檔案的方式進行管理,則不需要修改用戶端程式,在配置檔案中配置到底是使用目标類還是代理類
2、面向方面程式設計AOP:交叉業務的程式設計問題即為面向方面程式設計,AOP的目标是使交叉業務子產品化;
3、動态代理技術:
①JVM可以在運作期間動态的生成出類的位元組碼,這種動态的生成的往往被用作代理類,即動态代理類;
②JVM動态的生成的類必須實作一個或多個接口,是以動态生成的類之恩你更用作實作了相同接口的類的代理類;
③CGLIB庫可以動态的生成一個雷的子類,一個類的子類也可以用作該類的代理類,是以當一個沒有實作接口的類想要生成動态代理類,那麼可以使用這個庫
4、代理類的各個方法除了要調用目标的相應方法之外,還可以在代理方法中的如下四個位置加上系統功能代碼:
①在調用目标方法之前
②在調用目标方法之後
③在調用目标方法前後
④在處理目标方法異常的catch塊中
三、動态生成代理類:
1、動态生成一個類的位元組碼:
Proxy.getProxyClass(類加載器,需要實作的接口位元組碼)
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
2、列出生成的動态類的所有構造函數與方法:
package cn.ittest.Proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
public class ProxyDemo
{
public static void main(String[] args) throws Exception, NoSuchMethodException
{
//擷取Collection的動态代理
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
Constructor[] cs = clazzProxy.getConstructors(); //擷取動态代理類的所有構造方法
for(Constructor c : cs) //周遊構造方法
{
String name = c.getName(); //擷取構造方法的名字
StringBuffer sb = new StringBuffer(name);
sb.append("(");
Class[] clazzParams = c.getParameterTypes(); //擷取構造方法的類型參數
for(Class s : clazzParams)
{
sb.append(s).append(",");
}
if(c!=null)
sb.deleteCharAt(sb.length()-1);
sb.append(")");
System.out.println("構造方法:");
System.out.println(sb.toString()); //列印次構造方法
System.out.println("所有方法:");
}
//擷取Collection的動态代理
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
Method[] methods = clazzProxy1.getMethods(); //擷取動态代理類的所有方法
for(Method m : methods) //周遊方法
{
String name = m.getName(); //擷取方法的名字
StringBuffer sb1 = new StringBuffer(name);
sb1.append("(");
Class[] clazzParames = m.getParameterTypes(); //擷取方法的類型參數
for(Class s : clazzParames)
{
sb1.append(s).append(",");
}
if(m!=null)
sb1.deleteCharAt(sb1.length()-1);
sb1.append(")");
System.out.println(sb1.toString()); //列印次所有方法
}
}
3、擷取動态代理類
//擷取指定類的動态代理
public static Object getProxy(final Object object,final Mainboard mainboard)
{
Object relVal = Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new InvocationHandler()
{
public Object invoke(Object arg0, Method method,Object[] arg) throws Throwable
{
mainboard.opend(); //在調用代理類的每個方法時,加上自定義代碼
Object relVal = method.invoke(object,arg);
mainboard.close();
return relVal;
}
}
);
return relVal;
}
四、
了解InvocationHandler對象invoke方法的作用之後就知道如何添加代理功能,在具體實作中一般是将需要運作的代碼做成一個對象傳遞到匿名的InvocationHandler實作類中去
通常的方法是讓匿名InvocationHandler實作類去通路外面方法中的目标類執行個體對象的final類型的引用變量
這樣就需要建立一個對象,這個對象實作某個接口來确定其功能代碼,然後傳遞給InvocationHandler實作類