1. 程式人生 > >代理模式【結構模式】

代理模式【結構模式】

代理模式

Provide a surrogate or placeholder for another object to control access to it.
為其他物件提供一個代理或佔位符來控制對它的訪問。

靜態代理

public class StaticProxy {
    @Test
    public void all() {
        final Runner runner = new Runner();
        final RunProxy runProxy = RunProxy.builder().runner(runner).build();
        // 通過代理角色實現對真實角色的訪問控制
        runProxy.run();
    }
}

/**
 * 1)需要執行代理的行為
 */
interface IRun {
    void run();
}

/**
 * 2)實現行為的真實角色
 */
@Slf4j
class Runner implements IRun {
    @Override
    public void run() {
        log.info("真實角色的行為");
    }
}

/**
 * 3)持有真實角色的代理角色,可控制外部對代理的訪問
 */
@Builder
@Slf4j
class RunProxy implements IRun {
    private final IRun runner;

    @Override
    public void run() {
        if (ThreadLocalRandom.current().nextBoolean()) {
            // 執行真實角色的行為
            runner.run();
        } else {
            log.error("代理心情不好,拒絕處理");
        }
    }
}

動態代理

public class DynamicProxy {
    /**
     * 代理模式:
     * Provide a surrogate or placeholder for another object to control access to it.
     * 為其他物件提供一個代理或佔位符來控制對它的訪問。
     */
    @Test
    public void all() {
        final TargetImpl targetImpl = new TargetImpl();
        final LogAdvice logAdvice = new LogAdvice();
        final SelfProxy selfProxy = SelfProxy.builder()
                .target(targetImpl)
                .advice(logAdvice).build();
        final Itarget target = (Itarget) selfProxy.proxy();
        target.show();
    }
}

/**
 * 1)需要執行動態代理的目標介面
 */
interface Itarget {
    void show();
}

/**
 * 2)目標介面的實現類
 */
@Slf4j
class TargetImpl implements Itarget {

    @Override
    public void show() {
        log.info("呼叫目標方法");
        throw new RuntimeException("測試異常");
    }
}

/**
 * 3)動態織入的通知介面
 */
interface Iadvice {
    // 前置通知
    void before();

    // 後置通知
    void after();

    // 環繞通知
    Object around(Object target, Method method, Object[] args);

    // 返回通知
    void afterReturning();

    // 異常通知
    void afterThrowing();
}

/**
 * 通知介面的抽象實現,以簡化實現通知介面所需要的工作。
 */
@Slf4j
abstract class BaseAdvice implements Iadvice {

    @Override
    public void before() {
    }

    @Override
    public void after() {
    }

    @Override
    public Object around(Object target, Method method, Object[] args) {
        try {
            log.info("執行環繞方法", "around");
            return method.invoke(target, args);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    public void afterReturning() {
    }

    @Override
    public void afterThrowing() {
    }
}

/**
 * 4)具體的通知實現類
 */
@Slf4j
class LogAdvice extends BaseAdvice {
    private final ThreadLocal<Long> startTime = new ThreadLocal();

    @Override
    public void before() {
        log.info("開始執行方法 {}", "before");
        startTime.set(System.nanoTime());
    }

    @Override
    public void after() {
        final Long start = startTime.get();
        final long executeTime = System.nanoTime() - start;
        log.info("方法執行完畢 {}", "after");
        log.info("方法執行時間 {}", executeTime);
    }

    @Override
    public Object around(Object target, Method method, Object[] args) {
        try {
            log.info("執行環繞方法 {}", "around");
            return method.invoke(target, args);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    public void afterReturning() {
        log.info("方法返回 {}", "afterReturning");
    }

    @Override
    public void afterThrowing() {
        log.info("方法丟擲異常 {}", "afterThrowing");
    }

}

/**
 * 5)動態代理類
 */
@Builder
@AllArgsConstructor
class SelfProxy implements InvocationHandler {
    // 要代理的目標物件
    private final Object target;
    // 動態織入的通知介面
    private final Iadvice advice;

    @Override
    public Object invoke(Object target, Method method, Object[] args) throws Throwable {
        // 執行前置通知
        advice.before();
        try {
            // 執行環繞通知,此處方法的主體一定要是被代理物件。
            final Object around = advice.around(this.target, method, args);
            // 執行後置通知
            advice.after();
            return around;
        } catch (final Exception e) {
            // 執行異常通知
            advice.afterThrowing();
            throw new IllegalStateException(e);
        } finally {
            // 執行返回通知
            advice.afterReturning();
        }
    }

    public Object proxy() {
        // 獲取指定介面的動態代理類,基於 JDK 的 Proxy 實現。
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}