Java代理的初步理解

代理的作用

在實(shí)際的項(xiàng)目中,往往有一些通用的功能需要穿插在項(xiàng)目功能代碼的各個(gè)角落,比如很常見的log的記錄,還有接口訪問的權(quán)限檢查。總結(jié)下來,這類的代碼耦合,一種是預(yù)處理(信息過濾、權(quán)限校驗(yàn)),用來控制訪問;另一種是其他通用功能的織入用于功能擴(kuò)展。

使用代理的目的,就是解耦。把瑣碎的事情交給代理類去做,讓核心業(yè)務(wù)代碼專注于業(yè)務(wù),而無須關(guān)注那些通用功能的實(shí)現(xiàn),且那些通用功能的代碼也不會(huì)污染了核心的業(yè)務(wù)代碼。經(jīng)典的Spring Aop實(shí)際上就是代理的巧妙運(yùn)用。

另外EasyMock這類方便unit test的工具,也是基于代理。也正是對(duì)EasyMock的好奇,才來了解一下java的代理。

java代理的實(shí)現(xiàn)方法

  1. 靜態(tài)代理

  2. 動(dòng)態(tài)代理

    • 靜態(tài)代理的實(shí)現(xiàn)思路

      一個(gè)目標(biāo)類(被代理類),一個(gè)代理類,這兩個(gè)是核心,還有一個(gè)抽象接口,其實(shí)如果只是代理這個(gè)功能來說,抽象接口的意義是不明顯的。使用了抽象接口,主要在于在面向接口編程時(shí),代理類可以很優(yōu)雅地取代目標(biāo)類。

      首先說不使用抽象接口的。實(shí)現(xiàn)機(jī)制就是代理類持有目標(biāo)類的對(duì)象,因?yàn)楝F(xiàn)在代理類持有了這個(gè)對(duì)象,就可以不直接使用目標(biāo)類對(duì)象來invoke方法,而是調(diào)用代理類的某個(gè)方法,然后代理類方法間接地去invoke目標(biāo)方法,這樣的間接過程,就可以在目標(biāo)方法的執(zhí)行前后追加其他業(yè)務(wù)邏輯。 原本的Target.invoke()也變成了Proxy.invoke();

      代碼:

//目標(biāo)類
public class Real implements IService{
    @Override
    public void execute(){
        //do something
    }
}
  //代理類
  public class Proxy implements IService{
        private Real realObj;
        public Proxy(Real realObj){
            this.realObj = realObj;
        }
        @Override
        public void execute(){
            //do pre
            realObj.execute();
            //do after
        }
} 
//抽象類
public Interface IService{
  public void execute();
}

實(shí)際使用是將實(shí)體類傳給代理的構(gòu)造器,調(diào)用代理類的方法,就起到代理的作用。

public static void main(String []agrs){
  IService proxy = new Proxy(new Real());
  proxy.execute();  
}

  • 動(dòng)態(tài)代理

JDK提供了動(dòng)態(tài)代理的方法。動(dòng)態(tài)代理的所謂動(dòng)態(tài),動(dòng)的是代理類。動(dòng)態(tài)代理和靜態(tài)代理的區(qū)別,就是動(dòng)態(tài)生成了代理類,免去了手工編寫代理類的無趣。與靜態(tài)相比,可以少寫代碼偷點(diǎn)懶。

動(dòng)態(tài)代理的實(shí)現(xiàn)思路和靜態(tài)是一致的。簡(jiǎn)要記錄一下api的使用。

public static void main(String[] args) {
        IService real = new RealService();
        InvocationHandler handler = new MyInvocationHandler(real);
        IService proxy = (IService)Proxy.newProxyInstance(real.getClass().getClassLoader(),real.getClass().getInterfaces(),handler);
        proxy.execute();
    
    }

InvocationHandler是JDK定義的接口,是一種Callback接口,通過它傳人的回調(diào)函數(shù),代理類對(duì)象上的的所有方法調(diào)用都回到這個(gè)Callback中的可執(zhí)行代碼。使用InvocationHandler方法既可以創(chuàng)建類實(shí)現(xiàn)InvocationHandler接口,也可以使用匿名內(nèi)部類。

動(dòng)態(tài)代理的主要優(yōu)點(diǎn)是,減少代碼量,并且實(shí)現(xiàn)的Callback類可重用,但是此方法的局限是只能代理實(shí)現(xiàn)了接口的類,因?yàn)樵赑roxy.newProxyInstance()方法中,使用類加載器和目標(biāo)類的接口在運(yùn)行時(shí)生成了代理類。在編譯后實(shí)際上可以發(fā)現(xiàn)一個(gè)包含*$*的class文件。

  • cglib——JDK動(dòng)態(tài)代理的補(bǔ)全方案

