文章目录
- 一、动手实现一个动态代理框架
- 1、初识javassist
- 2、使用javassist实现一个动态代理框架
- 二、JDK动态代理
- 1、编码实现
- 2、基本原理
- (1)getProxyClass0方法
- (2)总结
- 写在后面
一、动手实现一个动态代理框架
1、初识javassist
Java操纵字节码,最底层一般是使用ASM进行操作的,但是ASM上手难度很大,我们可以使用javassist对字节码进行操作,相对来说简单一些。
下面这个实例,就是我们使用javassist对接口动态生成一个实现类:
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
public class JavassistDemo {
public static void main(String[] args) throws Exception {
TestService proxy = createProxy();
proxy.sayHello("zhangsan"); // hello:zhangsan
}
/**
* 生成一个TestService的实现类
*/
public static TestService createProxy() throws Exception {
// javassist 底层是ASM,ASM底层是编辑JVM指令码
ClassPool classPool = new ClassPool();
// 添加classLoader
classPool.appendSystemPath();
// 1.创建一个类
CtClass class1 = classPool.makeClass("TestServiceImpl");
class1.addInterface(classPool.get(TestService.class.getName()));
// 2.创建一个方法
CtMethod satHelloMethod = CtNewMethod.make(CtClass.voidType, // void返回值
"sayHello", // 方法名
new CtClass[]{classPool.get(String.class.getName())}, // 方法参数
new CtClass[0], // 异常类型
"{System.out.println(\"hello:\"+$1);}", // 方法体内容,$1表示第一个参数
class1 // 指定类
);
class1.addMethod(satHelloMethod);
// 3.实例化这个对象
Class aClass = classPool.toClass(class1);
// 强制转换
return (TestService) aClass.newInstance();
}
public interface TestService {
void sayHello(String name);
}
}
2、使用javassist实现一个动态代理框架
import javassist.*;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Collectors;
public class Javassist3Demo {
public static void main(String[] args) throws Exception {
TestService proxy = createProxy(TestService.class, new InvocationHandler() {
@Override
public Object invoke(String methodName, Object[] args) {
// 根据方法判断逻辑
if(methodName.equals("sayHello3")) {
System.out.println("hello" + args[0]);
return "aa";
} else {
System.out.println("hello2" + args[0]);
return "aa2";
}
}
});
proxy.sayHello("zhangsan"); // hellozhangsan
proxy.sayHello2("zz", 1);
System.out.println(proxy.sayHello3("qq"));
}
static int count = 0;
public static <T> T createProxy(Class<T> classInterface, InvocationHandler handler) throws Exception {
ClassPool classPool = new ClassPool();
classPool.appendSystemPath();
// 1.创建一个类
CtClass impl = classPool.makeClass("$Proxy" + count ++);
impl.addInterface(classPool.get(classInterface.getName()));
// 2.impl类中 添加属性handler
CtField fie = CtField.make("public com.mydemo.Javassast3Demo.InvocationHandler handler=null;", impl);
impl.addField(fie);
// 有返回值类型和无返回值类型的源码
String src = "return ($r)this.handler.invoke(\"%s\", $args);"; // $args获取所有参数
String voidSrc = "this.handler.invoke(\"%s\",$args);";
for (Method method : classInterface.getMethods()) {
CtClass returnType = classPool.get(method.getReturnType().getName());
String name = method.getName();
CtClass[] parameters = toCtClass(classPool, method.getParameterTypes());
CtClass[] errors = toCtClass(classPool, method.getExceptionTypes());
// 2.创建一个方法
CtMethod newMethod = CtNewMethod.make(returnType, // 返回值
name, // 方法名
parameters, // 方法参数
errors, // 异常类型
method.getReturnType().equals(Void.class) ? String.format(voidSrc, method.getName()) : String.format(src, method.getName()), // 方法体内容
impl // 指定类
);
impl.addMethod(newMethod);
}
// 生成字节码(辅助学习用)
//byte[] bytes = impl.toBytecode();
//Files.write(Paths.get(System.getProperty("user.dir") + "/target/" + impl.getName() + ".class"), bytes);
// 3.实例化这个对象
Class aClass = classPool.toClass(impl);
T t = (T) aClass.newInstance();
aClass.getField("handler").set(t, handler); // 初始化赋值
// 强制转换
return t;
}
private static CtClass[] toCtClass(ClassPool pool, Class[] classes) {
return Arrays.stream(classes).map(c -> {
try {
return pool.get(c.getName());
} catch (NotFoundException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList()).toArray(new CtClass[0]);
}
public interface InvocationHandler {
Object invoke(String methodName, Object args[]);
}
public class InvocationHandlerImpl implements InvocationHandler {
@Override
public Object invoke(String methodName, Object[] args) {
System.out.println("hello");
return null;
}
}
public interface TestService {
void sayHello(String name);
void sayHello2(String name, Integer id);
String sayHello3(String name);
}
}
这样,我们只要实现InvocationHandler 接口,就可以自定义代理类的核心逻辑了,我们对生成的代理类进行反编译:
public class $Proxy0 implements TestService {
public InvocationHandler handler = null;
public String sayHello3(String var1) {
return (String)this.handler.invoke("sayHello3", new Object[]{var1});
}
public void sayHello(String var1) {
this.handler.invoke("sayHello", new Object[]{var1});
}
public void sayHello2(String var1, Integer var2) {
this.handler.invoke("sayHello2", new Object[]{var1, var2});
}
public $Proxy0() {
}
}
实现了我们的接口,并且重写了接口的所有方法,最终执行的是InvocationHandler的invoke方法。
这也正是JDK动态代理的基本思想。
二、JDK动态代理
1、编码实现
public interface EchoService {
String echo(String message) throws NullPointerException;
}
public class DefaultEchoService implements EchoService {
@Override
public String echo(String message) {
return "[ECHO] " + message;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理实例
*/
public class JDKDynamicProxyDemo {
public static void main(String[] args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 真实的对象
DefaultEchoService realObj = new DefaultEchoService();
// 代理的对象
Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态前置");
Object obj = null;
if (EchoService.class.isAssignableFrom(method.getDeclaringClass())) {
// 执行真实方法
obj = method.invoke(realObj, args);
}
System.out.println("动态后置");
return obj;
}
});
EchoService echoService = (EchoService) proxy;
System.out.println(echoService.echo("Hello,World"));
}
}
我们发现,使用JDK动态代理,基本逻辑和我们上面使用javassist手写的工具类差不多,只不过JDK动态代理底层是使用更复杂的方式实现的,我们这里取巧,使用javassist实现。
注意!这里需要DefaultEchoService 实现EchoService接口,代理的其实是EchoService接口而不是DefaultEchoService 类。
动态代理对原代码没有侵入性,通常可以动态加载。
2、基本原理
为什么 Proxy.newProxyInstance 会生成新的字节码?
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
System.out.println(proxy.getClass());// com.sun.proxy.$Proxy0
Object proxy2 = Proxy.newProxyInstance(classLoader, new Class[]{Comparable.class}, (proxy1, method, args1) -> {
return null;
});
System.out.println(proxy2.getClass());// com.sun.proxy.$Proxy1
上面代码我们会发现,Java动态代理每生成一个代理,它的class总是com.sun.proxy包下的$Proxy*,从0开始累加,它是如何实现的呢?
我们来分析一下Proxy的newProxyInstance方法:
// java.lang.reflect.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);
}
/*
* Look up or generate the designated proxy class.
*/
// 先从缓存获取(见 (1))
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 获取代理对象的构造方法,带着InvocationHandler参数的构造方法
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;
}
});
}
// 返回Proxy对象
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)getProxyClass0方法
在getProxyClass0方法中,从proxyClassCache缓存中获取了这个代理类:
// java.lang.reflect.Proxy#getProxyClass0
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
而proxyClassCache在初始化时,自动创建了KeyFactory和ProxyClassFactory
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
ProxyClassFactory的核心方法apply,隐藏着代理接口的创建逻辑:
// java.lang.reflect.Proxy.ProxyClassFactory
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) { // 遍历我们传入的接口数组
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
// 通过classLoader加载我们的接口
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {// 只能代理接口,非接口直接抛异常
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) { // 包名就是com.sun.proxy
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num; // 依次递增
/*
* Generate the specified proxy class.
*/
// 代理类生成器,返回字节数组,就是字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// classLoader加载类,是一个native方法,返回一个Class对象
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
(2)总结
vm options参数设置-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,就可以把生成的代理类的源码保存在com.sun.proxy目录下面,或者用arthas来查看运行中的类信息。
JDK动态代理生成的代理类,我们通过反编译,发现其实是这个样子的:
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import com.demo.EchoService;
public final class $Proxy0
extends Proxy
implements EchoService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.demo.EchoService").getMethod("echo", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String echo(String string) throws NullPointerException {
try {
return (String)this.h.invoke(this, m3, new Object[]{string});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
InvocationHandler就是Proxy.newProxyInstance传入的最后一个参数。
当调用代理对象的方法时,会执行InvocationHandler的invoke方法。
注意,JDK生成的代理类的包名不总是com.sun.proxy,只有当接口为Public时是这样的,当接口为非public时,生成的代理类与接口所在包名相同。
写在后面
如果本文对你有帮助,请点赞收藏关注一下吧 ~