爲其餘對象提供一種代理以控制對這個對象的訪問。java
中介隔離:在某些狀況下,一個客戶類不想或者不能直接引用一個委託對象,而代理類對象能夠在客戶類和委託對象之間起到中介的做用,其特徵是代理類和委託類實現相同的接口。程序員
開閉原則,增長功能:代理類除了是客戶類和委託類的中介以外,咱們還能夠經過給代理類增長額外的功能來擴展委託類的功能,這樣作咱們只須要修改代理類而不須要再修改委託類,符合代碼設計的開閉原則。代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及過後對返回結果的處理等。代理類自己並不真正實現服務,而是同過調用委託類的相關方法,來提供特定的服務。真正的業務功能仍是由委託類來實現,可是能夠在業務功能執行的先後加入一些公共的服務。例如咱們想給項目加入緩存、日誌這些功能,咱們就可使用代理類來完成,而不必打開已經封裝好的委託類。web
代理模式主要包含三個角色,即抽象主題角色(Subject)、委託類角色(被代理角色,Proxied)以及代理類角色(Proxy)spring
抽象主題角色(Subject):能夠是接口,也能夠是抽象類數據庫
委託類角色(proxied):真實主題角色,業務邏輯的具體執行者設計模式
代理類角色(Proxy):內部含有對真實對象RealSubject的引用,負責對真實主題角色的調用,並在真實主題角色處理先後作預處理和後處理緩存
SpringAop、日誌收集、權限控制、過濾器、RPC遠程調用框架
靜態代理 和 動態代理函數
靜態代理
靜態代理是由程序員建立或工具生成代理類的源碼,再編譯代理類。spring-boot
所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就肯定了。
一句話,本身手寫代理類就是靜態代理。
基於接口實現方式
//主題(Subject) public interface OrderService { void order(); }//實現接口 public class OrderServiceImpl implements OrderService { public void order() { System.out.println("用戶下單操做.."); } }//代理類 public class OrderServiceProxy implements OrderService { /** * 代理對象 */ private OrderService proxiedOrderService; public OrderServiceProxy( OrderService orderService) { this.proxiedOrderService=orderService; } public void order() { System.out.println("日誌收集開始.."); proxiedOrderService.order(); System.out.println("日誌收集結束.."); } }//測試 public class ClientTest { public static void main(String[] args) { OrderService orderService = new OrderServiceProxy(new OrderServiceImpl()); orderService.order(); } }接口繼承方式實現
//繼承接口實現類 public class OrderServiceProxy extends OrderServiceImpl { public void order() { System.out.println("日誌收集開始.."); super.order(); System.out.println("日誌收集結束.."); } }//測試 public class ClientTest { public static void main(String[] args) { OrderService orderService = new OrderServiceProxy(); orderService.order(); } }
動態代理是在實現階段不用關心代理類,而在運行階段才指定哪個對象。
動態代理類的源碼是在程序運行期間由JVM根據反射等機制動態的生成 。
JDK動態代理的通常步驟以下:
1.建立被代理的接口和類;
2.實現InvocationHandler接口,對目標接口中聲明的全部方法進行統一處理;
3.調用Proxy的靜態方法,建立代理類並生成相應的代理對象;
//主題()Subject public interface OrderService { void order(); }//實現接口 public class OrderServiceImpl implements OrderService { public void order() { System.out.println("修改數據庫訂單操做.."); } }//代理類 public class JdkInvocationHandler implements InvocationHandler { /** * 目標代理對象 */ public Object target; public JdkInvocationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(">>>日誌收集開始>>>>"); // 執行代理對象方法 Object reuslt = method.invoke(target, args); System.out.println(">>>日誌收集結束>>>>"); return reuslt; } /** * 獲取代理對象接口 * * @param <T> * @return */ public <T> T getProxy() { return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } } JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl()); OrderService proxy = jdkInvocationHandler.getProxy(); proxy.order();原理分析
1. 獲取代理的生成的class文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
2.使用反編譯工具該Proxy0.class
注意:繼承了Proxy類,實現了代理的接口,因爲java不能多繼承,這裏已經繼承了Proxy類了,不能再繼承其餘的類,因此JDK的動態代理不支持對實現類的代理,只支持接口的代理。
Cglib是一個強大的,高性能,高質量的代碼生成類庫。它能夠在運行期擴展JAVA類與實現JAVA接口。其底層實現是經過ASM字節碼處理框架來轉換字節碼並生成新的類。大部分功能其實是ASM所提供的,Cglib只是封裝了ASM,簡化了ASM操做,實現了運行期生成新的class。
運行時動態的生成一個被代理類的子類(經過ASM字節碼處理框架實現),子類重寫了被代理類中全部非final的方法。在子類中採用方法攔截的技術攔截全部父類方法的調用,順勢植入橫切邏輯。
優勢:JDK動態代理要求被代理的類必須實現接口,當須要代理的類沒有實現接口時Cglib代理是一個很好的選擇。另外一個優勢是Cglib動態代理比使用java反射的JDK動態代理要快
缺點:對於被代理類中的final方法,沒法進行代理,由於子類中沒法重寫final函數
版權@須臾之餘https://my.oschina.net/u/3995125
實現MethodInterceptor接口的intercept方法後,全部生成的代理方法都調用這個方法。
intercept方法的具體參數有
obj 目標類的實例
1.method 目標方法實例(經過反射獲取的目標方法實例) 2.args 目標方法的參數 3.proxy 代理類的實例該方法的返回值就是目標方法的返回值。
public class OrderServiceImpl { public void order() { System.out.println("用戶下單操做.."); } }public class CglibMethodInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("<<<<<日誌收集開始...>>>>>>>"); Object reuslt = proxy.invokeSuper(obj, args); System.out.println("<<<<<日誌收集結束...>>>>>>>"); return reuslt; } } System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code"); CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor(); Enhancer enhancer = new Enhancer(); // 設置代理類的付類 enhancer.setSuperclass(OrderServiceImpl.class); // 設置回調對象 enhancer.setCallback(cglibMethodInterceptor); // 建立代理對象 OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create(); orderServiceImpl.order();Maven依賴
<dependencies> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.12</version> </dependency> </dependencies>
>>>>cglib日誌收集開始....
執行訂單業務邏輯代碼
>>>>cglib日誌收集結束....
靜態代理須要本身寫代理類,而動態代理不須要寫代理類。
JDK動態代理底層實現:
JDK的動態代理使用Java的反射技術生成動態代理類,只能代理實現了接口的類, 沒有實現接口的類不能實現動態代理。
CGLIB動態代理底層實現:
運行時動態的生成一個被代理類的子類(經過ASM字節碼處理框架實現),子類重寫了被代理類中全部非final的方法,在子類中採用方法攔截的技術攔截全部父類方法的調用,不須要被代理類對象實現接口,從而CGLIB動態代理效率比Jdk動態代理反射技術效率要高。
@Aspect @Component @Slf4j public class AopLogAspect { // 申明一個切點 裏面是 execution表達式 @Pointcut("execution(* com.xuyu.controller.*.*(..))") private void serviceAspect() { } // 請求method前打印內容 @Before(value = "serviceAspect()") public void methodBefore(JoinPoint joinPoint) { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder .getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); // 打印請求內容 log.info("===============請求內容==============="); log.info("請求地址:" + request.getRequestURL().toString()); log.info("請求方式:" + request.getMethod()); log.info("請求類方法:" + joinPoint.getSignature()); log.info("請求類方法參數:" + Arrays.toString(joinPoint.getArgs())); log.info("===============請求內容==============="); } // 在方法執行完結後打印返回內容 @AfterReturning(returning = "o", pointcut = "serviceAspect()") public void methodAfterReturing(Object o) { log.info("--------------返回內容----------------"); log.info("Response內容:" + o.toString()); log.info("--------------返回內容----------------"); } }
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <dependencies> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.12</version> </dependency> <!-- sprinboot web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
===============請求內容===============
: 請求地址:http://127.0.0.1:8080/getUser
: 請求方式:GET
: 請求類方法:String com.xuyu.service.controller.IndexController.getUser(String,Integer)
: 請求類方法參數:[xuyu, 2]
: ===============請求內容===============
: >>>userName:{},xuyu
: --------------返回內容----------------
: Response內容:success_getUser
: --------------返回內容----------------
版權@須臾之餘https://my.oschina.net/u/3995125
本文參考:螞蟻課堂