cglib可以代理未實(shí)現(xiàn)接口的類。

CGLIB原理是動(dòng)態(tài)生成一個(gè)目標(biāo)類的子類,子類重寫目標(biāo)類的所有不是final的方法(因?yàn)閒inal方法無法重寫),在方法體中織入了回調(diào)函數(shù)的調(diào)用。

其使用方法是基于Enhancer類,Enhancer設(shè)置繼承的父類(即為要代理的類),設(shè)置回調(diào)函數(shù)。現(xiàn)有的回調(diào)函數(shù)有:

  • net.sf.cglib.proxy.FixedValue, all method calls return a fixed value which is generated by the anonymous FixedValue implementation.
Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyClass.class);
        // FixedValue is a intercepter
        enhancer.setCallback(new FixedValue() {

            @Override
            public Object loadObject() throws Exception {
                System.out.println("FixedValue");
                return "hello,cglib";
            }

        });

        MyClass proxy = (MyClass) enhancer.create();
        proxy.dosome();
        proxy.execute();
        System.out.println(proxy.getClass());  //final method
//author said:
/*The anonymous subclass of FixedValue would become hardly referenced from the enhanced SampleClass such that neither the anonymous FixedValue instance or the class holding the @Test method would ever be garbage collected. This can introduce nasty memory leaks in your applications. Therefore, do not use non-static inner classes with cglib. (I only use them in this overview for keeping the examples short.)
*/
  • net.sf.cglib.proxy.InvocationHandler,和jdk中同款,它攔截所有方法到invoke(),和FixedValue類似,但是比前者要更靈活一點(diǎn)。在其內(nèi)部處理要注意無限遞歸調(diào)用。
Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(MyClass.class);
    // net.sf.cglib.proxy.InvocationHandler is a intercepter
    enhancer.setCallback(new net.sf.cglib.proxy.InvocationHandler() {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("InvocationHandler");
            return null;
        }

    });

    MyClass proxy = (MyClass) enhancer.create();
    proxy.dosome();
    proxy.execute();
  • net.sf.cglib.proxy.MethodInterceptor,The MethodInterceptor allows full control over the intercepted method and offers some utilities for calling the method of the enhanced class in their original state.它的回調(diào)方法里proxy.invokeSuper(obj, args);可以通過父類調(diào)用其它方法,而不必全部都指向回調(diào)函數(shù)。
Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyClass.class);
        // MethodInterceptor
        enhancer.setCallback(new MethodInterceptor() {

            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
                    return "Hello cglib!";
                  } else {
                    return proxy.invokeSuper(method, args);
                  }
            }
        });

        MyClass proxy = (MyClass) enhancer.create();
        proxy.dosome();
        proxy.execute();
        proxy.hashCode();
        System.out.println(proxy.getClass());

這些都是既有的回調(diào)函數(shù),需要更加的自定義,可以使用Enhancer#setCallbackFilter(CallbackHelper callbackHelper)方法自定義回調(diào)函數(shù),callbackHelper返回回調(diào)函數(shù)對(duì)象,至于返回那種,就是可以在callbackHelper中自定義了,如對(duì)不同的方法,指定不同的回調(diào)函數(shù)。

Enhancer enhancer = new Enhancer();
CallbackHelper callbackHelper = new CallbackHelper(SampleClass.class, new Class[0]) {
    @Override
    protected Object getCallback(Method method) {
      if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
        return new FixedValue() {
          @Override
          public Object loadObject() throws Exception {
            return "Hello cglib!";
          };
        }
      } else {
        return NoOp.INSTANCE; // A singleton provided by NoOp.
      }
    }
  };
  enhancer.setSuperclass(MyClass.class);
  enhancer.setCallbackFilter(callbackHelper);
  enhancer.setCallbacks(callbackHelper.getCallbacks());

//code snippet via https://github.com/cglib/cglib/wiki/Tutorial

summary

java代理的實(shí)現(xiàn)機(jī)制,大概就是這兩步:(1)創(chuàng)建目標(biāo)類的可替代類;(2)織入回調(diào)方法。

回頭去看靜態(tài)代理,實(shí)際上就是代理實(shí)現(xiàn)的基礎(chǔ)與核心。后面的動(dòng)態(tài)代理,無論是JDK自帶的還是CGLIB擴(kuò)展的,無非就是在尋找方法取代手工編寫可替代類的方法,并將整個(gè)過程“接口化”。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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