計模式 - 代理模式)
代理模式概述代理模式是Java開發(fā)中使用較多的一種設(shè)計模式。代理設(shè)計就是為其他對象提供一種代理以控制對這個對象的訪問。代理類似中介的身份應(yīng)用場景安全代理屏蔽對真實角色的直接訪問。遠程代理通過代理類處理遠程方法調(diào)用RMI延遲加載先加載輕量級的代理對象真正需要再加載真實對象比如你要開發(fā)一個大文檔查看軟件大文檔中有大的圖片有可能一個圖片有 100MB在打開文件時不可能將所有的圖片都顯示出來這樣就可以使用代理模式當(dāng)需要查看圖片時用proxy來進行大圖片的打開。分類靜態(tài)代理靜態(tài)定義代理類專門針對某個接口動態(tài)代理動態(tài)生成代理類不顯式的寫代理類JDK自帶的動態(tài)代理需要反射等知識一、靜態(tài)代理模式學(xué)習(xí)地址https://www.bilibili.com/video/BV1Kb411W75N/?p355spm_id_from333.1007.top_right_bar_window_history.content.clickvd_source461545ff50a35eaeaa8218ecdc5f7152實現(xiàn)步驟1、創(chuàng)建一個類NetWorkTest2、創(chuàng)建一個接口NetWork其中有browse()方法需要被代理類和被代理類共同實現(xiàn)3、創(chuàng)建被代理類Server實現(xiàn)NetWork接口重寫其中的browse()方法4、創(chuàng)建代理類ProxyServer實現(xiàn)NetWork接口。聲明變量NetWork、提供代理類ProxyServer構(gòu)造器、重寫的browse()方法調(diào)用NetWork.browse()5、使用時new一個真實的被代理類server、new一個代理類ProxyServer傳入server、調(diào)用ProxyServer的browse()方法不直接使用server.browse()的好處就是不止可以調(diào)用server.browse()方法proxyServer.browse()中還能做些其它的事比如信息校驗等//1、創(chuàng)建一個類NetWorkTestpublicclassNetWorkTest{publicstaticvoidmain(String[]args){//new一個真實的被代理類serverServerservernewServer();//server.browse();//相當(dāng)于server賦予了ProxyServer中的NetWork//因為server和ProxyServer都實現(xiàn)了NetWork所以server可以當(dāng)作NetWork傳入構(gòu)造器ProxyServer(NetWork work)實際調(diào)用的是實現(xiàn)NetWork 的server.browse()ProxyServerproxyServernewProxyServer(server);proxyServer.browse();}}//代理類和被代理類共同實現(xiàn)的接口NetWorkinterfaceNetWork{publicvoidbrowse();//瀏覽的方法}//被代理類classServerimplementsNetWork{Overridepublicvoidbrowse(){System.out.println(真實的服務(wù)器訪問網(wǎng)絡(luò));}}//代理類classProxyServerimplementsNetWork{//聲明變量privateNetWorkwork;//提供當(dāng)前代理類的構(gòu)造器publicProxyServer(NetWorkwork){this.workwork;//對work進行初始化}//校驗的方法publicvoidcheck(){System.out.println(聯(lián)網(wǎng)之前的檢查工作);}Overridepublicvoidbrowse(){check();//先調(diào)用check方法校驗work.browse();//真實的情況下使用work進行browse操作}}二、動態(tài)代理模式學(xué)習(xí)地址https://www.bilibili.com/video/BV1Vf4y127N5?p25vd_source461545ff50a35eaeaa8218ecdc5f7152動態(tài)代理主要是Spring的兩大特點之一AOP即面向切面編程AOP為Aspect Oriented Programming的縮寫意為面向切面編程通過預(yù)編譯方式和運行期間動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護的一種技術(shù)。利用AOP可以對業(yè)務(wù)邏輯的各個部分進行隔離從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低提高程序的可重用性同時提高了開發(fā)的效率。什么是 AOP1、通俗描述不通過修改源代碼方式在主干功能里面添加新功能2、通常使用AspectJ框架實現(xiàn)AOP主要意圖將日志記錄性能統(tǒng)計安全控制事務(wù)處理異常處理等代碼從業(yè)務(wù)邏輯中劃分出來通過對這些行為的分離我們希望可以將他們獨立到非指導(dǎo)業(yè)務(wù)邏輯的方法中進而改變這些行為的時候不影響業(yè)務(wù)邏輯的代碼。使用登錄例子說明 AOP:普通的登錄功能添加權(quán)限控制時需要修改原代碼。創(chuàng)建新的權(quán)限判斷模塊通過AOP加到原有的登錄流程中可以不改變原代碼1、AOP 使用動態(tài)代理的兩種情況代理創(chuàng)建被增強對象的代理對象通過代理對象將功能做到第一種 有接口情況使用 JDK 動態(tài)代理創(chuàng)建接口實現(xiàn)類UserDaoImpl代理對象增強類的方法第二種 沒有接口情況使用 CGLIB 動態(tài)代理創(chuàng)建子類Person的代理對象增強類的方法2、底層原理JDK動態(tài)代理實現(xiàn)基本邏輯1、創(chuàng)建接口UserDao2、創(chuàng)建接口的實現(xiàn)類UserDaoImpl3、創(chuàng)建代理對象UserDaoProxy實現(xiàn)InvocationHandler接口里面寫增強的方法4、調(diào)用Proxy.newProxyInstance傳入類加載器實現(xiàn)的接口UserDao實現(xiàn)InvocationHandler這個接口的類UserDaoProxy1.創(chuàng)建接口定義方法創(chuàng)建接口UserDaopublicinterfaceUserDao{publicintadd(inta,intb);//相加方法publicStringupdate(Stringid);//直接返回方法}2.創(chuàng)建接口實現(xiàn)類實現(xiàn)方法創(chuàng)建實現(xiàn)類UserDaoImpl 實現(xiàn)UserDao接口publicclassUserDaoImplimplementsUserDao{Overridepublicintadd(inta,intb){//相加方法returnab;}OverridepublicStringupdate(Stringid){//直接返回方法returnid;}}3.創(chuàng)建增強類實現(xiàn)InvocationHandler接口創(chuàng)建增強類UserDaoProxy 實現(xiàn)InvocationHandler接口里面編寫增強的內(nèi)容//創(chuàng)建代理對象代碼里面寫增強的邏輯實現(xiàn)InvocationHandler接口classUserDaoProxyimplementsInvocationHandler{//1 把創(chuàng)建的是誰的代理對象把誰傳遞過來//UserDaoImpl//有參數(shù)構(gòu)造傳遞new UserDaoProxy(userDao)傳遞對象privateObjectobj;publicUserDaoProxy(Objectobj){this.objobj;}//invoke里面寫增強的邏輯//三個參數(shù)proxy就是代理對象newProxyInstance方法的返回對象method調(diào)用的方法args: 方法中的參數(shù)OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{//方法之前System.out.println(方法之前執(zhí)行....method.getName() :傳遞的參數(shù)...Arrays.toString(args));//被增強的方法執(zhí)行Objectresmethod.invoke(obj,args);//方法之后System.out.println(方法之后執(zhí)行....obj);returnres;}}4.使用 Proxy 類創(chuàng)建接口代理對象調(diào)用方法newProxyInstance 方法有三個參數(shù)第一參數(shù)類加載器第二參數(shù)增強方法所在的類這個類實現(xiàn)的接口UserDao 支持多個接口第三參數(shù)實現(xiàn)InvocationHandler這個接口創(chuàng)建代理對象寫增強的部分importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.EnableAspectJAutoProxy;importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;importjava.util.Arrays;publicclassJDKProxy{publicstaticvoidmain(String[]args){Class[]interfaces{UserDao.class};UserDaoImpluserDaonewUserDaoImpl();//創(chuàng)建的代理對象傳到new UserDaoProxy(userDao)里//創(chuàng)建接口實現(xiàn)類代理對象//Proxy.newProxyInstance類加載器實現(xiàn)的接口實現(xiàn)InvocationHandler這個接口的類UserDaodao(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),interfaces,newUserDaoProxy(userDao));intresultdao.add(1,2);System.out.println(result:result);}}3、AspectJ操作術(shù)語連接點類里面哪些方法可以被增強稱為連接點切入點實際被真正增強的方法通知增強實際增強的邏輯部分稱為通知增強通知有多種類型前置通知增強的方法前面執(zhí)行后置通知增強的方法后面執(zhí)行環(huán)繞通知增強的方法前后都有執(zhí)行異常通知出現(xiàn)異常執(zhí)行最終通知類似finally切面是動作把通知應(yīng)用到切入點的過程。例如將權(quán)限判斷加入到登錄方法的過程Aspect切面 Aspect 聲明類似于 Java 中的類聲明在 Aspect 中會包含著一些 Pointcut 以及相應(yīng)的 Advice。Joint point連接點表示在程序中明確定義的點典型的包括方法調(diào)用對類成員的訪問以及異常處理程序塊的執(zhí)行等等它自身還可以嵌套其它 joint point。Pointcut切點表示一組 joint point這些 joint point 或是通過邏輯關(guān)系組合起來或是通過通配、正則表達式等方式集中起來它定義了相應(yīng)的 Advice 將要發(fā)生的地方。Advice增強Advice 定義了在Pointcut里面定義的程序點具體要做的操作它通過 before、after 和 around 來區(qū)別是在每個 joint point 之前、之后還是代替執(zhí)行的代碼。Target目標(biāo)對象織入Advice的目標(biāo)對象.。Weaving織入將Aspect和其他對象連接起來, 并創(chuàng)建Adviced object的過程4、AspectJ準(zhǔn)備工作1、Spring 框架一般都是基于 AspectJ 實現(xiàn) AOP 操作AspectJ 不是 Spring 組成部分AspectJ 不需要Spring 也能獨立使用獨立 AOP 框架一般把 AspectJ 和 Spirng 框架一起使用進行 AOP 操作2、基于 AspectJ 實現(xiàn) AOP 操作兩種主要方式1.基于 xml 配置文件實現(xiàn)2.基于注解方式實現(xiàn)一般使用方式3、在項目工程里面引入 AOP 相關(guān)依賴4、切入點表達式1切入點表達式作用知道對哪個類里面的哪個方法進行增強2語法結(jié)構(gòu) execution([權(quán)限修飾符] [返回類型] [類全路徑] 方法名稱 )//*表示任意的修飾符返回類型可以省略不寫參數(shù)列表..表示//舉例 1對 com.atguigu.dao.BookDao 類里面的 add 進行增強execution(*com.atguigu.dao.BookDao.add(..))//舉例 2對 com.atguigu.dao.BookDao 類里面的所有的方法進行增強execution(*com.atguigu.dao.BookDao.*(..))//舉例 3對 com.atguigu.dao 包里面所有類類里面所有方法進行增強execution(*com.atguigu.dao.*.*(..))5、相同的切入點抽取切入點表達式相同時可以抽取出來在方法上面加入切入點注解Pointcut之后使用時只需要使用方法//相同切入點抽取Pointcut(valueexecution(* com.atguigu.spring5.aopanno.User.add(..)))publicvoidpointdemo(){}//前置通知//Before 注解表示作為前置通知Before(valuepointdemo())publicvoidbefore(){System.out.println(before.........);}5、AspectJ注解開發(fā)-重點1.編寫配置文件創(chuàng)建配置類可以不需要xml 配置文件都用注解做到ConfigurationComponentScan(basePackages{com.atguigu})//開啟注解掃描EnableAspectJAutoProxy(proxyTargetClasstrue)//開啟 Aspect 生成代理對象publicclassConfigAop{}2.創(chuàng)建被增強類在類里面定義方法//被增強類ComponentpublicclassUser{publicvoidadd(){System.out.println(add.......);}}3.創(chuàng)建增強類配置不同類型的通知編寫增強的邏輯在增強類的里面在作為通知方法上面添加通知類型注解使用切入點表達式配置importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.*;importorg.springframework.core.annotation.Order;importorg.springframework.stereotype.Component;//增強的類ComponentAspect//生成代理對象Order(3)//有多個增強類多同一個方法進行增強設(shè)置增強類優(yōu)先級Order(數(shù)字類型值)數(shù)字類型值越小優(yōu)先級越高兩個增強類都會執(zhí)行publicclassUserProxy{//相同切入點抽取Pointcut(valueexecution(* com.atguigu.spring5.aopanno.User.add(..)))publicvoidpointdemo(){}//前置通知//Before注解表示作為前置通知Before(valuepointdemo())publicvoidbefore(){System.out.println(before.........);}//后置通知返回通知AfterReturning(valueexecution(* com.atguigu.spring5.aopanno.User.add(..)))publicvoidafterReturning(){System.out.println(afterReturning.........);}//最終通知After(valueexecution(* com.atguigu.spring5.aopanno.User.add(..)))publicvoidafter(){System.out.println(after.........);}//異常通知AfterThrowing(valueexecution(* com.atguigu.spring5.aopanno.User.add(..)))publicvoidafterThrowing(){System.out.println(afterThrowing.........);}//環(huán)繞通知Around(valueexecution(* com.atguigu.spring5.aopanno.User.add(..)))publicvoidaround(ProceedingJoinPointproceedingJoinPoint)throwsThrowable{System.out.println(環(huán)繞之前.........);//被增強的方法執(zhí)行proceedingJoinPoint.proceed();System.out.println(環(huán)繞之后.........);}}4.配置xml文件方式-了解可以作為配置類方式的替代僅供參考使用比較麻煩在 spring 配置文件中開啟注解掃描需要新加xmlns:contextxmlns:aop xsi:schemaLocation?xml version1.0 encodingUTF-8?beansxmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexmlns:contexthttp://www.springframework.org/schema/contextxmlns:aophttp://www.springframework.org/schema/aopxsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd!-- 開啟注解掃描 --context:component-scanbasepackagecom.atguigu.spring5.aopanno/context:component-scan!-- 開啟 Aspect 生成代理對象--!-- 到類上面尋找Aspect注解有注解生成代理對象--aop:aspectj-autoproxy/aop:aspectj-autoproxy6、AspectJ 配置文件開發(fā)-了解1、創(chuàng)建兩個類增強類和被增強類創(chuàng)建方法packagecom.atguigu.spring5.aopxml;publicclassBook{publicvoidbuy(){System.out.println(buy.............);}}packagecom.atguigu.spring5.aopxml;publicclassBookProxy{publicvoidbefore(){System.out.println(before.........);}}2、在 spring 配置文件中創(chuàng)建兩個類對象!--創(chuàng)建對象--beanidbookclasscom.atguigu.spring5.aopxml.Book/beanbeanidbookProxyclasscom.atguigu.spring5.aopxml.BookProxy/bean3、在 spring 配置文件中配置切入點!--配置 aop 增強--aop:config!--切入點id起個名字--aop:pointcutidpexpressionexecution(* com.atguigu.spring5.aopxml.Book.buy(..))/!--配置切面--aop:aspectrefbookProxy!--增強作用在具體的方法上--aop:beforemethodbeforepointcut-refp//aop:aspect/aop:config