淺談代理模式實(shí)現(xiàn)原理

什么是代理模式

代理模式:簡(jiǎn)單理解就是主要對(duì)我們方法執(zhí)行之前與之后實(shí)現(xiàn)增強(qiáng)

代理模式應(yīng)用場(chǎng)景

  1. 日志的采集
  2. 權(quán)限控制
  3. 實(shí)現(xiàn)aop
  4. Mybatis mapper
  5. Spring的事務(wù)
  6. 全局捕獲異常
  7. Rpc遠(yuǎn)程調(diào)用接口
  8. 代理數(shù)據(jù)源

代理模式實(shí)現(xiàn)的原理
代理模式主要包含三個(gè)角色,即抽象主題角色(Subject)、委托類角色(被代理角色,Proxied)以及代理類角色(Proxy),如上圖所示:

抽象主題角色:可以是接口,也可以是抽象類;
委托類角色: 真實(shí)主題角色,業(yè)務(wù)邏輯的具體執(zhí)行者;
代理類角色:內(nèi)部含有對(duì)真實(shí)對(duì)象RealSubject的引用,負(fù)責(zé)對(duì)真實(shí)主題角色的調(diào)用,并在真實(shí)主題角色處理前后做預(yù)處理和后處理。

代理模式創(chuàng)建方式

靜態(tài)代理

靜態(tài)代理需要自己人工編寫(xiě)代理類代碼

基于接口實(shí)現(xiàn)方式如下:

public class OrderServiceProxy  implements  OrderService{
    private OrderService orderService;

    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }

    public String addOrder(String userName, String userPwd) {
        System.out.println("使用靜態(tài)代理類打印日志開(kāi)始:userName:" + userName + "," + userPwd);
        String result = orderService.addOrder(userName, userPwd);
        System.out.println("使用靜態(tài)代理類打印日志結(jié)束:userName:" + userName + "," + userPwd);
        return result;
    }
}


public interface OrderService {
    /**
     * 需要被代理的方法
     * @return
     */
     String addOrder(String userName,String userPwd);
}


public class Test001 {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
        orderService.addOrder("靜態(tài)代理測(cè)試","123456");
    }
} 

基于繼承的實(shí)現(xiàn)方式

public class OrderServiceProxy  extends OrderServiceImpl {
    private OrderService orderService;

    public OrderServiceProxy(OrderService orderService) {
        this.orderService = orderService;
    }

    public String addOrder(String userName, String userPwd) {
        System.out.println("使用靜態(tài)代理類打印日志開(kāi)始:userName:" + userName + "," + userPwd);
        String result = super.addOrder(userName, userPwd);
        System.out.println("使用靜態(tài)代理類打印日志結(jié)束:userName:" + userName + "," + userPwd);
        return result;
    }
} 

動(dòng)態(tài)代理與靜態(tài)代理的區(qū)別
動(dòng)態(tài)代理不需要寫(xiě)代理類對(duì)象,通過(guò)程序自動(dòng)生成,而靜態(tài)代理需要我們自己寫(xiě)代理類對(duì)象。

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

  1. 動(dòng)態(tài)代理是在實(shí)現(xiàn)階段不用關(guān)心代理類,而在運(yùn)行階段才指定哪一個(gè)對(duì)象。
  2. 動(dòng)態(tài)代理類的源碼是在程序運(yùn)行期間由JVM根據(jù)反射等機(jī)制動(dòng)態(tài)的生成 。

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

JDK動(dòng)態(tài)代理的一般步驟如下:

  1. 創(chuàng)建被代理的接口和類;
  2. 實(shí)現(xiàn)InvocationHandler接口,對(duì)目標(biāo)接口中聲明的所有方法進(jìn)行統(tǒng)一處理;
  3. 調(diào)用Proxy的靜態(tài)方法,創(chuàng)建代理類并生成相應(yīng)的代理對(duì)象;

實(shí)現(xiàn)原理:利用攔截器機(jī)制必須實(shí)現(xiàn)InvocationHandler接口中的invoke方法實(shí)現(xiàn)對(duì)我們的目標(biāo)方法增強(qiáng)。

public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目標(biāo)對(duì)象
     */
    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    /**
     * @param proxy  使用jdk程序生成的代理類
     * @param method 目標(biāo)方法
     * @param args   方法需要傳遞的參數(shù)
     * @return
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk動(dòng)態(tài)代理打印日志開(kāi)始" + args[0]);
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk動(dòng)態(tài)代理打印日志結(jié)束" + args[1]);
        return result;
    }

    /**
     * 生成代理類
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
 
     /**
     * 測(cè)試代理類
     *
     * @param <T>
     * @return
     */ 
    public static void main(String[] args) {
        JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
        OrderServiceImpl orderService = jdkInvocationHandler.getProxy();
        orderService.addOrder("動(dòng)態(tài)代理", "測(cè)試動(dòng)態(tài)代理");
    } 
}
public interface OrderService {
     void addOrder(String arg0, String arg1);
} 

