未曾设想过的JDK动态代理写法
前言
在阅读TomcatJdbc源码时,发现了一个很四倍秀的JDK动态代理的写法,我当即就把这个写法分享给了隔壁组可爱的女同事,她在惊叹不已的同时向我问道:这么写有啥用呢?我随即陷入了沉思,气氛一时间竟有点尴尬,感觉相较于传统的使用Proxy#newProxyInstance来创建动态代理对象,这个四倍秀的不怎么常见的写法,大有一种先脱裤子再放屁的感觉,今天就来看下是不是这么回事儿。
正文
在给出四倍秀JDK动态代理的写法前,我们先回顾一下穿着裤子放屁的通常的JDK动态代理的写法。
public class MainTest {
public static void main(String[] args) {
// 创建被代理类对象
RealObject realObject = new RealObject();
// 获取被代理类的类加载器
ClassLoader classLoader = realObject.getClass()
.getClassLoader();
// 获取被代理类实现的接口的Class对象
Class<?>[] interfaces = realObject.getClass()
.getInterfaces();
// 以被代理类对象作为入参创建InvocationHandler
InvocationHandler invocationHandler
= new MyInvocationHandler(realObject);
// 通过调用Proxy的newProxyInstance()方法创建动态代理对象
Object proxyInstance = Proxy.newProxyInstance(
classLoader, interfaces, invocationHandler);
((TestServiceA) proxyInstance).executeTestA();
((TestServiceA) proxyInstance).submitTestA();
((TestServiceB) proxyInstance).executeTestB();
((TestServiceB) proxyInstance).submitTestB();
}
}
相关接口,被代理类和InvocationHandler如下所示。
public interface MyServiceA {
void executeTestA();
void submitTestA();
}
public interface MyServiceB {
void executeTestB();
void submitTestB();
}
public class MyRealObject implements MyServiceA, MyServiceB {
@Override
public void executeTestA() {
System.out.println("Test A execute.");
}
@Override
public void submitTestA() {
System.out.println("Test A submit.");
}
@Override
public void executeTestB() {
System.out.println("Test B execute.");
}
@Override
public void submitTestB() {
System.out.println("Test B submit.");
}
}
public class MyInvocationHandler implements InvocationHandler {
private final Object realObject;
public MyInvocationHandler(Object realObject) {
this.realObject = realObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object invokeResult = method.invoke(realObject, args);
after();
return invokeResult;
}
private void before() {
System.out.println("Begin to do.");
}
private void after() {
System.out.println("Finish to do.");
}
}
上述是很标准的使用JDK动态代理的写法,那么下面再稍微一探Proxy#newProxyInstance方法的实现,看一下在创建动态代理对象的过程中,其为我们做了什么。
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 生成代理类的Class对象
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 获取代理类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 生成代理对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
其实主要步骤就是如下三步。
// 1. 生成代理类的Class对象
Class<?> cl = getProxyClass0(loader, intfs);
// 2. 获取代理类的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
// 3. 生成代理对象
cons.newInstance(new Object[]{h});
而我口中说的四倍秀写法,其实就是自己完成上述三个步骤,示例如下。
public class MainTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
// 获取代理类的Class对象且代理类实现了TestServiceA和TestServiceB接口
Class<?> proxyClass = Proxy.getProxyClass(MyServiceA.class.getClassLoader(),
MyServiceA.class, MyServiceB.class);
// 获取代理类的构造器
Constructor<?> proxyClassConstructor = proxyClass.getConstructor(InvocationHandler.class);
// 创建一个被代理类对象
MyRealObject myRealObject = new MyRealObject();
// 基于被代理类对象创建InvocationHandler
InvocationHandler invocationHandler = new MyInvocationHandler(myRealObject);
// 创建动态代理对象
Object proxyInstance = proxyClassConstructor.newInstance(invocationHandler);
((MyServiceA) proxyInstance).executeTestA();
((MyServiceA) proxyInstance).submitTestA();
((MyServiceB) proxyInstance).executeTestB();
((MyServiceB) proxyInstance).submitTestB();
}
}
乍一看还真的挺唬人的,但是无论是哪种写法,底层原理都是一样的,两者没有啥本质区别。
总结
本文介绍的这种四倍秀JDK动态代理写法,和通常我们看见的JDK动态代理写法本质是一样的,但是了解这种写法,能够在阅读一些源码时能反映过来这里使用了动态代理。
转载自:https://juejin.cn/post/7313753156520607755