JAVA動態(tài)代理的實現(xiàn)方式

1. 靜態(tài)代理VS動態(tài)代理

代理類可以增強被代理對象的方法??煞譃殪o態(tài)代理和動態(tài)代理。

1.1 靜態(tài)代理

靜態(tài)代理:源代碼中需要聲明代理類

public class TestStaticProxy {

    public static void main(String[] args) {
        IRegisterService iRegisterService = new RegisterServiceImpl();
        IRegisterService proxy = new RegisterServiceProxy(iRegisterService);
        proxy.register("RyanLee", "123");
    }
}

interface IRegisterService {
    void register(String name, String pwd);
}

class RegisterServiceImpl implements IRegisterService {
    @Override
    public void register(String name, String pwd) {
        System.out.println(String.format("【向數(shù)據(jù)庫中插入數(shù)據(jù)】name:%s,pwd:%s", name, pwd));
    }
}

class RegisterServiceProxy implements IRegisterService {
    IRegisterService iRegisterService;

    public RegisterServiceProxy(IRegisterService iRegisterService) {
        this.iRegisterService = iRegisterService;
    }

    @Override
    public void register(String name, String pwd) {
        System.out.println("[Proxy]一些前置處理");
        System.out.println(String.format("[Proxy]打印注冊信息:姓名:%s,密碼:%s", name, pwd));
        iRegisterService.register(name, pwd);
        System.out.println("[Proxy]一些后置處理");

    }
}

執(zhí)行結(jié)果

[Proxy]一些前置處理
[Proxy]打印注冊信息:姓名:RyanLee,密碼:123
【向數(shù)據(jù)庫中插入數(shù)據(jù)】name:RyanLee,pwd:123
[Proxy]一些后置處理

1.2 動態(tài)代理

動態(tài)代理:無需聲明代理類。是使用反射和字節(jié)碼的技術(shù),在運行期創(chuàng)建指定接口或類的子類(即動態(tài)代理類)以及其實例對象的技術(shù)。通過動態(tài)代理技術(shù)可以無侵入地對代碼進行增強。

java源文件編譯生成字節(jié)碼

動態(tài)代理生成字節(jié)碼

動態(tài)代理實現(xiàn)的方式主要有兩種:
1.JDK原生動態(tài)代理
2.CGLib動態(tài)代理(CG:Code Generation)

2.動態(tài)代理的實現(xiàn)方式

2.1 JDK原生動態(tài)代理

動態(tài)代理類和被代理類必須繼承同一個接口。動態(tài)代理只能對接口中聲明的方法進行代理。

2.1.1 Proxy

java.lang.reflect.Proxy是所有動態(tài)代理的父類。它通過靜態(tài)方法newProxyInstance()來創(chuàng)建動態(tài)代理的class對象和實例。

2.1.2 InvocationHandler

每一個動態(tài)代理實例都有一個關(guān)聯(lián)的InvocationHandler。通過代理實例調(diào)用方法,方法調(diào)用請求會被轉(zhuǎn)發(fā)給InvocationHandler的invoke方法。

2.1.3 栗子

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

public class TestJdkDynamicProxy {

    public static void main(String[] args) {
        IRegisterService iRegisterService = new RegisterServiceImpl();
        InsertDataHandler insertDataHandler = new InsertDataHandler();
        IRegisterService proxy = (IRegisterService)insertDataHandler.getProxy(iRegisterService);
        proxy.register("RyanLee", "123");
    }
}


class InsertDataHandler implements InvocationHandler {
    Object obj;

    public Object getProxy(Object obj){
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        Object result = method.invoke(obj, args);
        doAfter();
        return result;
    }
    private void doBefore() {
        System.out.println("[Proxy]一些前置處理");
    }
    private void doAfter() {
        System.out.println("[Proxy]一些后置處理");
    }

}

執(zhí)行結(jié)果

[Proxy]一些前置處理
【向數(shù)據(jù)庫中插入數(shù)據(jù)】name:RyanLee,pwd:123
[Proxy]一些后置處理
  • proxy類型:


2.2 CGLib動態(tài)代理

CGLib(Code Generation Library)是一個基于ASM的字節(jié)碼生成庫。它允許我們在運行時對字節(jié)碼進行修改或動態(tài)生成。CGLib通過繼承被代理類的方式實現(xiàn)代理。

2.2.1 Enhancer

Enhancer指定要代理的目標對象。通過create方法得到代理對象。通過代理實例調(diào)用非final方法,方法調(diào)用請求會首先轉(zhuǎn)發(fā)給MethodInterceptor的intercept

2.2.2 MethodInterceptor

通過代理實例調(diào)用方法,調(diào)用請求都會轉(zhuǎn)發(fā)給intercept方法進行增強。

2.2.3 栗子

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class TestCGLibDynamicProxy {

    public static void main(String[] args) {
        IRegisterService iRegisterService = new RegisterServiceImpl();
        InsertDataInterceptor interceptor = new InsertDataInterceptor();
        RegisterServiceImpl proxy = (RegisterServiceImpl) interceptor.getProxy(iRegisterService);
        proxy.register("RyanLee", "123");
    }
}

class InsertDataInterceptor implements MethodInterceptor {
    Object target;

    public Object getProxy(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回調(diào)方法
        enhancer.setCallback(this);
        // 創(chuàng)建代理對象
        return enhancer.create();
    }

    private void doBefore() {
        System.out.println("[Proxy]一些前置處理");
    }

    private void doAfter() {
        System.out.println("[Proxy]一些后置處理");
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        doBefore();
        Object result = methodProxy.invoke(target, objects);
        doAfter();
        return result;
    }
}

執(zhí)行結(jié)果

[Proxy]一些前置處理
【向數(shù)據(jù)庫中插入數(shù)據(jù)】name:RyanLee,pwd:123
[Proxy]一些后置處理
  • proxy類型:


2.3 JDK動態(tài)代理 VS CGLib動態(tài)代理。

兩種動態(tài)代理的最大的區(qū)別是:
JDK動態(tài)代理要求被代理對象必須基于接口來實現(xiàn)。動態(tài)代理類和被代理類必須繼承同一個接口。動態(tài)代理只能對接口中聲明的方法進行代理。對那些沒有實現(xiàn)接口的bean。JDK動態(tài)代理無法代理。而CGLib通過繼承被代理類的方式實現(xiàn)代理。

  • Spring 注解默認使用JDK動態(tài)代理來實現(xiàn)。也可以通過修改配值,改為使用CGLib動態(tài)代理來實現(xiàn)。因而建議Spring注解不要寫在Interface上。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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