public class OrderServiceImpl implements OrderService {

    public void addOrder(String arg0, String arg1) {
        System.out.println(arg0+"-----"+arg1);
    }
}

運(yùn)行結(jié)果

    Connected to the target VM, address: '127.0.0.1:54763', transport: 'socket'
    使用Jdk動(dòng)態(tài)代理打印日志開(kāi)始動(dòng)態(tài)代理測(cè)試
    動(dòng)態(tài)代理測(cè)試-----測(cè)試動(dòng)態(tài)代理
    使用Jdk動(dòng)態(tài)代理打印日志結(jié)束測(cè)試動(dòng)態(tài)代理
    Disconnected from the target VM, address: '127.0.0.1:54763', transport: 'socket' 

注意:繼承了Proxy類,實(shí)現(xiàn)了代理的接口,由于java不能多繼承,這里已經(jīng)繼承了Proxy類了,不能再繼承其他的類,所以JDK的動(dòng)態(tài)代理不支持對(duì)實(shí)現(xiàn)類的代理,只支持接口的代理。

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

利用asm字節(jié)碼技術(shù),生成子類實(shí)現(xiàn)對(duì)目標(biāo)方法實(shí)現(xiàn)增強(qiáng)

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

Maven依賴

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
</dependencies> 

實(shí)現(xiàn)一個(gè)業(yè)務(wù)類,注意,這個(gè)業(yè)務(wù)類并沒(méi)有實(shí)現(xiàn)任何接口:

package com.jpeony.spring.proxy.cglib;
 
public class HelloService {
 
    public HelloService() {
        System.out.println("HelloService構(gòu)造");
    }
 
    /**
     * 該方法不能被子類覆蓋,Cglib是無(wú)法代理final修飾的方法的
     */
    final public String sayOthers(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }
 
    public void sayHello() {
        System.out.println("HelloService:sayHello");
    }
}

自定義MethodInterceptor

package com.jpeony.spring.proxy.cglib;
 
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
 
import java.lang.reflect.Method;
 
/**
 * 自定義MethodInterceptor
 */
public class MyMethodInterceptor implements MethodInterceptor{
 
    /**
     * sub:cglib生成的代理對(duì)象
     * method:被代理對(duì)象方法
     * objects:方法入?yún)?     * methodProxy: 代理方法
     */
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======代理開(kāi)始======");
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("======代理結(jié)束======");
        return object;
    }
} 

調(diào)用代理類測(cè)試代碼


package com.jpeony.spring.proxy.cglib;
 
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
 
public class Client {
    public static void main(String[] args) {
        // 代理類class文件存入本地磁盤(pán)方便我們反編譯查看源碼
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\myWork");
        // 通過(guò)CGLIB動(dòng)態(tài)代理獲取代理對(duì)象的過(guò)程
        Enhancer enhancer = new Enhancer();
        // 設(shè)置enhancer對(duì)象的父類
        enhancer.setSuperclass(HelloService.class);
        // 設(shè)置enhancer的回調(diào)對(duì)象
        enhancer.setCallback(new MyMethodInterceptor());
        // 創(chuàng)建代理對(duì)象
        HelloService proxy= (HelloService)enhancer.create();
        // 通過(guò)代理對(duì)象調(diào)用目標(biāo)方法
        proxy.sayHello();
    }
 

Jdk與Cglib動(dòng)態(tài)代理的區(qū)別

  1. Jdk動(dòng)態(tài)代理利用反射技術(shù)生成匿名的代理類走InvokeHandler回調(diào)方法實(shí)現(xiàn)增強(qiáng),同時(shí)也是一種基于接口的方式實(shí)現(xiàn)代理。
  2. Cglib動(dòng)態(tài)代理利用asm字節(jié)碼技術(shù)生成一個(gè)子類覆蓋其中的方法實(shí)現(xiàn)增強(qiáng),同時(shí)采用fastClass機(jī)制對(duì)整個(gè)代理類建立索引比反射效率要高
  3. 在Spring中如果需要被代理的對(duì)象如果實(shí)現(xiàn)了接口采用Jdk動(dòng)態(tài)代理,沒(méi)有實(shí)現(xiàn)接口則使用Cglib動(dòng)態(tài)代理。
?著作權(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)容

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