理解代理模式


title: “理解代理模式”

url: “https://wsk1103.github.io/

tags:

  • 設計模式

是什麼

代理模式屬於結構型模式。給某一個對象提供一個代理,並且使用該代理的對象來訪問原來的對象。

代理模式一般分爲靜態代理和動態代理,動態代理又分爲JDK代理和CGLIB代理。

優缺點

優點

在不修改原來類的情況下,增強類的功能。

缺點

系統更復雜。

靜態代理

spring 的事務 @translation 爲例。

1. 先創建一個用戶的增刪改查的接口和其實現類。

/**
 * @author wsk1103
 * @date 2019/7/22
 * @description 用戶類
 */
public interface UserService {

    /**
     * 更新
     */
    void update();

}

/**
 * @author wsk1103
 * @date 2019/7/22
 * @description 描述
 */
public class UserServiceImpl implements UserService{

    @Override
    public void update() {
        System.out.println("update......");
    }
}

一般來說,這就是一個數據庫的更新操作了。

2. 靜態代理實現事務

但是如果我們想要爲其加入 事務控制 ,又不想修改原來的類,那就可以試試靜態代理。

/**
 * @author wsk1103
 * @date 2019/7/22
 * @description 靜態代理
 */
public class TranslationProxy implements UserService {

    private UserService userService;

    public TranslationProxy(UserService service) {
        this.userService = service;
    }

    @Override
    public void update() {
        start();
        userService.update();
        commit();
    }

    private void start() {
        System.out.println("start translation");
    }
    
    private void commit() {
        System.out.println("commit translation");
    }
}

3. 測試和輸出結果

/**
 * @author wsk1103
 * @date 2019/7/22
 * @description 描述
 */
public class Main {

    public static void main(String[] args) {
        System.out.println("不使用靜態代理");
        UserService userService = new UserServiceImpl();
        userService.update();
        System.out.println("-------");

        System.out.println("使用靜態代理");
        UserService target = new UserServiceImpl();
        UserService proxy = new TranslationProxy(target);
        proxy.update();
        System.out.println("--------");

    }

}

不使用靜態代理
update......
-------
使用靜態代理
start translation
update......
commit translation
--------

優點

既達到了功能的增強,並且沒有破壞入侵源代碼。

缺點

代理類和委託類實現相同的接口,同時要實現相同的方法。這樣就出現了大量的代碼重複。如果接口增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的複雜度。

JDK動態代理

還是以 事務 爲例子。

JDK動態代理主要涉及:java.lang.reflect.Proxy

1. 實現動態代理

import java.lang.reflect.Proxy;
/**
 * @author wsk1103
 * @date 2019/7/22
 * @description 動態代理
 */
public class TranslationProxyFactory {

    //被代理的對象
    private Object target;

    public TranslationProxy(Object o) {
        this.target = o;
    }

    private void start() {
        System.out.println("start translation");
    }

    private void commit() {
        System.out.println("commit translation");
    }

    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy1, method1, args1) -> {
                    start();
                    //執行目標對象方法
                    Object returnValue = method1.invoke(target, args1);
                    commit();
                    return returnValue;
                }
        );
    }
}

2. 測試和結果

public class Main {

    public static void main(String[] args) {
        System.out.println("不使用靜態代理");
        UserService userService = new UserServiceImpl();
        userService.update();
        System.out.println("-------");

        System.out.println("使用動態代理");
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) new TranslationProxyFactory(target).getProxyInstance();
        proxy.update();
        System.out.println("--------");

    }
}

不使用靜態代理
update......
-------
使用動態代理
start translation
update......
commit translation
--------

優點

解耦

缺點

基於Java反射機制實現,代理對象不需要實現接口,但是目標對象一定要實現接口,否則不能用動態代理

CGLIB動態代理

需要引入新的包 CGLIB 。如果是使用spring的話就不需要,因爲spring中默認會引入了。

  • Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)
  • Cglib包的底層是通過使用字節碼處理框架ASM來轉換字節碼並生成新的子類.

1. maven 引入 cglib

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.12</version>
</dependency>

2. CGLIB動態代理工廠


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

import java.lang.reflect.Method;

/**
 * @author wsk1103
 * @date 2019/7/22
 * @description 動態代理
 */
public class TranslationProxy implements MethodInterceptor {

    //被代理的對象
    private Object target;

    public TranslationProxy(Object o) {
        this.target = o;
    }

    private void start() {
        System.out.println("start translation");
    }

    private void commit() {
        System.out.println("commit translation");
    }

    public Object getProxyInstance() {
        //1.工具類
        Enhancer en = new Enhancer();
        //2.設置父類
        en.setSuperclass(target.getClass());
        //3.設置回調函數
        en.setCallback(this);
        //4.創建子類(代理對象)
        return en.create();
    }

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

測試和輸出

package com.wsk.utils;

/**
 * @author wsk1103
 * @date 2019/7/22
 * @description 描述
 */
public class Main {

    public static void main(String[] args) {
        System.out.println("不使用代理");
        UserService userService = new UserServiceImpl();
        userService.update();
        System.out.println("-------");

        System.out.println("使用動態代理");
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) new TranslationProxy(target).getProxyInstance();
        proxy.update();
        System.out.println("--------");

    }
}

不使用代理
update......
-------
使用動態代理
start translation
update......
commit translation
--------

優點

解耦

缺點

代理的類不能爲final,否則報錯;目標對象的方法如果爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.

java中的應用

  • java.lang.reflect.Proxy
  • Apache Commons Proxy
  • Mocking frameworks Mockito, Powermock, EasyMock

參考:
https://github.com/iluwatar/java-design-patterns/tree/master/proxy
https://segmentfault.com/a/1190000009235245#articleHeader7

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章