Java反射 - 動(dòng)態(tài)代理

使用Java反射,您可以在運(yùn)行時(shí)創(chuàng)建接口的動(dòng)態(tài)實(shí)現(xiàn)。 你可以使用類java.lang.reflect.Proxy。 這個(gè)類的名字是我將這些動(dòng)態(tài)接口實(shí)現(xiàn)稱為動(dòng)態(tài)代理的原因。 動(dòng)態(tài)代理可以用于許多不同的目的,例如, 數(shù)據(jù)庫(kù)連接和事務(wù)管理,用于單元測(cè)試的動(dòng)態(tài)模擬對(duì)象,以及其他類似于AOP的方法攔截目的。

創(chuàng)建代理

您使用Proxy.newProxyInstance()方法創(chuàng)建動(dòng)態(tài)代理。 newProxyInstance()方法需要3個(gè)參數(shù):

  1. 用于“加載”動(dòng)態(tài)代理類的ClassLoader。
  2. 要實(shí)現(xiàn)的接口數(shù)組。
  3. 一個(gè)InvocationHandler將代理上的所有方法調(diào)用轉(zhuǎn)發(fā)。
    這里是一個(gè)例子:
InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                            MyInterface.class.getClassLoader(),
                            new Class[] { MyInterface.class },
                            handler);

運(yùn)行此代碼后,代理變量包含MyInterface接口的動(dòng)態(tài)實(shí)現(xiàn)。 所有對(duì)代理的調(diào)用都將被轉(zhuǎn)發(fā)給一般的InvocationHandler接口的處理程序?qū)崿F(xiàn)。 InvocationHandler在下一節(jié)中介紹。

InvocationHandler

如前所述,您必須將InvocationHandler實(shí)現(xiàn)傳遞給Proxy.newProxyInstance()方法。 所有對(duì)動(dòng)態(tài)代理的方法調(diào)用都被轉(zhuǎn)發(fā)給這個(gè)InvocationHandler實(shí)現(xiàn)。 以下是InvocationHandler接口的外觀:

public interface InvocationHandler{
  Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable;
}

下面是一個(gè)示例實(shí)現(xiàn):

public class MyInvocationHandler implements InvocationHandler{

  public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {
    //do something "dynamic"
  }
}

傳遞給invoke()方法的proxy參數(shù)是實(shí)現(xiàn)接口的動(dòng)態(tài)代理對(duì)象。 大多數(shù)情況下你不需要這個(gè)對(duì)象。

傳遞給invoke()方法的Method對(duì)象表示在動(dòng)態(tài)代理實(shí)現(xiàn)的接口上調(diào)用的方法。 從Method對(duì)象中,您可以獲取方法名稱,參數(shù)類型,返回類型等。有關(guān)更多信息,請(qǐng)參閱Methods上的文本。

Object [] args數(shù)組包含調(diào)用接口中的方法時(shí)傳遞給代理的參數(shù)值。 注意:被實(shí)現(xiàn)的接口中的基本元素(int,long等)被封裝在它們的對(duì)象(Integer,Long等)中。

數(shù)據(jù)庫(kù)連接和事務(wù)管理

Spring框架有一個(gè)事務(wù)代理,可以為你啟動(dòng)和提交/回滾事務(wù)。 在高級(jí)連接和事務(wù)劃分和傳播的文本中更詳細(xì)地描述了這個(gè)工作原理,所以我只簡(jiǎn)要描述它。 通話順序變成這

web controller --> proxy.execute(...);
  proxy --> connection.setAutoCommit(false);
  proxy --> realAction.execute();
    realAction does database work
  proxy --> connection.commit()
DI容器

依賴注入容器有一個(gè)強(qiáng)大的功能,允許你將整個(gè)容器注入到它生成的bean中。 但是,由于不需要對(duì)容器接口進(jìn)行依賴,因此容器可以自行適應(yīng)設(shè)計(jì)的自定義工廠接口。 你只需要接口。 沒(méi)有執(zhí)行。 因此,工廠界面和你的類可以看起來(lái)像這樣:

public interface IMyFactory {
  Bean   bean1();
  Person person();
  ...
}
public class MyAction{

  protected IMyFactory myFactory= null;

  public MyAction(IMyFactory factory){
    this.myFactory = factory;
  }

  public void execute(){
    Bean bean = this.myFactory.bean();
    Person person = this.myFactory.person();
  }

}

當(dāng)MyAction類調(diào)用由容器注入其構(gòu)造函數(shù)的IMyFactory實(shí)例上的方法時(shí),方法調(diào)用將被轉(zhuǎn)換為對(duì)IContainer.instance()方法的調(diào)用,IContainer.instance()方法是用于從容器中獲取實(shí)例的方法。 這樣一來(lái),一個(gè)對(duì)象可以在運(yùn)行時(shí)使用Butterfly Container作為工廠,而不僅僅是在創(chuàng)建時(shí)注入自己的依賴。 而這個(gè)沒(méi)有任何容器特定接口的依賴。

類AOP方法攔截

