載類的工具-----類加載器
✔java虛拟機中可以安裝多個類加載器,系統預設三個主要類加載器
每個類負責加載特定位置的類
BootStrap,ExtClassLoader,AppClassLoader
✔類加載器也是java類,因為其他是java類的類加載器本身也要被類加載器加載,顯然必須有第一個類加載器不java類,這正是BootStrap
✔java虛拟機中的所有類加載器采用具有斧子關系的樹形結構進行
組織,在執行個體化每個類加載器對象時,需要為其指定一個夫級類
加載器對象或者預設采用系統類加載器為其夫級類加載
類加載器之間的父子關系和管轄範圍
注意:如果AppClassLoader(兒子)下和ExClassLoader(爸爸)下,都有一個共同的.class檔案,
那麼,優先去加載的是ExClassLoader(爸爸)…其實這個就是下面的委托機制….
通常不可以,因為有委托機制存在,每次加載都是Bootstrap加載rt包中的System類.但是,我也有辦法,自己可以寫一個加載器.讓他挂在AppClassLoader上,指定他去加載我編寫的System
java.lang
類 ClassLoader
内部會先用loadClass方法,去找他的爸爸.看爸爸能不能幹.
Class<?> | loadClass(String name) 使用指定的二進制名稱來加載類。 |
當,爸爸傳回來資訊說不能幹後,再調用findClass方法自己幹
protected Class<?> | findClass(String name) 使用指定的二進制名稱查找類。 |
這是一種模版方法設計模式(了解):
(ClassLoader)父類中有àloadClass()&findClass()&definClass()[得到class檔案轉換成位元組碼]
|----子類A(自己幹隻需覆寫findClass,局部細節自己幹)
|----子類B(自己幹覆寫findClass,不覆寫loadClass是為了保留,流程)
總體的流程,在父類中已經規定好了,而有一些細節,就空出來,留給子類去覆寫.
子類A和子類B的流程是一樣的.但是子類A和B自己幹的方法都各不相同.
package cn.itcast.day2;
import java.util.Date;
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
System.out.println(//檢視該類是由那一個類加載器加載的
"該類是由"+ClassLoaderTest.class.getClassLoader().getClass().getName()+"類加載器,加載進來的");
//引出Bootstrap加載器:
//下面語句會報出空指針異常,一看到空指針異常就,應該想到"."(點)前的東西為"null"
//System.out.println(//java.lang.NullPointerException
//System.class.getClassLoader().getClass().getName());
//是以,起初判斷.getName()前面的.getClass()為null.但再想一下,隻有一個java對象,
//就一定是一個Class搞出來的,這裡是不可能的.---是以,再往前推,有可能
//getClassLoader()這個根就等于null.實踐如下:
System.out.println(
System.class.getClassLoader());//result:null
//從這個例子中得知,System,并不是,沒有加載器,而是證明了他,是有一個特殊的加載器(Bootstrap)加載的
//引出Bootstrap,ExtClassLoader,AppClassLoader三個加載器之間的關系.
//定義一個[ClassLoader]對象,得到的是那個AppClassLoader
ClassLoader loader=ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());//列印出基層的類加載器
loader=loader.getParent();//将基層類加載器的父親覆寫給基層
}
System.out.println(loader);//上面循環,是以前從孫子-->爸爸--->爺爺(null)
//列印出加密後的類,來看看效果.
// System.out.println(new ClassLoaderAttachment().toString());
//-------------注意這裡,父類是AppClassLoader,他隻能加載ClassPaht下的,是以要加上包名
Class clazz=new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");
//這裡不能直接用那個加密過的類,因為系統識别不了,一堆亂碼.是以,這裡提前內建的Date父類就起作用了.
Date d1=(Date)clazz.newInstance();
System.out.println(d1);
}
}
代理
代理的概念與作用
程式中的代理
要為已存在的多個具有相同接口的目标類的各個方法增加一些系統功能,例如,異常處理,日志,計算方法的運作時間,事務管理,等等
編寫一個與目标類具有相同接口的代裡類,代理類的每個方法調用目标類的相同方法,并在調用方法時加上系統功能的代碼
如果采用工廠模式和配置檔案的方式進行管理,則不需要修改用戶端程式,在配置檔案中配置是使用目标類,還是代理類,這樣以後很容易切換,譬如,想要日志功能時就配置代理類,否則配置目标類,這樣,增加系統功能很容易,以後運作一段事件後,又想去掉系統功能也很容易
AOP(面向方面的程式設計語言)
系統中存在交叉業務,一個交叉業務就是要切入到系統中的一個方面,如下所示:
安全 事物 日志
StudentService -------|--------|--------|-----
CourseService -------|------- |--------|-----
MiscService -------|--------|--------|-----
具體的程式代碼描述交叉業務:
Method1 method2 method3
{ { {
-------------------------------------切面
…… ……. ………..
-------------------------------------切面
} } }
交叉業務的程式設計問題即為面向方面的程式設計(Aspect oriented program 簡稱AOP),AOP的目标就是要使交叉業務子產品化,可以采用将切面代碼移動到原始方法的周圍,這與直接在方法中編寫切面代碼的運作效果是一樣的,如下所示
-------------------------------------切面
Func1 func2 func3
{ { {
…….. …….. …….
} } }
-------------------------------------切面
使用代理技術正好可以解決這種問題,代理是實作AOP功能的核心和關鍵技術
動态代理技術
l 要為系統中的各種接口的類增加代理功能,那将需要太多的代理類,全部采用靜态代理方式,将是一件非常麻煩的事情,寫成百上千的代理類,很痛苦
l JVM可以在運作期動态生成出類的位元組碼(以前都是手動用javac編譯出class檔案,現在JVM自動的生成位元組碼),這種動态生成的類往往被用作代理類,即動态代理類
l JVM生成的動态類必須實作一個或多個接口,是以,JVM生成的動态類隻能用作具有相同接口的目标類的代理
l CGLIB庫可以動态生成一個類的子類,一個類的子類也可以用作該類的代理,是以,如果要為一個沒有實作接口的類生成動态代理類,那麼可以使用CGLIB庫
l 代理類的各個方法中通常除了要調用目标的相應方法和對外傳回目标傳回的結果外,還可以在代理方法中的如下四個位置加上系統功能代碼:
1. 在調用目标方法之前
2. 在調用目标方法之後
3. 在調用目标方法前後
4. 在處理目标方法異常的catch塊中
分析JVM動态生成的類
1.建立實作了Collection接口的動态類和檢視其方法名稱
在java.lang.reflect.Proxy類中有個getProxyClass()方法:傳回class位元組碼,就是造出一個類 ,此方法需要指定類加載器和實作的接口(此例子實作的接口是Collection),而使用的類加載器通與接口的一樣,
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
參數:
loader - 定義代理類的類加載器
interfaces - 代理類要實作的接口清單
傳回:
用指定的類加載器定義的代理類,它可以實作指定的接口
使用Class類中的getProxyClass方法擷取方法的所有所有參數
列出動态類中的所有構造方法和參數簽名
列出動态類中的所有方法和參數簽名
2.建立動态類的執行個體對象(三種方式)
1.因為沒有無參的構造方法,是以需要先擷取有參的構造方法
用反射獲得構造方法
getConstructor方法擷取了構造方法,再通過通過構造方法Constructor類中的newInstance方法,來建立對象,因為在newInstance中需要傳入參數的類型是此構造方法的實際類型(InvocationHandler對象),而InvocationHandler是一個接口,
不能new對象,隻能先建立一個類實作這個接口
編寫一個是簡單的實作InvocationHandler接口的類
調用構造方法建立動态類的執行個體對象,并将編寫的invocationHandler類的執行個體 對象傳進去
2.也可以将建立動态類執行個體對象的代理改成匿名内部類的形式編寫,
3.用Proxy.newInstance方法直接一步就就能建立代理對象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
參數:
loader - 定義代理類的類加載器
interfaces - 代理類要實作的接口清單
h - 指派方法調用的調用處理程式(就是實作的接口對象)
傳回:
一個帶有代理類的指定調用處理程式的代理執行個體,它由指定的類加載器定義,并實作指定的接口
package cn.itcast.day3;
//reflect[映射]
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
//代理
public class ProxyTest {
public static void main(String[] args)throws Exception {
//*****用jvm虛拟機生成一個動态類....用到 Proxy.getProxyClass()
//類加載器通常用和這個類接口相同的加載器
//一般字首為clazz的變量,都是一份位元組碼
Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());//$Proxy0
//列印規定格式化的所有構造方法.
System.out.println("-----------------begin constructors list-----------------");
/**$Proxy0()
$Proxy0(InvocationHandler,int)*/
//這個類上有什麼構造方法?
//getConstructors()獲得,該類中所有的構造方法.以數組方式存放.
Constructor[] constructors=clazzProxy1.getConstructors();
//周遊構造方法,數組.
for(Constructor constructor:constructors){
String name=constructor.getName();//擷取構造方法的名字,并存儲
StringBuilder sBuilder=new StringBuilder(name);
sBuilder.append("(");//格式"("
/*Class<?>[] getParameterTypes()
按照聲明順序傳回一組 Class 對象,這些對象表示此 Constructor對象所表示構造方法的形參類型。*/
Class[] clazzParams=constructor.getParameterTypes();
//周遊形參類型
for(Class clazzParam: clazzParams){
sBuilder.append(clazzParam.getName()).append(",");//一個參數後加一個逗号..格式
}
//在這判斷下,因為,有可能該構造方法,沒有參數
if(clazzParams.length!=0)
//删除最後一個多餘的","逗号
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(")");//格式")"
System.out.println(sBuilder.toString());
}
//列印出所有的方法
System.out.println("-----------------begin methods list-----------------");
/**$Proxy0()
$Proxy0(InvocationHandler,int)*/
//這個類上有什麼構造方法?
Method[] methods=clazzProxy1.getMethods();
//周遊方法數組
for(Method method:methods){
String name=method.getName();
StringBuilder sBuilder=new StringBuilder(name);
sBuilder.append("(");
Class[] clazzParams=method.getParameterTypes();
for(Class clazzParam: clazzParams){
sBuilder.append(clazzParam.getName()).append(",");
}
if(clazzParams.length!=0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(")");
System.out.println(sBuilder.toString());
}
//建立這個動态累的執行個體對象
System.out.println("-----------------begin create instance object-----------------");
//clazzProxy1.newInstance();Class中的newInstrance(),為調用無參的構造方法.
Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class);
//提前建立一個類實作這個接口InvocationHandler(是代理執行個體的調用處理程式 實作的接口)
class MyInvocationHander1 implements InvocationHandler{
@Override//唯一的一個子類
/**invoke(Object proxy, Method method, Object[] args)
在代理執行個體上處理方法調用并傳回結果。*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return "第一種方法,準備一個InvocationHandler的子類,測試";
}
}
Collection proxy1=(Collection)constructor.newInstance(new MyInvocationHander1());
//proxy1.toString(),為null..并不是該對象沒有建立.
System.out.println(proxy1);
//注意:隻有:hashCode和equals和toString這三個方法是交給代理中handler.
//clear(),沒有傳回值,不報異常
proxy1.clear();
//因為,這個代理中也有size()方法,這個size()方法的傳回值是整數(因為內建了同一個接口方法的參數和傳回值自然一樣)
//proxy1.size();有傳回值,報異常
Collection proxy2=(Collection)constructor.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return "第二種方法,匿名InvocationHandler子類測試";
}
});
//proxy1.toString(),為null..并不是該對象沒有建立.
System.out.println(proxy2);
//第三種方式,直接一步到位,同時搞出位元組碼,和執行個體對象..*************************************************
/** static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
傳回一個指定接口的代理類執行個體,該接口可以将方法調用指派到指定的調用處理程式。 */
ArrayList target=new ArrayList();
Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
//注意:每調一次add方法,他就會去找一次handler的invoke方法
proxy3.add("bls");
//System.out.println("+++++"+proxy3.add("zxx"));
System.out.println(proxy3);
System.out.println("size:"+proxy3.size());
}
//給我目标給我系統功能,我就會生成一個代理.這個代理就會執行目标,并在該
//目标中添加功能代碼.
private static Object getProxy(final Object target,final Advice advice) {
//Proxy的靜态方法,直接一步到位
Object proxy3=Proxy.newProxyInstance(
target.getClass().getClassLoader(),//第一個參數
//new Class[]{Collection.class},//第二個參數不能用可變參數,因為,可變參數,要放到最後哦..
target.getClass().getInterfaces(),
new InvocationHandler(){//第三個參數***************************
//指定一個目标
@Override
//invoke在代理執行個體上處理方法調用并傳回結果。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/* long beginTime=System.currentTimeMillis();
Object retVal=method.invoke(target, args);
long endTime=System.currentTimeMillis();
System.out.println(method.getName()+"--running time of "+(endTime-beginTime));*/
advice.beforeMethod(method);
Object retVal=method.invoke(target, args);
advice.afterMethod(method);
//傳回這個值.(目标)
return retVal;
//return method.invoke(proxy, args);
}
}
);
return proxy3;
}
}
package cn.itcast.day3.aopframework;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import cn.itcast.day3.Advice;
public class BeanFactory {
// 配置檔案
Properties props = new Properties();
// 構造方法接收一個配置檔案
public BeanFactory(InputStream ips) {
try {
// ---- load()從輸入流中讀取屬性清單(鍵和元素對)。
props.load(ips);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Object getBean(String name) {
// 拿到類名
String className = props.getProperty(name);
Object bean = null;
try {
Class clazz = Class.forName(className);
// 調用不帶參數的構造方法//javaBean必須要有一個
bean = clazz.newInstance();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 如果是ProxyFactoryBean類型,就創造一個代理----spring中ProxyFactoryBean是一個接口
if (bean instanceof ProxyFactoryBean) {
Object proxy = null;
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;
try {
// 從配置檔案中擷取類名後,再擷取相應的位元組碼,後在new一個無參數的執行個體對象
Advice advice = (Advice) Class.forName(
props.getProperty(name + ".advice")).newInstance();
Object target = Class.forName(
props.getProperty(name + ".target")).newInstance();
proxyFactoryBean.setAdvice(advice);
proxyFactoryBean.setTarget(target);
proxy = proxyFactoryBean.getProxy();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
package cn.itcast.day3.aopframework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.itcast.day3.Advice;
public class ProxyFactoryBean {
private Advice advice;
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getTarget() {
return target;
}
public Object getProxy() {
// TODO Auto-generated method stub
return getProxy(target,advice);
}
//給我目标,給我系統功能,我就會生成一個代理.這個代理就會執行目标,并在該
//目标中添加功能代碼.
private static Object getProxy(final Object target,final Advice advice) {
Object proxy3=Proxy.newProxyInstance(
target.getClass().getClassLoader(),//第一個參數
//new Class[]{Collection.class},//第二個參數不能用可變參數,因為,可變參數,要放到最後哦..
target.getClass().getInterfaces(),
new InvocationHandler(){//第三個參數
//指定一個目标
@Override
//invoke在代理執行個體上處理方法調用并傳回結果。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/* long beginTime=System.currentTimeMillis();
Object retVal=method.invoke(target, args);
long endTime=System.currentTimeMillis();
System.out.println(method.getName()+"--running time of "+(endTime-beginTime));*/
System.out.println("ok");
advice.beforeMethod(method);
Object retVal=method.invoke(target, args);
advice.afterMethod(method);
//傳回這個值.(目标)
return retVal;
//return method.invoke(proxy, args);
}
}
);
System.out.println(proxy3);
return proxy3;
}
}