Java 方法代理實例操做,靜態代理、JDK動態代理、CGLIB動態代理

方法的代理能夠在調用方法時進行其它的相關操做,並減小代碼的入侵和偶合。不少框架都用到了動態代理,並提供了減化代理操做,如:Spring 的 AOP。java

靜態代理

以電腦爲模型,咱們都知道,電腦是由 CPU、GPU、DISK 多個設備組裝的,它們都是經過接口相鏈接。如今模擬一臺電腦(Computer)經過接口設備(Device),代理(Proxy)組裝不一樣的設備(CPU、GPU),並運行(run)。編程

接口設備Device

/*
 * File:Device.java
 * User:iChochy
 * URL:https://ichochy.com
 * Copyright (c) 2020
 * Date:2020/08/31 17:49:31
 */

package com.ichochy.proxy;

public interface Device {

    public void run();
}

設備 CPU

/*
 * File:CPU.java
 * User:iChochy
 * URL:https://ichochy.com
 * Copyright (c) 2020
 * Date:2020/08/31 17:52:31
 */

package com.ichochy.proxy;

public class CPU implements Device {


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

}

設備 GPU

/*
 * File:GPU.java
 * User:iChochy
 * URL:https://ichochy.com
 * Copyright (c) 2020
 * Date:2020/08/31 17:52:31
 */

package com.ichochy.proxy;

public class GPU implements Device {

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

}

電腦Computer

Computerstart方法代理執行接口類方法框架

/*
 * File:SimpleProxy.java
 * User:iChochy
 * URL:https://ichochy.com
 * Copyright (c) 2020
 * Date:2020/08/31 17:54:31
 */

package com.ichochy.proxy;

public class Computer {

    private Device device;

    public Device start(){
        System.out.println("Start Computer");
        device.run();
        return device;
    }

    public Device getDevice() {
        return device;
    }

    public void setDevice(Device device) {
        this.device = device;
    }
}

運行電腦

經過類Computerstart方法代理執行接口類方法ide

package com.ichochy;

import com.ichochy.proxy.CPU;
import com.ichochy.proxy.Computer;
import com.ichochy.proxy.GPU;

public class App {
    public static void main(String[] args) {
        Computer proxy = new Computer();
        proxy.setDevice(new CPU());
        proxy.start();
        proxy.setDevice(new GPU());
        proxy.start();
    }
}

運行狀況

Start Computer
Game
Start Computer
Display

小結

靜態代理能夠代理某個方法,實現AOP操做,代理需求變動只需修改代理類,實現瞭解偶的效果。但不一樣的接口多個方法就要重複的編寫代理類,來實現方法代理操做。post

JDK動態代理

實現接口InvocationHandlerinvoke方法,經過ProxynewProxyInstance方法,構建代理接口實例。相比靜態代理更加靈活,動態代理不一樣的接口和接口中的方法。this

改進電腦Computer

實現接口InvocationHandlerinvoke方法,運用反射,動態執行代理方法代理

/*
 * File:SimpleProxy.java
 * User:iChochy
 * URL:https://ichochy.com
 * Copyright (c) 2020
 * Date:2020/08/31 17:54:31
 */

package com.ichochy.proxy;

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

public class Computer implements InvocationHandler {

    private Device device;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Start...");
        //運用反射,動態執行代理方法,並返回方法執行結果
        Object result = method.invoke(device, args);
        System.out.println("End");
        return result;
    }

    public Device getDevice() {
        return device;
    }

    public void setDevice(Device device) {
        this.device = device;
    }


}

動態運行電腦

經過類ProxynewProxyInstance方法構建代理接口類,實現方法的代理執行日誌

/*
 * File:App.java
 * User:iChochy
 * URL:https://ichochy.com
 * Copyright (c) 2020
 * Date:2020/09/01 12:46:01
 */

package com.ichochy;

import com.ichochy.proxy.CPU;
import com.ichochy.proxy.Computer;
import com.ichochy.proxy.Device;

import java.lang.reflect.Proxy;

public class App {

    public static void main(String[] args) throws Exception {
        CPU cpu = new CPU();
        Computer computer = new Computer();
        computer.setDevice(cpu);
        //獲取代理接口實例
        Device device = (Device) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), cpu.getClass().getInterfaces(), computer);
        device.run();
    }
}

運行狀況

Start... 
Game
End

小結

運用反射,動態代理能夠代理不一樣的接口的多個方法,沒必要修改代碼。但只能用於接口方法的代理,沒法實現全部類方法。code

CGLIB動態代理

CGLIB庫是用於生成和轉換Java字節碼的高級API,它容許運行時對字節碼進行修改和動態生成,經過繼承方式實現動態代理。xml

引入CGLIB

經過Maven庫管理引入第三方CGLIB

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

改進電腦Computer

實現接口MethodInterceptorintercept方法,運用反射,動態執行代理方法(原父類方法)

/*
 * File:SimpleProxy.java
 * User:iChochy
 * URL:https://ichochy.com
 * Copyright (c) 2020
 * Date:2020/08/31 17:54:31
 */

package com.ichochy.proxy;

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

import java.lang.reflect.Method;

public class Computer implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Start...");
        //運用反射,動態執行代理方法,並返回方法執行結果
        Object result = methodProxy.invokeSuper(obj, args);
        System.out.println("End");
        return result;
    }
}

動態運行電腦

經過類Enhancercreate方法構建代理類,實現方法的代理執行

/*
 * File:App.java
 * User:iChochy
 * URL:https://ichochy.com
 * Copyright (c) 2020
 * Date:2020/09/01 12:46:01
 */

package com.ichochy;

import com.ichochy.proxy.CPU;
import com.ichochy.proxy.Computer;
import net.sf.cglib.proxy.Enhancer;

public class App {

    public static void main(String[] args) throws Exception {
        Computer computer = new Computer();
        Enhancer enhancer = new Enhancer();
        //設置要代理超類
        enhancer.setSuperclass(CPU.class);
        //設置回調處理類
        enhancer.setCallback(computer);
        //構建代理類
        CPU cpu = (CPU)enhancer.create();
        cpu.run();
    }
}

運行狀況

Start... 
Game
End

小結

經過CGLIB庫能夠很方便的實現方法的動態代理,實現AOP操做。CGLIB庫構建代理類的子類,並重寫代理父類的方法,經過執行子類方法實現動態代理操做。

總結

當咱們要對一類方法或全部方法進行相同操做時,運用方法代理能夠很好實現咱們的需求,並不用去重寫之前的業務方法,如:事務處理、日誌監控、權限管理、異常捕捉及處理。

總結:方法代理,實現AOP操做。

相關文章

源文https://ichochy.com/posts/20200824/

相關文章
相關標籤/搜索