静态代理设计与动态代理设计
靜態(tài)代理設(shè)計(jì)模式
代理設(shè)計(jì)模式最本質(zhì)的特質(zhì):一個(gè)真實(shí)業(yè)務(wù)主題只完成核心操作,而所有與之輔助的功能都由代理類來(lái)完成。
?
例如,在進(jìn)行數(shù)據(jù)庫(kù)更新的過(guò)程之中,事務(wù)處理必須起作用,所以此時(shí)就可以編寫代理設(shè)計(jì)模式來(lái)完成。
?
范例:結(jié)合傳統(tǒng)的代理設(shè)計(jì)模式以及以購(gòu)物車CartDao為例來(lái)編寫代理設(shè)計(jì)模式
package so.strong.mall.proxy; import java.util.List; public interface CartDao {boolean insert(Cart cart) throws Exception;List<Cart> findAll() throws Exception; }以上CartDao接口定義的方法,更行插入一定需要事務(wù)控制,對(duì)于查詢操作,不需要事務(wù)控制。
?
定義CartDao真實(shí)實(shí)現(xiàn)
package so.strong.mall.proxy; import java.util.List; public class CartDAOImpl implements CartDao{@Overridepublic boolean insert(Cart cart) throws Exception {System.out.println("=====執(zhí)行數(shù)據(jù)增加操作=====");return false;}@Overridepublic List<Cart> findAll() throws Exception {System.out.println("=====執(zhí)行數(shù)據(jù)列表操作=====");return null;} }?
定義代理主題類
package so.strong.mall.proxy; import java.util.List; public class CartDAOProxy implements CartDao {private CartDao cartDao;public CartDAOProxy() {}public void setCartDao(CartDao cartDao) {this.cartDao = cartDao;}public void prepare() {System.out.println("=====取消掉jdbc的自動(dòng)提交");}public void commit() {System.out.println("=====手工提交事務(wù)");}public void rollback() {System.out.println("=====出現(xiàn)錯(cuò)誤,事務(wù)回滾");}@Overridepublic boolean insert(Cart cart) throws Exception {try {this.prepare();boolean flag = this.cartDao.insert(cart);this.commit();return flag;} catch (Exception e) {this.rollback();throw e;}}@Overridepublic List<Cart> findAll() throws Exception {return this.cartDao.findAll();} }?
業(yè)務(wù)層現(xiàn)在并不關(guān)心到底是代理類還是真實(shí)主題類,它只關(guān)心一點(diǎn),只要取得了CartDao接口對(duì)象就可以,那么這一操作可以通過(guò)工廠類來(lái)隱藏。
package so.strong.mall.proxy; public class DAOFactory {public static CartDao getCartDaoInstance() {CartDAOProxy proxy = new CartDAOProxy();proxy.setCartDao(new CartDAOImpl());return proxy;} }此時(shí)業(yè)務(wù)層暫時(shí)不需要繼續(xù)進(jìn)行,只需要通過(guò)客戶端模擬業(yè)務(wù)層調(diào)用即可。
public class TestDemo { public static void main(String[] args) throws Exception{CartDao dao = DAOFactory.getCartDaoInstance();dao.insert(new Cart());} } //=====取消掉jdbc的自動(dòng)提交 //=====執(zhí)行數(shù)據(jù)增加操作===== //=====手工提交事務(wù)因?yàn)槭聞?wù)和處理本身與核心業(yè)務(wù)有關(guān)的功能,但是它不是核心,那么用代理解決是最合適的方式。
?
動(dòng)態(tài)代理設(shè)計(jì)模式
上面給出的代理設(shè)計(jì)模式的確可以完成代理要求,但是有一個(gè)問(wèn)題:如果說(shuō)現(xiàn)在項(xiàng)目里面有200張數(shù)據(jù)表,那么至少也需要200個(gè)左右的DAO接口,如果用上面的代理設(shè)計(jì)模式,那么意味著除了編寫200個(gè)的DAO接口實(shí)現(xiàn),還要編寫200個(gè)代理類,并且有意思的是,這些代理類實(shí)現(xiàn)幾乎相同。
以上的代理設(shè)計(jì)模式屬于靜態(tài)代理設(shè)計(jì)模式,只能夠作為代理模式的雛形出現(xiàn),并不能購(gòu)作為代碼使用的設(shè)計(jì)模式,為此專門引入了動(dòng)態(tài)代理設(shè)計(jì)模式的概念。
即:利用一個(gè)代理類可以實(shí)現(xiàn)所有被代理的操作。
?
如果要想實(shí)現(xiàn)動(dòng)態(tài)設(shè)計(jì)模式,那么必須首先觀察一個(gè)接口:java.lang.reflect.InvocatonHandler. ? 它里面有一個(gè)方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;這個(gè)方法就屬于代理中調(diào)用真實(shí)主題類的操作方法,這個(gè)方法里面的參數(shù)意義如下:
- Object proxy:表示代理類的對(duì)象;
- Method method:表示現(xiàn)在正在調(diào)用的方法;
- Object[] args:表示方法里面的參數(shù)。
但是這個(gè)方法沒(méi)有所對(duì)應(yīng)的真實(shí)對(duì)象,所以需要在創(chuàng)建這個(gè)類對(duì)象的時(shí)候設(shè)置好真實(shí)代理對(duì)象。
?
如果要想找到代理對(duì)象則要使用java.lang.reflect.Proxy類來(lái)動(dòng)態(tài)創(chuàng)建,此類主要使用以下方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException此方法參數(shù)定義如下:
- ClassLoader loader :指的是取得對(duì)象的加載器;
- Class<?>[] interfaces: 代理設(shè)計(jì)模式的核心是圍繞接口進(jìn)行的,所以此處必須取出全部的接口;
- InvocationHandler h:代理的實(shí)現(xiàn)類。
?
范例:使用動(dòng)態(tài)代理實(shí)現(xiàn)上面的代理
CartDao不變,修改CartDAOProxy代理類
package so.strong.mall.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class CartDAOProxy implements InvocationHandler {private Object obj; //這個(gè)是真實(shí)對(duì)象主題/*** 將要操作的真實(shí)主題對(duì)象綁定到代理之中,而后返回一個(gè)代理類對(duì)象* @param obj 真實(shí)對(duì)象主題* @return 代理類對(duì)象*/public Object bind(Object obj) {this.obj = obj;return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), this);}public void prepare() {System.out.println("=====取消掉jdbc的自動(dòng)提交");}public void commit() {System.out.println("=====手工提交事務(wù)");}public void rollback() {System.out.println("=====出現(xiàn)錯(cuò)誤,事務(wù)回滾");}//只要執(zhí)行了操作方法,那么就一定會(huì)觸發(fā)invoke @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object object = null;//接收返回值if (method.getName().contains("insert")) { //更新插入類操作this.prepare();try {object = method.invoke(this.obj, args); //反射調(diào)用方法this.commit();} catch (Exception e) {this.rollback();}} else {object = method.invoke(this.obj, args);//查詢操作不需要事務(wù)支持 }return object;} } //修改工廠 package so.strong.mall.proxy; public class DAOFactory {public static Object getCartDaoInstance(Object realObject) {return new CartDAOProxy().bind(realObject);} } //修改調(diào)用 package so.strong.mall.proxy; public class TestDemo {public static void main(String[] args) throws Exception{CartDao dao =(CartDao) DAOFactory.getCartDaoInstance(new CartDAOImpl());dao.insert(new Cart());} }?
CGLIB實(shí)現(xiàn)動(dòng)態(tài)代理設(shè)計(jì)模式
動(dòng)態(tài)代理模式的確好用,而且也解決了代理類重復(fù)的問(wèn)題,但是不管是傳統(tǒng)靜態(tài)代理或動(dòng)態(tài)代理都有個(gè)設(shè)計(jì)缺陷,以動(dòng)態(tài)代理為例:
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); //傳入真實(shí)主題類,返回代理主題類代理設(shè)計(jì)模式有一個(gè)硬性要求,就是類必須要有接口,所以業(yè)界很多人認(rèn)為應(yīng)該在沒(méi)有接口的環(huán)境下也能使用代理設(shè)計(jì)模式。
所以在此時(shí)在開源社區(qū)里面提供了一個(gè)組件包——CGLIB,利用此包可以在沒(méi)有接口的情況下也能夠使用動(dòng)態(tài)代理設(shè)計(jì)模式,它是模擬的類。
如果要想使用CGLIB,那么必須首先搞清楚對(duì)應(yīng)關(guān)系:
- Proxy:net.sf.cglib.proxy.Enhancer
- InvocationHandler:net.sf.cglib.proxy.MethodInterceptor
- 真實(shí)主題調(diào)用:net.sf.cglib.proxy.MethodProxy
老師課上使用的是引入CGLIB的jar包,我去mvn倉(cāng)庫(kù)找了一下,找到了一個(gè)cglib,放到pom里面發(fā)現(xiàn)也可以。
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version> </dependency>?
范例:使用CGLIB實(shí)現(xiàn)動(dòng)態(tài)代理設(shè)計(jì)模式
package so.strong.mall.proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method;class ItemDAOImpl {public void insert(Item item) {System.out.println("=====增加操作=====");} }class MyProxy implements MethodInterceptor {private Object target; //真實(shí)操作主題public MyProxy(Object target) {this.target = target;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {Object object = null;this.prepare();object = method.invoke(this.target, args);this.commit();return object;}public void prepare() {System.out.println("=====取消掉jdbc的自動(dòng)提交=====");}public void commit() {System.out.println("=====手工提交事務(wù)=====");} }public class TestCGLIB {public static void main(String[] args) {ItemDAOImpl itemDAO = new ItemDAOImpl(); //真實(shí)主題對(duì)象//代理設(shè)計(jì)模式之中必須要有公共的集合點(diǎn),例如:接口,而CGLIB沒(méi)有接口Enhancer enhancer = new Enhancer(); //創(chuàng)建一個(gè)代理工具類enhancer.setSuperclass(ItemDAOImpl.class); //設(shè)置一個(gè)虛擬的父類enhancer.setCallback(new MyProxy(itemDAO)); //設(shè)置代理的回調(diào)操作ItemDAOImpl proxyDao = (ItemDAOImpl) enhancer.create();proxyDao.insert(new Item());} }可以發(fā)現(xiàn)此時(shí)沒(méi)有了對(duì)接口的依賴,也可以實(shí)現(xiàn)動(dòng)態(tài)代理設(shè)計(jì),但是需要模擬代理的父類對(duì)象。
轉(zhuǎn)載于:https://www.cnblogs.com/itermis/p/8940582.html
總結(jié)
以上是生活随笔為你收集整理的静态代理设计与动态代理设计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: solidity开发以太坊代币智能合约
- 下一篇: Google Guava新手教程