Spring框架可以攔截方法調(diào)用給一個(gè)給定的bean,前提是bean實(shí)現(xiàn)了一些接口。 Spring框架將這個(gè)bean包裝在一個(gè)動(dòng)態(tài)代理中。 所有對(duì)bean的調(diào)用都會(huì)被代理攔截。 代理可以決定在其他對(duì)象上調(diào)用其他對(duì)象的方法,而不是或者在方法調(diào)用委托給包裝的bean之后。

AOP動(dòng)態(tài)代理底層實(shí)戰(zhàn)

package com.reflection.detail;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * Created by Fant.J.
 * 2018/2/7 17:01
 */
public class Reflection_DynamicProx {

    public static void main(String[] args) {
        CommonDao commonDao = new CommonDaoImpl();
        InvocationHandler handler = new MyInvocationHandler(commonDao);
        CommonDao proxy = (CommonDao) Proxy.newProxyInstance(
                commonDao.getClass().getClassLoader(),
                commonDao.getClass().getInterfaces(),
                handler);
        proxy.insert(2,3);
        proxy.insert(3,3);
        proxy.delete(5,2);
    }

    /**
     * 重寫(xiě)InvocationHandler
     * 將代理上的所有方法調(diào)用轉(zhuǎn)發(fā)
     * 相當(dāng)于 Method一章節(jié)中,我們講到的反射實(shí)例化
     * aClass.getMethod("setName", String.class);
     * 然后調(diào)用ethod.invoke(people,"Fant.J");
     */
    static final class MyInvocationHandler implements InvocationHandler{
        //這個(gè)就是我們要代理的真實(shí)對(duì)象
        private Object subject;

        //構(gòu)造方法,需要把我們要代理的真實(shí)對(duì)象 傳進(jìn)來(lái) 供給invoke方法使用
        public MyInvocationHandler(Object subject)
        {
            this.subject = subject;
        }

        /**
         * 重寫(xiě)invoke方法
         * @param proxy 代理對(duì)象
         * @param method 代理方法
         * @param args 代理方法傳入的參數(shù)
         * @return 返回反射結(jié)果
         * @throws Throwable 
         */

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //do something "dynamic"
            System.out.println("方法【" + method.getName() + "】開(kāi)始執(zhí)行, 參數(shù)為" + Arrays.asList(args) + "...");
            long start = System.currentTimeMillis();
            Object result = method.invoke(subject, args);
            long end = System.currentTimeMillis();
            System.out.println("方法【" + method.getName() + "】執(zhí)行完成,運(yùn)算結(jié)果為:" + result + ", 用時(shí)" + (end - start) + "毫秒!");
            return result;

        }
    }
    static interface CommonDao{
        public int insert(int a,int b);
        public int delete(int a,int b);
    }
    static class CommonDaoImpl implements CommonDao{
        @Override
        public int insert(int a, int b) {
            return a+b;
        }

        @Override
        public int delete(int a, int b) {
            return a-b;
        }
    }
}

方法【insert】開(kāi)始執(zhí)行, 參數(shù)為[2, 3]...
方法【insert】執(zhí)行完成,運(yùn)算結(jié)果為:5, 用時(shí)0毫秒!
方法【insert】開(kāi)始執(zhí)行, 參數(shù)為[3, 3]...
方法【insert】執(zhí)行完成,運(yùn)算結(jié)果為:6, 用時(shí)0毫秒!
方法【delete】開(kāi)始執(zhí)行, 參數(shù)為[5, 2]...
方法【delete】執(zhí)行完成,運(yùn)算結(jié)果為:3, 用時(shí)0毫秒!

有什么不懂的可以去先看下Java反射前面的東西。Java反射文集
.也可以在文章下方留言。DI容器相繼推出。

項(xiàng)目代碼:github鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,872評(píng)論 11 349
  • 代理模式 定義 為實(shí)際對(duì)象提供一個(gè)代理,以控制對(duì)實(shí)際對(duì)象的訪問(wèn)。代理類負(fù)責(zé)為委托類預(yù)處理消息,過(guò)濾消息并轉(zhuǎn)發(fā)消息,...
    CoderBao閱讀 1,266評(píng)論 0 0
  • 這部分主要是開(kāi)源Java EE框架方面的內(nèi)容,包括Hibernate、MyBatis、Spring、Spring ...
    雜貨鋪老板閱讀 1,572評(píng)論 0 2
  • 01 去年九月,機(jī)緣巧合,參加過(guò)一次閱卷工作。 地點(diǎn)在安徽一個(gè)小縣城,二三十名學(xué)生被關(guān)在一個(gè)院子里,每日,除了吃喝...
    谷潤(rùn)良閱讀 1,510評(píng)論 4 21
  • 我們認(rèn)識(shí)七個(gè)年頭,在一起六年。如果說(shuō)自此以后再也沒(méi)有什么干系,我愿意停下來(lái)一段時(shí)間,緩緩情緒,好好的給我們六年的感...
    爆米花198306閱讀 334評(píng)論 1 4

友情鏈接更多精彩內(nèi)容