动态代理
背景介绍
动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装 RPC 调用、面向切面的编程(AOP)。类似日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等都基于动态代理实现。
JDK 动态代理
利用反射机制,代理类实现 InvocationHandler,被代理类需要实现接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 计算核心目标方法执行所花费时间【只需要传入目标类和指定方法名称】
* 注意 jdk 动态代理目标类必须实现接口
*
* @description: jdk 动态代理
* @author: tiger
* @create: 2023-04-10 00:23
*/
public class DynamicProxyJDK implements InvocationHandler {
// 目标对象(被代理的类) -- 真实对象
private Object target;
long start;
long end;
/**
* 返回指定接口代理类实例,便于客户端获取
*
* @return
*/
public Object getProxy() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 参数一:目标对象
* 参数二:目标方法
* 参数三:目标方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 前置逻辑
before(method.getName());
// 核心逻辑
Object result = method.invoke(target, args);
// 后置逻辑
after();
return result;
}
/*------ 中介下需要做的事情 start------*/
/**
* 前置处理
*
* @param method
*/
private void before(String method) {
System.out.println("正在执行:" + method);
start = System.currentTimeMillis(); //1s=1000ms
}
/**
* 后置处理
*/
private void after() {
end = System.currentTimeMillis();
System.out.println("共花费了:" + (end - start) + "毫秒");
}
/*------ 中介下需要做的事情 end------*/
public void setTarget(Object target) {
this.target = target;
}
public DynamicProxyJDK() {
}
public DynamicProxyJDK(Object target) {
this.target = target;
}
/**
* 测试
*
* @param args
*/
public static void main(String[] args) {
// 代理类
DynamicProxyJDK proxy = new DynamicProxyJDK(new Dog());
// 获取得到代理类接口
Animal animal = (Animal) proxy.getProxy();
animal.eat();
}
}
interface Animal {
void eat();
}
class Dog implements Animal {
public void eat() {
System.out.println("核心事件:小狗小口吃肉...");
}
}
cglib 动态代理
操作字节码机制,基于 ASM 实现(效率相对较高),代理类实现 MethodInterceptor
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 计算核心目标方法执行所花费时间【只需要传入目标类和指定方法名称】
*
* @description: cglib 动态代理
* @author: tiger
* @create: 2023-04-10 00:23
*/
public class DynamicProxyCglib implements MethodInterceptor {
long start;
long end;
/**
* 参数一:目标对象
* 参数二:目标方法
* 参数三:目标方法的参数
* 参数四:代理方法的对象
*/
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//前置处理
before(method.getName());
//核心逻辑方法
Object result = proxy.invokeSuper(target, args);
//后置处理
after();
return result;
}
/*------ 中介下需要做的事情 start------*/
/**
* 前置处理
*
* @param method
*/
private void before(String method) {
System.out.println("正在执行:" + method + "方法");
start = System.currentTimeMillis(); //1s=1000ms
}
/**
* 后置处理
*/
private void after() {
end = System.currentTimeMillis();
System.out.println("共花费了:" + (end - start) + "毫秒");
}
/*------ 中介下需要做的事情 end------*/
/**
* 2、获取指定方法,并执行此方法 invoke
*
* @param clzz
* @throws Exception
* @throws NoSuchMethodException
*/
private static Method callMethod(Class clzz, String mName, Class... types) {
//传入方法名和类型参数[类型.class](有多少传多少)
Method m = null;
try {
m = clzz.getDeclaredMethod(mName, types);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
//公开权限,以可以操作执行所有方法
m.setAccessible(true);
return m;
}
/**
* 暴露给外界调用的方法
*
* @param instance
* @param methodName
*/
public static void doHandle(Object instance, String methodName) {
Enhancer enhancer = new Enhancer();
// 注入目标类
enhancer.setSuperclass(instance.getClass());
// 拦截器对象
DynamicProxyCglib proxy = new DynamicProxyCglib();
// 设置回调方法
enhancer.setCallback(proxy);
// 获得实例对象
instance = enhancer.create();
Method m2 = callMethod(instance.getClass(), methodName);
try {
m2.invoke(instance);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
/**
* 测试
*
* @param args
*/
public static void main(String[] args) {
doHandle(new Tiger(), "eat");
}
}
class Tiger {
public void eat() {
System.out.println("核心事件:老虎大口吃肉...");
}
}