mybatis之動態代理


mybatis之動態代理的應用

 

         在前文(https://www.cnblogs.com/NYfor2018/p/9093472.html)我們知道了,Mybatis的使用需要用到Mapper映射文件,一個是映射接口,另一個是映射XML文件(此處不詳談映射文件XML),在應用中我們可以感覺到,映射接口似乎對接着XML文件中的實現命令,可是我們在運行程序是時候調用的往往是Mapper接口,而不是一個包含邏輯的實現類。很顯然Mapper產生了代理類。

         首先,什么是代理模式?代理模式的定義:為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。(取自百度百科:https://baike.baidu.com/item/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/8374046?fr=aladdin

         舉個栗子,如圖:

 

 

         我們租房的時候一般是去找中介,而不是直接去找包租婆。放在代理模式這里來說,就是,我們在訪問真實的對象的時候,往往不是直接去訪問真實對象,而是通過代理對象,來對真實對象進行訪問。

         為什么要使用代理模式?通過代理,一方面可以控制如何訪問真正的服務對象,提供額外的服務。另外一方面有機會通過重寫一些類來滿足特定的需要。就像是,有時候租客反映的一些問題,中介可以直接解決而不需要麻煩到包租婆。

         一般來說,動態代理分為兩種:一種是JDK反射機制提供的代理;另一種是CGLB代理。在JDK提供的代理,我們必須要提供接口;而在CGLIB中則不需要提供接口。

         在學習動態代理之前,先了解一下反射的基礎。

 

反射簡單應用

import java.lang.reflect.Method;
public class ReflectService {

         public void sayHello(String name) {

                   System.out.println("hello"+name);

         }

         public static void main(String[] args) throws Exception, IllegalAccessException, ClassNotFoundException {

                   //通過反射創建ReflectService對象

                   Object service = Class.forName(ReflectService.class.getName()).newInstance();

                   //獲取服務方法

                   Method method = service.getClass().getMethod("sayHello", String.class);

                   //反射調用方法

                   method.invoke(service, "zhangsan");
         }
}

 

①   先根據類名,來創建實例對象,所以就找了ReflectService類的類名。

②   然后再根據實例對象來找回它的方法,參數是方法名及其參數。

③   利用反射機制來調用ReflectService類的sayHello方法。

 

JDK動態代理

 

JDK的動態代理,是由JDK的java.lang.reflect.*包提供支持的,我們需要完成以下步驟:

① 編寫服務類和接口,服務類是真正的服務提供者,而JDK代理中接口是必要的。

② 編寫代理類,提供綁定和代理方法。

 

首先,先編寫動態代理的接口

public interface HelloService {

         public void sayHello(String name);

}

 

接着,寫一個這個接口的實現類

public class HelloServiceImpl implements HelloService{

         @Override
         public void sayHello(String name) {
                   System.out.println("hello "+name);
         }
}

 

然后,寫一個代理類,提供真實對象的綁定和代理方法。代理類的要求是實現InvocationHandler接口的代理方法,當一個對象被綁定后,執行其方法的時候就會進入到代理方法里。

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class HelloServiceProxy implements InvocationHandler{

         /**

          * 真實的服務對象

          */

         private Object target;

        

         /**

          *

          * @param target

          * @return 綁定委托對象並返回一個代理類

          */

         public Object bind(Object target) {

                   this.target = target;

                   //取得代理對象

                   //public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException

                   //loader服務對象的類加載器,interfaces是加載服務對象的接口,h是執行這個代理類的方法的執行者

                   return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

         }

        

         @Override

         /**

          * 通過代理對象調用方法首先進入這個方法

          * invoke方法的參數分別是:proxy是代理對象,method是被調用的方法,args是方法的參數

          */

         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                   System.err.println("#########我是JDK動態代理##########");

                   Object result = null;

                   //反射方法前調用

                   System.err.println("我准備說hello");

                   result = method.invoke(target, args);

                   //反射方法后調用

                   System.err.println("我說過hello了");

                   return result;

         }

}

 

 

最后,編寫一個測試類:

public class HelloServiceMain {

         public static void main(String[] args) {

                   HelloServiceProxy HelloHandler = new HelloServiceProxy();

                   HelloService proxy = (HelloService)HelloHandler.bind(new HelloServiceImpl());

                   proxy.sayHello("張三");

         }

}

 

運行出來的結果是:

 

 

整個過程可以這樣理解:

先創建一個代理類對象a,然后用代理類對象綁定真正提供服務對象b,返回一個接口b+,再用這個接口b+來進行服務。

如果我們把proxy.sayHello(“張三”);注釋掉:

 

 

我們會發現控制台什么東西都沒有輸出。

所以我們可以知道,代理類的invoke方法,只有在代理的真正提供服務的對象被調用的時候,才會起作用。

所以這時候我們可以這樣理解:

 

 

         挑選的人相當於訪問者,相親交流平台相當於代理,被挑選的人相當於真正提供服務的對象。當挑選的人在向相親交流平台要求得到被挑選的人的信息,實際上,提供信息的人不是平台,是被挑選的人。挑選的人在對被挑選的人打招呼,而不是對相親交流平台打招呼。而且,相親交流平台不只是為一個獨特的挑選的人提供被挑選的人的信息,只要有忍看上被挑選的人,相親交流平台就可以提供ta的信息。

 

 

CGLIB動態代理

此處僅突出CGLIB動態代理跟JDK動態代理在代理類上的區別:

import java.lang.reflect.Method;

 

import org.springframework.cglib.proxy.Enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;

import org.springframework.cglib.proxy.MethodProxy;

 

public class HelloServiceCgLib implements MethodInterceptor{

         private Object target;

         public Object getInstance(Object target) {

                   this.target = target;

                   Enhancer enhancer = new Enhancer();

                   enhancer.setSuperclass(this.target.getClass());

                   //回調方法

                   enhancer.setCallback(this);

                   return enhancer.create();

         }

         @Override

         public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

                   System.err.println("##########我是CGLIB的動態代理##########");

                   //反射方法前調用

                   System.err.println("我准備說hello");

                   Object returnObj = proxy.invokeSuper(obj, args);

                   //反射方法后調用

                   System.err.println("我說過hello了");

                   return returnObj;

         }

}

可以看到CGLIB的代理類是實現MethodInterceptor的代理方法。在mybatis中通常在延遲加載的時候才會用到CGLIB的動態代理。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM