详解:设计模式之-代理设计
分享一波:程序員賺外快-必看的巔峰干貨
概念
通過代理控制對象的訪問,可以詳細訪問某個對象的方法,在這個方法調(diào)用處理,或調(diào)用后處理(類似于AOP)。
代理設(shè)計模式應(yīng)用場景:AOP、權(quán)限控制、事務(wù)
常見代理的分類有:靜態(tài)代理,動態(tài)代理(JDK動態(tài)代理,Cglib等)
注意:本文所指的“代理”為設(shè)計模式中的“代理設(shè)計模式”,跟正向、反向代理并非一個概念,不要混淆。
靜態(tài)代理
由程序員創(chuàng)建或工具生成代理類的源碼,再編譯代理類。所謂靜態(tài)也就是在程序運行前就已經(jīng)存在代理類的字節(jié)碼文件,代理類和委托類的關(guān)系在運行前就確定了。
public interface IUserDao {
void save();
}
public class UserDao implements IUserDao {
public void save() {
System.out.println(“已經(jīng)保存數(shù)據(jù)…”);
}
}
/**
-
代理類
*/
public class UserDaoProxy implements IUserDao {
private IUserDao target;public UserDaoProxy(IUserDao iuserDao) {
this.target = iuserDao;
}public void save() {
System.out.println(“開啟事物…”);
target.save();
System.out.println(“關(guān)閉事物…”);
}
}
靜態(tài)代理需要每個被代理類都有一個代理類,因此代碼量較多,實際開發(fā)中非必須情況盡量少用。
動態(tài)代理
JDK動態(tài)代理
1)原理:是根據(jù)類加載器和接口創(chuàng)建代理類(此代理類是接口的實現(xiàn)類,所以必須使用接口 面向接口生成代理,位于java.lang.reflect包下)
2)實現(xiàn)方式:
通過實現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器 IvocationHandler handler = new InvocationHandlerImpl(…);
通過為Proxy類指定ClassLoader對象和一組interface創(chuàng)建動態(tài)代理類Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
通過反射機制獲取動態(tài)代理類的構(gòu)造函數(shù),其參數(shù)類型是調(diào)用處理器接口類型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
通過構(gòu)造函數(shù)創(chuàng)建代理類實例,此時需將調(diào)用處理器對象作為參數(shù)被傳入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
缺點:jdk動態(tài)代理,必須是面向接口,目標業(yè)務(wù)類必須實現(xiàn)接口
public class InvocationHandlerImpl implements InvocationHandler {
/** 目標代理對象 */ private Object target;public InvocationHandlerImpl(Object target) {this.target = target; }public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 反射執(zhí)行方法Object invoke = method.invoke(target, args);return invoke; } public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,IllegalAccessException, IllegalArgumentException, InvocationTargetException {// 被代理對象IUserDao userDao = new UserDao();InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);ClassLoader loader = userDao.getClass().getClassLoader();Class<?>[] interfaces = userDao.getClass().getInterfaces();// 主要裝載器、一組接口及調(diào)用處理動態(tài)代理實例IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);newProxyInstance.save(); }}
Cglib動態(tài)代理
原理:利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節(jié)碼生成子類來處理。
使用cglib[Code Generation Library]實現(xiàn)動態(tài)代理,并不要求委托類必須實現(xiàn)接口,底層采用asm字節(jié)碼生成框架生成代理類的字節(jié)碼
引入依賴:
org.apache.commons commons-lang3 3.8.1 cglib cglib 3.1創(chuàng)建代理:
public class CglibProxy implements MethodInterceptor {
/** 代理對象 */ private Object target;public Object getInstance(Object target) {// 固定代碼,創(chuàng)建訪問對象this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return enhancer.create(); }public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object invoke = methodProxy.invoke(target, args);return invoke; }public static void main(String[] args) {CglibProxy cglibProxy = new CglibProxy();UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());userDao.save(); }}
Cglib和jdk動態(tài)代理的區(qū)別
java動態(tài)代理是利用反射機制生成一個實現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。
而cglib動態(tài)代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節(jié)碼生成子類來處理。
Spring中。
1、如果目標對象實現(xiàn)了接口,默認情況下會采用JDK的動態(tài)代理實現(xiàn)AOP
2、如果目標對象實現(xiàn)了接口,可以強制使用CGLIB實現(xiàn)AOP
3、如果目標對象沒有實現(xiàn)了接口,必須采用CGLIB庫,spring會自動在JDK動態(tài)代理和CGLIB之間轉(zhuǎn)換
JDK動態(tài)代理只能對實現(xiàn)了接口的類生成代理,而不能針對類 。
CGLIB是針對類實現(xiàn)代理,主要是對指定的類生成一個子類,覆蓋其中的方法 。
因為是繼承,所以該類或方法最好不要聲明成final ,final可以阻止繼承和多態(tài)。
分享一波:程序員賺外快-必看的巔峰干貨
如果以上內(nèi)容對你覺得有用,并想獲取更多的賺錢方式和免費的技術(shù)教程
請關(guān)注微信公眾號:HB荷包
一個能讓你學習技術(shù)和賺錢方法的公眾號,持續(xù)更新
總結(jié)
以上是生活随笔為你收集整理的详解:设计模式之-代理设计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Notepad++来比较文件
- 下一篇: 详解模板方法设计模式