代理
為其他對(duì)象提供一種代理以控制這個(gè)對(duì)象的訪(fǎng)問(wèn),在某些情況下一個(gè)對(duì)象不能直接訪(fǎng)問(wèn)那個(gè)對(duì)象時(shí),代理就起到了客戶(hù)端和被代理對(duì)象 (委托類(lèi)) 中介作用。
按照代理的創(chuàng)建時(shí)期,代理類(lèi)可以分為兩種:
靜態(tài):由程序員創(chuàng)建代理類(lèi)或特定工具自動(dòng)生成源代碼再對(duì)其編譯。在程序運(yùn)行前代理類(lèi)的.class文件就已經(jīng)存在了。
動(dòng)態(tài):在程序運(yùn)行時(shí)運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成。
靜態(tài)代理
Subject
: 代理類(lèi)和被代理類(lèi)實(shí)現(xiàn)同樣的接口
Proxy
:代理類(lèi),里面有被代理類(lèi),具體邏輯委托被代理類(lèi)進(jìn)行處理
RealSubject
:被代理類(lèi),可以在其內(nèi)做一些訪(fǎng)問(wèn)權(quán)限控制,額外的業(yè)務(wù)處理
Client
:看到的是代理類(lèi),并不知道具體處理業(yè)務(wù)邏輯的類(lèi),降低耦合性
代碼實(shí)現(xiàn)
UserDAO 代理和被代理的公共的接口(Subject)
public interface ProxyDao { boolean insert(String name); }
UserDAOImpl 被代理類(lèi)(RealSubject)
public class ProxyDaoImpl implements ProxyDao { @Override public boolean insert(String name) { System.out.println("insert name=" + name); return true; } }
ProxyByInterface 代理類(lèi),通過(guò)實(shí)現(xiàn)接口方式實(shí)現(xiàn)代理方式(Proxy)
public class ProxyByInterface implements ProxyDao { private ProxyDao proxyDao; public ProxyByInterface(ProxyDao proxyDao) { this.proxyDao = proxyDao; } @Override public boolean insert(String name) { System.out.println("before insert by interface"); return proxyDao.insert(name); } }
ProxyByExtend 代理類(lèi),通過(guò)繼承被代理類(lèi)實(shí)現(xiàn)的代理方式(Proxy)
public class ProxyByExtend extends ProxyDaoImpl{ private ProxyDaoImpl proxyDao; public ProxyByExtend(ProxyDaoImpl proxyDao) { this.proxyDao = proxyDao; } @Override public boolean insert(String name) { System.out.println("before insert by extend"); return proxyDao.insert(name); } }
獲取代理對(duì)象客戶(hù)端(Client)
public class Client { public static void main(String[] args) { ProxyDaoImpl proxyDao = new ProxyDaoImpl(); //和被代理類(lèi)實(shí)現(xiàn)同個(gè)接口方式進(jìn)行代理 ProxyByInterface proxyByInterface = new ProxyByInterface(proxyDao); proxyByInterface.insert("zc-Interface"); //通過(guò)繼承被代理類(lèi)方式進(jìn)行代理 ProxyByExtend proxyByExtend = new ProxyByExtend(proxyDao); proxyByExtend.insert("zc-Extend"); } }
好處:
可以不用動(dòng)原來(lái)類(lèi)的邏輯,再次增加一些功能,符合開(kāi)閉原則。
真正的業(yè)務(wù)還是交給被代理對(duì)象處理的,無(wú)須修改原來(lái)的類(lèi)就可以使用代理進(jìn)行實(shí)現(xiàn)。
缺點(diǎn):
出現(xiàn)了大量的代碼重復(fù)。如果接口增加一個(gè)方法,除了所有實(shí)現(xiàn)類(lèi)需要實(shí)現(xiàn)這個(gè)方法外,所有代理類(lèi)也需要實(shí)現(xiàn)此方法。增加了代碼維護(hù)的復(fù)雜度。
代理對(duì)象只服務(wù)于一種類(lèi)型的對(duì)象,如果要服務(wù)多類(lèi)型的對(duì)象。勢(shì)必要為每一種對(duì)象都進(jìn)行代理,靜態(tài)代理在程序規(guī)模稍大時(shí)就無(wú)法勝任了。
動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理
Jdk動(dòng)態(tài)代理,針對(duì)的是實(shí)現(xiàn)接口的類(lèi)
;
要求目標(biāo)對(duì)象必須實(shí)現(xiàn)接口
,因?yàn)樗鼊?chuàng)建代理對(duì)象的時(shí)候是根據(jù)接口創(chuàng)建的。被代理對(duì)象可以可以實(shí)現(xiàn)多個(gè)接口
,創(chuàng)建代理時(shí)指定創(chuàng)建某個(gè)接口的代理對(duì)象就可以調(diào)用該接口定義的方法了。
需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 類(lèi)的支持
//Object proxy:被代理的對(duì)象 //Method method:要調(diào)用的方法 //Object[] args:方法調(diào)用時(shí)所需要參數(shù) public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
//CLassLoader loader:類(lèi)的加載器 //Class<?> interfaces:得到全部的接口 //InvocationHandler h:得到InvocationHandler接口的子類(lèi)的實(shí)例 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
代碼實(shí)現(xiàn)
NameDao 姓名-接口(Subject)
public interface NameDao { public boolean addName(String name); }
AgeDao 年齡-接口(Subject)
public interface AgeDao { public boolean addAge(Integer age); }
NameAndAgeDaoImpl 姓名、年齡實(shí)現(xiàn)類(lèi)(RealSubject)
public class NameAndAgeDaoImpl implements NameDao,AgeDao { @Override public boolean addName(String name) { System.out.println("NameDaoImpl----->" + name); return true; } @Override public boolean addAge(Integer age) { System.out.println("AgeDaoImpl----->" + age); return true; } }
MyInvocationHandler,對(duì)接口提供的方法進(jìn)行增強(qiáng)(Proxy)
public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("----------before----------"); System.out.println("Proxy=" + proxy.getClass()); System.out.println("method=" + method); System.out.println("args=" + Arrays.toString(args)); //執(zhí)行目標(biāo)方法對(duì)象 Object result = method.invoke(target, args); System.out.println("----------after----------"); return result; } }
ProxyFactory 代理工廠
public class ProxyFactory { public static Object getProxy(Object proxyObj) { /** * loader 指定加載jvm運(yùn)行時(shí)動(dòng)態(tài)生成的代理對(duì)象的加載器 * interface 真實(shí)對(duì)象實(shí)現(xiàn)的所有接口 * h 實(shí)現(xiàn)InvocationHandler接口對(duì)象 */ // return Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(), // proxyObj.getClass().getInterfaces(), new MyInvocationHandler(proxyObj)); return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), proxyObj.getClass().getInterfaces(), new MyInvocationHandler(proxyObj)); } public static void main(String[] args) { NameDao nameDao = (NameDao) getProxy(new NameAndAgeDaoImpl()); AgeDao ageDao = (AgeDao) getProxy(new NameAndAgeDaoImpl()); nameDao.addName("zc"); ageDao.addAge(20); } }
思考 為什么需要 實(shí)現(xiàn)接口的類(lèi),而不是 類(lèi)
main函數(shù)中,運(yùn)行該語(yǔ)句:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
public static void main(String[] args) { System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); NameDao nameDao = (NameDao) getProxy(new NameAndAgeDaoImpl()); AgeDao ageDao = (AgeDao) getProxy(new NameAndAgeDaoImpl()); nameDao.addName("zc"); ageDao.addAge(20); }
可以查看 $Proxy0
類(lèi):
會(huì)發(fā)現(xiàn)他已經(jīng)繼承了 Proxy , 之后才是創(chuàng)建的一個(gè)(多個(gè))接口;而由于java是 單繼承、多接口 的特性,所以JDK動(dòng)態(tài)代理,需要實(shí)現(xiàn)接口的類(lèi)。
CGLib動(dòng)態(tài)代理
CGLIB實(shí)現(xiàn)動(dòng)態(tài)代理,并不要求被代理類(lèi)必須實(shí)現(xiàn)接口,底層采用asm字節(jié)碼生成框架生成代理類(lèi)字節(jié)碼(該代理類(lèi)繼承了被代理類(lèi))。
所以被代理類(lèi)一定不能定義為final class并且對(duì)于final 方法不能被代理。
實(shí)現(xiàn)需要
//MethodInterceptor接口的intercept方法 /** *obj 代理對(duì)象 *method 委托類(lèi)方法,被代理對(duì)象的方法字節(jié)碼對(duì)象 *arg 方法參數(shù) *MethodProxy 代理方法MethodProxy對(duì)象,每個(gè)方法都會(huì)對(duì)應(yīng)有這樣一個(gè)對(duì)象 */ public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy)
Ehancer enhancer = new Enhancer() //Enhancer為字節(jié)碼增強(qiáng)器,很方便對(duì)類(lèi)進(jìn)行擴(kuò)展 enhancer.setSuperClass(被代理類(lèi).class); enhancer.setCallback(實(shí)現(xiàn)MethodInterceptor接口的對(duì)象) enhancer.create()//返回代理對(duì)象,是被代理類(lèi)的子類(lèi)
代碼實(shí)現(xiàn)
UserDaoImpl 用戶(hù)實(shí)現(xiàn)類(lèi)(RealSubject)
public class UserDaoImpl { public boolean insert(String name) { System.out.println("insert name=" + name); return true; } public final boolean insert1(String name) { System.out.println("final insert name=" + name); return true; } }
CglibProxy CGLIB代理類(lèi)(Proxy)
public class CglibProxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("----------before----------"); System.out.println("Proxy=" + o.getClass()); System.out.println("method=" + method); System.out.println("args=" + Arrays.toString(objects)); System.out.println("methodProxy=" + methodProxy); //執(zhí)行目標(biāo)方法對(duì)象 Object result = methodProxy.invokeSuper(o, objects); System.out.println("----------after----------"); return result; } }
ProxyFactory 代理工廠
public class ProxyFactory { private static Enhancer enhancer = new Enhancer(); private static CglibProxy cglibProxy = new CglibProxy(); public static Object getProxy(Class cls) { enhancer.setSuperclass(cls); enhancer.setCallback(cglibProxy); return enhancer.create(); } public static void main(String[] args) { UserDaoImpl userDao = (UserDaoImpl) getProxy(UserDaoImpl.class); userDao.insert("zc"); } }
思考
為什么這里面使用 invokeSuper()
,不使用 invoke()
1.Method method
是被代理對(duì)象的方法字節(jié)碼對(duì)象。
2.MethodProxy methodProxy
是代理對(duì)象的方法字節(jié)碼對(duì)象。
使用 MethodProxy 的好處:
不需要給代理對(duì)象傳入被代理對(duì)象,效率更高。不會(huì)出現(xiàn)死循環(huán)的問(wèn)題。
第一點(diǎn)查看代碼就可以看出,對(duì)第二點(diǎn)進(jìn)行講解:
如何出現(xiàn)死循環(huán)的現(xiàn)象:
Proxy.newProxyInstance(xxx, xxx, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ... //加入這一句 proxy.toString(); ... } });
原因:代理對(duì)象方法的時(shí)候,都會(huì)經(jīng)過(guò)攔截器方法。因此,如果在攔截器中再調(diào)用代理對(duì)象的方法,就會(huì)再次進(jìn)入攔截器,這樣就形成了死循環(huán)。
**invokeSuper()**方法,可以使用代理對(duì)象父類(lèi)的方法(就是被代理對(duì)象)而不必經(jīng)過(guò)攔截器-----詳情可以學(xué)習(xí):《類(lèi)加載機(jī)制》、《雙親委派模型》
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注服務(wù)器之家的更多內(nèi)容!
原文鏈接:https://blog.csdn.net/qq_43740362/article/details/120123454