方法的代理可以在调用方法时进行其它的相关操作,并减少代码的入侵和偶合。很多框架都用到了动态代理,并提供了减化代理操作,如:Spring 的 AOP。
静态代理
以电脑为模型,我们都知道,电脑是由 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
Computer
的start
方法代理执行接口类方法
/*
* 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;
}
}
运行电脑
通过类Computer
的start
方法代理执行接口类方法
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
操作,代理需求变更只需修改代理类,实现了解偶的效果。但不同的接口多个方法就要重复的编写代理类,来实现方法代理操作。
JDK动态代理
实现接口InvocationHandler
的invoke
方法,通过Proxy
的newProxyInstance
方法,构建代理接口实例。相比静态代理更加灵活,动态代理不同的接口和接口中的方法。
改进电脑Computer
实现接口InvocationHandler
的invoke
方法,运用反射,动态执行代理方法
/*
* 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;
}
}
动态运行电脑
通过类Proxy
的newProxyInstance
方法构建代理接口类,实现方法的代理执行
/*
* 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
小结
运用反射,动态代理可以代理不同的接口的多个方法,不必修改代码。但只能用于接口方法的代理,无法实现所有类方法。
CGLIB动态代理
CGLIB库是用于生成和转换Java字节码的高级API,它允许运行时对字节码进行修改和动态生成,通过继承方式实现动态代理。
引入CGLIB
库
通过Maven
库管理引入第三方CGLIB
库
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
改进电脑Computer
实现接口MethodInterceptor
的intercept
方法,运用反射,动态执行代理方法(原父类方法)
/*
* 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;
}
}
动态运行电脑
通过类Enhancer
的create
方法构建代理类,实现方法的代理执行
/*
* 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
操作。