<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringAOP的底層實現(xiàn)——代理模式

          共 14108字,需瀏覽 29分鐘

           ·

          2021-10-01 14:01

          設(shè)計模式專欄

           


          1、什么是代理模式

          Provide a surrogate or placeholder for another object to control access to it.

          Proxy  Pattern:為其他對象提供一種代理以控制對這個對象的訪問。

          說人話:在不改變原始類(或叫被代理類)代碼的情況下,通過引入代理類來給原始類附加功能,比如Spring AOP。

          2、代理模式定義

          ①、Subject

          抽象主題角色,可以是抽象類,可以是接口,是一個最普通的業(yè)務(wù)類定義,無特殊要求。

          ②、RealSubject

          真實主題角色,也叫被代理角色,是業(yè)務(wù)邏輯的具體執(zhí)行者。

          ③、Proxy

          代理主題角色,也叫代理類,它負責(zé)對真實角色的應(yīng)用,把所有抽象主題類定義的方法限制委托給真實主題角色實現(xiàn),并在真實主題角色處理前后做一些預(yù)處理或善后工作。

          通用代碼如下:

          /**
           * 抽象主題類
           */

          public interface Subject {
              void doSomething();
          }
          /**
           * 真實主題角色
           */

          public class RealSubject implements Subject{

              @Override
              public void doSomething() {
                  //TODO 具體執(zhí)行的事
              }
          }
          /**
           * 代理主題角色
           */

          public class Proxy implements Subject{
              //要代理的具體實現(xiàn)類
              private Subject realSubject;

              public Proxy(Subject realSubject){
                  this.realSubject = realSubject;
              }
              @Override
              public void doSomething() {
                  this.before();
                  realSubject.doSomething();
                  this.after();
              }

              // 預(yù)處理
              private void before(){
                  // TODO
              }

              // 善后處理
              private void after(){
                  // TODO
              }
          }

          3、代理模式的兩種實現(xiàn)

          比如用代理模式實現(xiàn)統(tǒng)計某個接口的耗時。

          3.1 靜態(tài)代理

          ①、基于接口編程

          抽象主題類:

          public interface IUserController {
              // 登錄
              String login(String username,String password);
              // 注冊
              String register(String username,String password);
          }

          具體主題類:

          public class UserController implements IUserController{
              @Override
              public String login(String username, String password) {
                  // TODO 登錄邏輯
                  return null;
              }

              @Override
              public String register(String username, String password) {
                  // TODO 注冊邏輯
                  return null;
              }
          }

          代理主題類:

          public class UserControllerProxy implements IUserController{
              private IUserController userController;

              public UserControllerProxy(IUserController userController){
                  this.userController = userController;
              }
              @Override
              public String login(String username, String password) {
                  long startTime = System.currentTimeMillis();
                  // 登錄邏輯
                  userController.login("username","password");
                  long endTime = System.currentTimeMillis();
                  long responseTime = endTime - startTime;
                  System.out.println("接口響應(yīng)時間:"+responseTime);
                  return null;
              }

              @Override
              public String register(String username, String password) {
                  long startTime = System.currentTimeMillis();
                  // 注冊邏輯
                  userController.register("username","password");
                  long endTime = System.currentTimeMillis();
                  long responseTime = endTime - startTime;
                  System.out.println("接口響應(yīng)時間:"+responseTime);
                  return null;
              }
          }

          測試:

          因為原始類 UserController 和代理類 UserControllerProxy 實現(xiàn)相同的接口,是基于接口而非實現(xiàn)編程,將UserController類對象替換為UserControllerProxy類對象,不需要改動太多代碼

          public class StaticProxyTest {
              public static void main(String[] args) {
                  IUserController userController = new UserControllerProxy(new UserController());
                  userController.login("username","password");
                  userController.register("username","password");
              }
          }

          在上面的代碼中,代理類和具體主題類需要實現(xiàn)相同的接口,假如具體主題類沒有實現(xiàn)接口,并且不是我們開發(fā)維護的(比如來自第三方接口),我們要統(tǒng)計這個第三方接口的耗時,那應(yīng)該如何實現(xiàn)代理模式呢?

          ②、基于繼承

          繼承具體主題類,然后擴展其方法即可,直接看代碼。

          public class UserControllerProxy extends UserController {
              @Override
              public String login(String username, String password) {
                  long startTime = System.currentTimeMillis();
                  // 登錄邏輯
                  super.login("username","password");
                  long endTime = System.currentTimeMillis();
                  long responseTime = endTime - startTime;
                  System.out.println("接口響應(yīng)時間:"+responseTime);
                  return null;
              }

              @Override
              public String register(String username, String password) {
                  long startTime = System.currentTimeMillis();
                  // 注冊邏輯
                  super.register("username","password");
                  long endTime = System.currentTimeMillis();
                  long responseTime = endTime - startTime;
                  System.out.println("接口響應(yīng)時間:"+responseTime);
                  return null;
              }
          }

          3.2 動態(tài)代理

          在上面的例子中,有兩個問題:

          ①、我們需要在代理類中,將具體主題類中的所有的方法,都重新實現(xiàn)一遍,并且為每個方法都附加相似的代碼邏輯,如果方法很多,重復(fù)代碼也會很多。

          ②、如果要添加的附加功能的類有不止一個,我們需要針對每個類都創(chuàng)建一個代理類。

          那該如何解決上面的問題呢?答案就是動態(tài)代理(Dynamic Proxy)。

          動態(tài)代理:不事先為每個原始類編寫代理類,而是在運行的時候,動態(tài)地創(chuàng)建原始類對應(yīng)的代理類,然后在系統(tǒng)中用代理類替換掉原始類。

          JDK動態(tài)代理:

          public class DynamicProxyHandler implements InvocationHandler {

              private Object target;
              public DynamicProxyHandler(Object target){
                  this.target = target;
              }
              @Override
              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  long startTime = System.currentTimeMillis();
                  Object result = method.invoke(this.target, args);
                  long endTime = System.currentTimeMillis();
                  long responseTime = endTime - startTime;
                  System.out.println("接口響應(yīng)時間:"+responseTime);
                  return result;
              }
          }

          測試:

          public class DynamicProxyTest {
              public static void main(String[] args) {
                  // 1、創(chuàng)建具體主題類
                  IUserController userController = new UserController();
                  // 2、創(chuàng)建 Handler
                  DynamicProxyHandler proxyHandler = new DynamicProxyHandler(userController);
                  // 3、動態(tài)產(chǎn)生代理類
                  IUserController o = (IUserController)Proxy.newProxyInstance(userController.getClass().getClassLoader(),
                          userController.getClass().getInterfaces(),
                          proxyHandler);
                  o.login("username","password");
                  o.register("username","password");
              }
          }

          這是 JDK 動態(tài)代理,利用反射機制生成一個實現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理,代理對象是在程序運行時產(chǎn)生的,而不是編譯期,要求是具體主題類必須實現(xiàn)接口。

          另外一種方式是 Cglib 動態(tài)代理。CGLIB(Code Generation Library)是一個基于ASM的字節(jié)碼生成庫,它允許我們在運行時對字節(jié)碼進行修改和動態(tài)生成,也就是通過修改字節(jié)碼生成子類來處理。

          Cglib 動態(tài)代理:

          public class UserController{
              public String login(String username, String password) {
                  // TODO 登錄邏輯
                  System.out.println("登錄");
                  return null;
              }

              public String register(String username, String password) {
                  // TODO 注冊邏輯
                  System.out.println("注冊");
                  return null;
              }
          }

          注意:真實主題類是沒有實現(xiàn)接口的。

          public class CglibDynamicProxy implements MethodInterceptor {
              private Object target;

              public CglibDynamicProxy(Object target){
                  this.target = target;
              }

              // 給目標(biāo)創(chuàng)建代理對象
              public Object newProxyInstance(){
                  // 1.工具類
                  Enhancer enhancer = new Enhancer();
                  // 2.設(shè)置父類
                  enhancer.setSuperclass(target.getClass());
                  // 3.設(shè)置回調(diào)函數(shù)
                  enhancer.setCallback(this);
                  // 4.創(chuàng)建子類(代理對象)
                  return enhancer.create();
              }

              @Override
              public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                  long startTime = System.currentTimeMillis();
                  Object result = method.invoke(this.target, args);
                  long endTime = System.currentTimeMillis();
                  long responseTime = endTime - startTime;
                  System.out.println("接口響應(yīng)時間:"+responseTime);
                  return result;
              }
          }

          測試:

          public class CglibDynamicProxyTest {
              public static void main(String[] args) {
                  UserController userController = new UserController();
                  CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy(userController);
                  UserController o = (UserController)cglibDynamicProxy.newProxyInstance();
                  o.login("username","password");
                  o.register("username","password");
              }
          }

          4、代理模式優(yōu)點

          ①、職責(zé)清晰

          真實的角色就是實現(xiàn)實際的業(yè)務(wù)邏輯, 不用關(guān)心其他非本職責(zé)的事務(wù), 通過后期的代理完成一件事務(wù), 附帶的結(jié)果就是編程簡潔清晰。

          ②、高擴展性

          具體主題角色是隨時都會發(fā)生變化的, 只要它實現(xiàn)了接口, 甭管它如何變化,代理類完全都可以在不做任何修改的情況下使用。

          5、代理模式應(yīng)用場景

          ①、業(yè)務(wù)系統(tǒng)的非功能性需求開發(fā)

          這是最常用的一個場景。比如:監(jiān)控、統(tǒng)計、鑒權(quán)、限流、事務(wù)、冪等、日志。我們將這些附加功能與業(yè)務(wù)功能解耦,放到代理類中統(tǒng)一處理,讓程序員只需要關(guān)注業(yè)務(wù)方面的開發(fā)。

          典型例子就是 SpringAOP。

          ②、RPC

          RPC(遠程代理) 框架也可以看作一種代理模式,通過遠程代理,將網(wǎng)絡(luò)通信、數(shù)據(jù)編解碼等細節(jié)隱藏起來??蛻舳嗽谑褂?RPC 服務(wù)的時候,就像使用本地函數(shù)一樣,無需了解跟服務(wù)器交互的細節(jié)。除此之外,RPC 服務(wù)的開發(fā)者也只需要開發(fā)業(yè)務(wù)邏輯,就像開發(fā)本地使用的函數(shù)一樣,不需要關(guān)注跟客戶端的交互細節(jié)。

          關(guān)于我

          可樂是一個熱愛技術(shù)的Java程序猿,公眾號「IT可樂」定期分享有趣有料的精品原創(chuàng)文章!

          非常感謝各位人才能看到這里,原創(chuàng)不易,文章如果有幫助可以關(guān)注、點贊、分享或評論,這都是對我的莫大支持!

          愿你我人生盡量沒有遺憾的事,愿你我都能奔赴在各自想去的路上。


          瀏覽 57
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲精品人妻在线 | 正在播放学生妹一级 | 四虎亚洲 | 亚洲免费视频网 | 草大B老骚B |