cglib 教程 - 使用cglib實(shí)現(xiàn)動(dòng)態(tài)代理

cglib是一款優(yōu)秀的Java 字節(jié)碼生成框架,它可以生成并操縱Java字節(jié)碼(底層基于ASM)。

cglib - Byte Code Generation Library is high level API to generate and transform Java byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.

動(dòng)態(tài)代理實(shí)現(xiàn)

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

Java 實(shí)現(xiàn)動(dòng)態(tài)代理主要涉及以下幾個(gè)類:

  • java.lang.reflect.Proxy: 這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類,即 DynamicProxyClass extends Proxy。
  • java.lang.reflect.InvocationHandler: 這里稱他為"調(diào)用處理器",他是一個(gè)接口,我們動(dòng)態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個(gè)類,而這個(gè)類必須實(shí)現(xiàn) InvocationHandler 接口。
package com.bytebeats.codelab.cglib.proxy.impl;

import com.bytebeats.codelab.cglib.proxy.ProxyFactory;

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

/**
 * JDK動(dòng)態(tài)代理實(shí)現(xiàn)
 * @author Ricky
 *
 */
public class JdkProxyFactory implements ProxyFactory {

    @Override
    public <T> T getProxy(final Object target) {

        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                if (Object.class.equals(method.getDeclaringClass())) {
                    return method.invoke(this, args);
                }
                String methodName = method.getName();
                //打印日志
                System.out.println("[before] The method " + methodName + " begins with " + (args!=null ? Arrays.asList(args) : "[]"));

                //調(diào)用目標(biāo)方法
                Object result = null;
                try {
                    //前置通知
                    result = method.invoke(target, args);
                    //返回通知, 可以訪問到方法的返回值
                } catch (NullPointerException e) {
                    e.printStackTrace();
                    //異常通知, 可以訪問到方法出現(xiàn)的異常
                }
                //后置通知. 因?yàn)榉椒梢阅軙?huì)出異常, 所以訪問不到方法的返回值
                //打印日志
                System.out.println("[after] The method ends with " + result);
                return result;
            }
        });
    }
}

JDK 動(dòng)態(tài)生成的代理類具有幾個(gè)特點(diǎn):

  • 繼承 Proxy 類,并實(shí)現(xiàn)了在Proxy.newProxyInstance()中提供的接口數(shù)組。
  • 命名方式為 $ProxyN,其中N會(huì)慢慢增加,一開始是 $Proxy1,接下來是$Proxy2...
  • 有一個(gè)參數(shù)為 InvocationHandler 的構(gòu)造函數(shù)。這個(gè)從 Proxy.newProxyInstance() 函數(shù)內(nèi)部的clazz.getConstructor(new Class[] { InvocationHandler.class }) 可以看出。

Java 實(shí)現(xiàn)動(dòng)態(tài)代理的缺點(diǎn):因?yàn)?Java 的單繼承特性(每個(gè)代理類都繼承了 Proxy 類),只能針對(duì)接口創(chuàng)建代理類,不能針對(duì)類創(chuàng)建代理類。

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

net.sf.cglib.proxy.Enhancer 類提供了非常簡(jiǎn)潔的API來創(chuàng)建代理對(duì)象,有兩種回調(diào)的防方式:InvocationHandler和MethodInterceptor。

1、InvocationHandler

@Test
public void testInvocationHandler() throws Exception {
  Enhancer enhancer = new Enhancer();
  enhancer.setSuperclass(SampleClass.class);
  enhancer.setCallback(new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
      if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
        return "Hello cglib!";
      } else {
        throw new RuntimeException("Do not know what to do.");
      }
    }
  });
  SampleClass proxy = (SampleClass) enhancer.create();
  assertEquals("Hello cglib!", proxy.test(null));
  assertNotEquals("Hello cglib!", proxy.toString());
}

2、

@Test
public void testMethodInterceptor() throws Exception {
  Enhancer enhancer = new Enhancer();
  enhancer.setSuperclass(SampleClass.class);
  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(obj, args);
      }
    }
  });
  SampleClass proxy = (SampleClass) enhancer.create();
  assertEquals("Hello cglib!", proxy.test(null));
  assertNotEquals("Hello cglib!", proxy.toString());
  proxy.hashCode(); // Does not throw an exception or result in an endless loop.
}

對(duì)上面代碼稍作封裝,如下:

package com.bytebeats.codelab.cglib.proxy.impl;

import com.bytebeats.codelab.cglib.proxy.ProxyFactory;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * CgLib動(dòng)態(tài)代理實(shí)現(xiàn)
 * @author Ricky
 *
 */
public class CgLibProxyFactory implements ProxyFactory {
    private final Enhancer en = new Enhancer();

    @Override
    public <T> T getProxy(Object target) {

        //進(jìn)行代理
        en.setSuperclass(target.getClass());
        en.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

                if (Object.class.equals(method.getDeclaringClass())) {
                    return method.invoke(this, args);
                }
                String methodName = method.getName();
                //打印日志
                System.out.println("[before] The method " + methodName + " begins with " + (args!=null ? Arrays.asList(args) : "[]"));
                Object result = null;
                try{
                    //前置通知
                    result = methodProxy.invokeSuper(o, args);
                    //返回通知, 可以訪問到方法的返回值
                    System.out.println(String.format("after method:%s execute", method.getName()));
                } catch (Exception e){
                    e.printStackTrace();
                    //異常通知, 可以訪問到方法出現(xiàn)的異常
                }
                //后置通知. 因?yàn)榉椒梢阅軙?huì)出異常, 所以訪問不到方法的返回值
                //打印日志
                System.out.println("[after] The method ends with " + result);
                return result;
            }
        });
        //生成代理實(shí)例
        return (T)en.create();
    }
}

完整測(cè)試類:

package com.bytebeats.codelab.cglib.proxy;

import com.bytebeats.codelab.cglib.proxy.impl.CgLibProxyFactory;
import com.bytebeats.codelab.cglib.proxy.impl.JdkProxyFactory;
import com.bytebeats.codelab.cglib.service.HelloService;
import com.bytebeats.codelab.cglib.service.HelloServiceImpl;

public class ProxyDemo {

    public static void main(String[] args) {

        //需要被代理的類
        HelloService helloService = new HelloServiceImpl();

        //jdk代理
        ProxyFactory jdkProxyFactory = new JdkProxyFactory();
        HelloService jdkProxy = jdkProxyFactory.getProxy(helloService);
        jdkProxy.echo("ricky");
        jdkProxy.hashCode();

        //CgLib代理
        ProxyFactory cgLibProxyFactory = new CgLibProxyFactory();
        HelloService cgLibProxy = cgLibProxyFactory.getProxy(helloService);
        cgLibProxy.echo("ricky");
        jdkProxy.hashCode();
    }

}

參考資料
CGLIB Tutorial
Java 動(dòng)態(tài)代理機(jī)制分析及擴(kuò)展,第 1 部分

最后編輯于
?著作權(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)容