前言
什么是java代理,為什么要學(xué)習(xí)java代理。在很多框架中都會(huì)用到彤钟,如Spring中的AOP、Hadoop中的RPC等
什么是JAVA代理
給某一個(gè)對(duì)象提供一個(gè)代理跷叉,并由代理對(duì)象控制對(duì)原對(duì)象的引用逸雹。
代理的種類(lèi)
代理分為靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理是在編譯時(shí)就將接口云挟、實(shí)現(xiàn)類(lèi)梆砸、代理類(lèi)全部由程序員手動(dòng)完成,且代理的類(lèi)在程序運(yùn)行之前就已經(jīng)存在了园欣。但如果我們需要很多的代理帖世,每一個(gè)都這么手動(dòng)的去創(chuàng)建實(shí)屬浪費(fèi)時(shí)間,而且會(huì)有大量的重復(fù)代碼沸枯,此時(shí)我們就可以采用動(dòng)態(tài)代理日矫,動(dòng)態(tài)代理可以在程序運(yùn)行期間根據(jù)需要?jiǎng)討B(tài)的創(chuàng)建代理類(lèi)及其實(shí)例,來(lái)完成具體的功能绑榴。
靜態(tài)代理
定義一個(gè)PersonEntity實(shí)體
public class PersonEntity{
private String name;
private Integer age;
get,set方法省略
}
定義一個(gè)Person接口
public interface PersonService {
public PersonEntity getPerson();
public void personInfo(PersonEntity entity);
}
實(shí)現(xiàn)PersonService接口
public class PersonServiceImpl implements PersonService {
private static PersonEntity entity=null;
@Override
public PersonEntity getPerson() {
return createPerson();
}
@Override
public void personInfo(PersonEntity entity) {
System.out.println("person_name:"+entity.getName()+",person_age:"+entity.getAge());
}
public static PersonEntity createPerson(){
if(entity==null){
entity=new PersonEntity();
return entity;
}else{
return entity;
}
}
}
調(diào)用PersonServiceImpl
public class Test{
public static void main(String[] args) {
PersonService pService = new PersonServiceImpl();
PersonEntity entity=new PersonEntity();
entity.setAge(22);
entity.setName("youshh");
pService.personInfo(entity);
}
}
運(yùn)行結(jié)果:
person_name:youshh,person_age:22
動(dòng)態(tài)代理
動(dòng)態(tài)代理的基礎(chǔ)代碼同靜態(tài)代碼一致哪轿,但是并不是通過(guò)new 的方式來(lái)獲取接口而是通過(guò)重寫(xiě)InvocationHandler的invoke接口來(lái)實(shí)現(xiàn)代理功能(InvocationHandler是JDK的動(dòng)態(tài)代理接口里面只有一個(gè)invoke方法,返回值為Object)
代理PersonProxyHandler的代碼:
public class PersonProxyHandler implements InvocationHandler {
private Object object;
public PersonProxyHandler(Object object) {
super();
this.object = object;
}
/**
* 繼承InvocationHandler 代理接口翔怎, 實(shí)現(xiàn)invoke方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
System.out.println("before proxy ");
Object p=method.invoke(object, args);
System.out.println("after proxy");
return p;
}
}
調(diào)用
public class Test {
public static void main(String[] args) {
PersonService pService = new PersonServiceImpl();
InvocationHandler handler =new PersonProxyHandler(pService);
PersonService pService2 = (PersonService) Proxy.newProxyInstance(handler.getClass().getClassLoader(), pService.getClass().getInterfaces(), handler);
PersonEntity entity=pService2.getPerson();
entity.setAge(24);
entity.setName("youshh");
pService2.personInfo(entity);
}
}
運(yùn)行結(jié)果:
person_name:youshh,person_age:22
值得注意的是在invoke中如果return null窃诉,那么在調(diào)用有返回值的方法時(shí)就獲取不到其返回值,而最終獲取到的是null赤套,例如PersonEntity entity=pService2.getPerson();在調(diào)用時(shí)
entity最終為null飘痛,所以在實(shí)際業(yè)務(wù)中invoke的是否需要返回值更具實(shí)際來(lái)定義。
從上面代碼中可以看出 PersonService 是通過(guò)Proxy.newProxyInstance 生成的容握,
在proxy中的代碼有什么樣的呢宣脉?
為什么能通過(guò)Proxy的靜態(tài)方法newProxyInstance獲取到PersonService的實(shí)例呢?這個(gè)是通過(guò)JAVA的反射機(jī)制實(shí)現(xiàn)的唯沮。
//由于 Proxy 內(nèi)部從不直接調(diào)用構(gòu)造函數(shù)脖旱,所以 private 類(lèi)型意味著禁止任何調(diào)用private Proxy() {}
//由于 Proxy 內(nèi)部從不直接調(diào)用構(gòu)造函數(shù),所以 protected 意味著只有子類(lèi)可以調(diào)用
protectedProxy(InvocationHandler h) {this.h = h;}
Proxy靜態(tài)方法newProxyInstance
public static Object newProxyInstance(ClassLoader loader, Class[]interfaces,InvocationHandler h) throwsIllegalArgumentException {//檢查 h 不為空介蛉,否則拋異常if(h ==null) {thrownewNullPointerException();
}//獲得與指定類(lèi)裝載器和一組接口相關(guān)的代理類(lèi)類(lèi)型對(duì)象Class cl =getProxyClass(loader, interfaces);//通過(guò)反射獲取構(gòu)造函數(shù)對(duì)象并生成代理類(lèi)實(shí)例try{
Constructor cons=cl.getConstructor(constructorParams);return(Object) cons.newInstance(newObject[] { h });
}catch(NoSuchMethodException e) {thrownewInternalError(e.toString());
}catch(IllegalAccessException e) {thrownewInternalError(e.toString());
}catch(InstantiationException e) {thrownewInternalError(e.toString());
}catch(InvocationTargetException e) {thrownewInternalError(e.toString());
}
}
動(dòng)態(tài)代理還有一種實(shí)現(xiàn)方式:cglib動(dòng)態(tài)代理實(shí)現(xiàn)萌庆,據(jù)說(shuō)它的運(yùn)行速度要遠(yuǎn)遠(yuǎn)快于JDK的Proxy動(dòng)態(tài)
這個(gè)目前還沒(méi)進(jìn)行深入研究
總結(jié)
一個(gè)典型的動(dòng)態(tài)代理創(chuàng)建對(duì)象過(guò)程可分為以下四個(gè)步驟:
1、通過(guò)實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器
IvocationHandler handler = new InvocationHandlerImpl(...);
2币旧、通過(guò)為Proxy類(lèi)指定ClassLoader對(duì)象和一組interface創(chuàng)建動(dòng)態(tài)代理類(lèi)
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3践险、通過(guò)反射機(jī)制獲取動(dòng)態(tài)代理類(lèi)的構(gòu)造函數(shù),其參數(shù)類(lèi)型是調(diào)用處理器接口類(lèi)型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通過(guò)構(gòu)造函數(shù)創(chuàng)建代理類(lèi)實(shí)例巍虫,此時(shí)需將調(diào)用處理器對(duì)象作為參數(shù)被傳入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
為了簡(jiǎn)化對(duì)象創(chuàng)建過(guò)程彭则,Proxy類(lèi)中的newInstance方法封裝了2~4,只需兩步即可完成代理對(duì)象的創(chuàng)建占遥。
生成的ProxySubject繼承Proxy類(lèi)實(shí)現(xiàn)Subject接口俯抖,實(shí)現(xiàn)的Subject的方法實(shí)際調(diào)用處理器的invoke方法,而invoke方法利用反射調(diào)用的是被代理對(duì)象的的方法(Object result=method.invoke(proxied,args))
第一次寫(xiě)文章瓦胎,有寫(xiě)的不好的地方大家可以題出來(lái)芬萍。