动态代理是Java开发中常用的一种设计模式,相比于静态代理方式,动态代理毫无疑问更加灵活,可以在运行时对目标实例进行代理增加,以此对实例方法进行各种增强。在本篇文章中,笔者将介绍如何使用Java字节码操作库Javassist实现动态代理机制。
实现方式
Javassist实现动态代理机制的方式有两种,一种是使用Javassist提供的ProxyFactory实现,实现方式与JDK类似。另一种是使用Javassist操作字节码,自主实现动态代理机制。两种方式各有好处,前者比较简单,容易上手,后者比较复杂,但好在足够灵活,甚至可以复用JDK的InvocationHandler。下面笔者将介绍后者的实现思路!
JDK实现动态代理
在使用Javassist实现动态代理之前,笔者先介绍一下JDK的动态代理是怎么实现的。
首先,使用JDK内置的方法,实现一个动态代理机制,示例代码如下:
public interface Move {
void move();
}
public class Walk implements Move {
@Override
public void move() {
System.out.println("I'm Walking");
}
}
public class DefaultInvocation implements InvocationHandler {
private Object target;
public DefaultInvocation(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("before invoke ");
Object obj = method.invoke(target,args);
System.out.println("after invoke ");
return obj;
}
}
public class Test {
public static void main(String[] args) throws Exception {
//加入这个参数,可以将生成的代理类保存到硬盘
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Object proxy = Proxy.newProxyInstance(Walk.class.getClassLoader(), new Class[]{Move.class},
new DefaultInvocation(new Walk()));
((Move) proxy).move();
}
}
笔者用的是Idea,代码目录如下图所示。
运行,查找到编译之后代理实例的class文件,就是上图所示的$Proxy0.class。反编译一下,得到下面的代码:
package com.sun.proxy;
import cn.bdqfork.proxy.Move;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Move {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void move() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("cn.bdqfork.proxy.Move").getMethod("move");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从反编译的代理实例中可以看出,JDK在编译过程中,生成了一个目标接口的子类,并继承了Proxy类。该子类中将目标代理接口的方法作为属性,在调用相应接口方法时,并不会直接调用代理对象的方法,而是调用了InvocationHandler里面实现的invoke方法。InvocationHandler是交给了用户去实现,因此,在此基础上,用户可以实现各种方法增强。
Javassist实现动态代理
笔者在上文介绍了JDK的动态代理机制,本质上就是生成了一个实现目标接口的子类,并在相应的方法实现中调用InvocationHandler里面的invoke方法。
在Javassist中,提供了一系列的字节码操作API以在运行时灵活的生成需要的类。参考JDK的思路,只要用Javassist动态生成一个类似的代理子类,动态代理就实现了,实现思路如下。
定义ClassGenerator类
为了更好的对代理子类进行描述,定义一个ClassGenerator类,该类记录了一个代理子类的一些信息,包括类名、父类、需要实现的接口、构造函数等,并实现一些添加这些信息的方法。ClassGenerator的定义如下:
package cn.bdqfork.core.aop.proxy;
import javassist.*;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author bdq
* @since 2019/10/1
*/
public class ClassGenerator {
/**
* ClassPool缓存
*/
private static final Map<ClassLoader, ClassPool> POOL_CACHE = new ConcurrentHashMap<>();
/**
* 构造方法名占位符
*/
private static final String INIT_FLAG = "<init>";
/**
* Javassist ClassPool
*/
private ClassPool classPool;
/**
* 全类名
*/
private String className;
/**
* 简单类名
*/
private String simpleName;
/**
* 父类
*/
private String superClass;
/**
* 是否添加默认构造方法
*/
private boolean addDefaultConstructor;
/**
* 需要实现的接口
*/
private List<String> interfaces;
/**
* 构造方法
*/
private List<String> constructors;
/**
* 属性
*/
private List<String> fields;
/**
* 方法,包括实现接口中的所有方法
*/
private List<String> methods;
public ClassGenerator() {
this(null);
}
public ClassGenerator(ClassLoader classLoader) {
if (classLoader == null) {
classPool = ClassPool.getDefault();
} else {
classPool = POOL_CACHE.get(classLoader);
if (classPool == null) {
classPool = new ClassPool(true);
classPool.appendClassPath(new LoaderClassPath(classLoader));
POOL_CACHE.putIfAbsent(classLoader, classPool);
}
}
}
private static String modifier(int modfier) {
StringBuilder modifier = new StringBuilder();
if (Modifier.isPublic(modfier)) {
modifier.append("public");
}
if (Modifier.isProtected(modfier)) {
modifier.append("protected");
}
if (Modifier.isPrivate(modfier)) {
modifier.append("private");
}
if (Modifier.isStatic(modfier)) {
modifier.append(" static");
}
if (Modifier.isVolatile(modfier)) {
modifier.append(" volatile");
}
return modifier.toString();
}
public ClassGenerator setClassName(String className) {
this.className = className;
this.simpleName = className.substring(className.lastIndexOf(".") + 1);
return this;
}
public ClassGenerator setSuperClass(String superClass) {
this.superClass = superClass;
return this;
}
public ClassGenerator addInterface(String interfaceName) {
if (interfaces == null) {
interfaces = new LinkedList<>();
}
interfaces.add(interfaceName);
return this;
}
public ClassGenerator addConstructor(String constructor) {
if (constructors == null) {
constructors = new LinkedList<>();
}
constructors.add(constructor);
return this;
}
public ClassGenerator addConstructor(int modifier, Class<?>[] parameters, String body) {
return addConstructor(modifier, parameters, null, body);
}
/**
* 生成构造方法,最终生成的方法文本示例如下
* modifier <init> (parameters) throws exceptions{
* body
* }
*
* @param modifier 修饰符
* @param parameterTypes 参数
* @param exceptionTypes 异常
* @param body 方法体
* @return ClassGenerator
*/
public ClassGenerator addConstructor(int modifier, Class<?>[] parameterTypes, Class<?>[] exceptionTypes, String body) {
StringBuilder codeBuilder = new StringBuilder();
//添加方法修饰符
codeBuilder.append(modifier(modifier))
.append(" ")
.append(INIT_FLAG)
.append("(");
//添加参数
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) {
codeBuilder.append(",");
}
Class<?> parameter = parameterTypes[i];
codeBuilder.append(parameter.getCanonicalName())
.append(" ")
.append("arg")
.append(i);
}
codeBuilder.append(")");
//判断是否有异常,如果有,添加异常抛出
if (exceptionTypes != null && exceptionTypes.length > 0) {
codeBuilder.append("throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i > 0) {
codeBuilder.append(",");
}
Class<?> exceptionClass = exceptionTypes[i];
codeBuilder.append(exceptionClass.getCanonicalName());
}
}
//添加方法体
codeBuilder.append("{")
.append(body)
.append("}");
return addConstructor(codeBuilder.toString());
}
public ClassGenerator addDefaultConstructor() {
addDefaultConstructor = true;
return this;
}
public ClassGenerator addField(String field) {
if (fields == null) {
fields = new LinkedList<>();
}
fields.add(field);
return this;
}
public ClassGenerator addMethod(String method) {
if (methods == null) {
methods = new LinkedList<>();
}
methods.add(method);
return this;
}
/**
* 生成方法代码文本,生成的示例如下
* modifier returnType methodName(parameters) throws exceptions{
* body
* }
*
* @param modifier 修饰符
* @param returnType 返回类型
* @param methodName 方法名
* @param parameterTypes 参数类型
* @param exceptionTypes 异常类型
* @param body 方法体
* @return ClassGenerator
*/
public ClassGenerator addMethod(int modifier, Class<?> returnType, String methodName, Class<?>[] parameterTypes,
Class<?>[] exceptionTypes, String body) {
StringBuilder methodBuilder = new StringBuilder();
methodBuilder.append(modifier(modifier))
.append(" ")
.append(returnType.getName());
methodBuilder.append(" ").append(methodName).append("(");
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) {
methodBuilder.append(",");
}
methodBuilder.append(parameterTypes[i].getName())
.append(" arg")
.append(i);
}
methodBuilder.append(")");
if (exceptionTypes != null && exceptionTypes.length > 0) {
methodBuilder.append("throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i > 0) {
methodBuilder.append(",");
}
methodBuilder.append(exceptionTypes[i].getName());
}
}
methodBuilder.append("{").append(body).append("}");
addMethod(methodBuilder.toString());
return this;
}
public Class<?> toClass() {
return toClass(ClassGenerator.class.getClassLoader(), ClassGenerator.class.getProtectionDomain());
}
/**
* 构建CtClass并转换为Class返回给调用者
*
* @param classLoader ClassLoader
* @param protectionDomain ProtectionDomain
* @return Class<?>
*/
public Class<?> toClass(ClassLoader classLoader, ProtectionDomain protectionDomain) {
try {
CtClass ctClass = classPool.makeClass(className);
if (superClass != null) {
ctClass.setSuperclass(classPool.get(superClass));
}
if (interfaces != null) {
for (String interfaceName : interfaces) {
ctClass.addInterface(classPool.get(interfaceName));
}
}
if (addDefaultConstructor) {
ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass));
}
if (fields != null) {
for (String field : fields) {
ctClass.addField(CtField.make(field, ctClass));
}
}
if (constructors != null) {
for (String constructor : constructors) {
if (constructor.contains(INIT_FLAG)) {
constructor = constructor.replace(INIT_FLAG, simpleName);
}
ctClass.addConstructor(CtNewConstructor.make(constructor, ctClass));
}
}
if (methods != null) {
for (String method : methods) {
ctClass.addMethod(CtNewMethod.make(method, ctClass));
}
}
return ctClass.toClass(classLoader, protectionDomain);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
设置类名,添加接口名这些属性的方法比较简单,就不做介绍了,着重介绍一下方法的添加。需要注意的是,由于Javassist在生成方法的时候,需要传入方法文本,所以在ClassGenerator的addMethod中存在大量的文本拼接,以生成一个完整的方法。同时在拼接方法文本的时候,需要提取方法的描述符,检测是否有异常抛出等。在设置完代理子类的相关信息之后,在toClass方法中将代理子类编译并加载。
在toClass方法中,使用Javassist提供的字节码操作技术,访问ClassGenerator的属性,构造出一个CtClass,最后调用CtClass的toClass方法,生成一个了Class。如果对该知识不了解,可以查看本人的另一篇博客 Javassist之内省与定制(一) 。
定义Proxy
在定义了ClassGenerator之后,下面来定义Proxy类。ClassGenerator记录了代理子类的信息,并提供了编译代理子类并加载的方法。Proxy的作用就是根据不同的目标代理接口,构造代理子类的信息,将这些信息设置到ClassGenerator中,获取代理实例。参考JDK的实现,Proxy类也是一个抽象类,具体代码如下:
package cn.bdqfork.core.aop.proxy;
import cn.bdqfork.core.utils.ReflectUtils;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
/**
* @author bdq
* @since 2019/9/30
*/
public abstract class Proxy implements Serializable {
private static final Map<String, Object> CACHE = Collections.synchronizedMap(new WeakHashMap<>());
private static final AtomicLong PROXY_COUNTER = new AtomicLong(0);
public Proxy() {
}
/**
* 抽象方法,由代理子类实现,调用代理子类的构造方法生成代理实例
*
* @param handler InvocationHandler
* @return 代理实例
*/
public abstract Object newInstance(InvocationHandler handler);
/**
* 生成代理实例
*
* @param classLoader ClassLoader
* @param interfaces 代理接口
* @param handler InvocationHandler
* @return Object
* @throws IllegalArgumentException 生成失败时抛出
*/
public static Object newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException {
//通过接口名生成KEY来缓存实例
String key = getKey(interfaces);
if (CACHE.containsKey(key)) {
return CACHE.get(key);
}
ClassGenerator generator = new ClassGenerator(classLoader);
for (Class<?> interfaceClass : interfaces) {
generator.addInterface(interfaceClass.getName());
}
//生成代理子类名称,例如Proxy0
String className = Proxy.class.getName() + PROXY_COUNTER.getAndIncrement();
generator.setClassName(className).setSuperClass(Proxy.class.getName());
//与JDK类似,将接口方法作为代理子类的属性
generator.addField("private java.lang.reflect.Method[] methods;");
//将InvocationHandler也作为代理子类的属性
generator.addField("private " + InvocationHandler.class.getName() + " handler;");
//添加构造方法,初始化InvocationHandler
generator.addConstructor(Modifier.PUBLIC, new Class[]{InvocationHandler.class}, "$0.handler=$1;");
//添加默认构造方法
generator.addDefaultConstructor();
//扫描接口,获取所有的接口方法,并通过方法签名进行去重
Set<String> worked = new HashSet<>();
List<Method> methods = new ArrayList<>();
for (Class interfaceClass : interfaces) {
for (Method method : interfaceClass.getMethods()) {
if (worked.contains(ReflectUtils.getSignature(method))) {
continue;
}
worked.add(ReflectUtils.getSignature(method));
methods.add(method);
}
}
//根据接口方法信息,生成代理实例的方法实现
for (int i = 0; i < methods.size(); i++) {
Method method = methods.get(i);
StringBuilder codeBuilder = new StringBuilder();
codeBuilder.append("Object result = $0.handler.invoke($0,$0.methods[").append(i).append("],$args);");
Class<?> returnType = method.getReturnType();
if (!Void.TYPE.equals(returnType)) {
codeBuilder.append("return ").append(castResult("result", returnType));
}
generator.addMethod(Modifier.PUBLIC, returnType, method.getName(),
method.getParameterTypes(), method.getExceptionTypes(), codeBuilder.toString());
}
//添加Proxy抽象方法的实现
generator.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " handler){return new " + className + "($1);}");
try {
Class<?> clazz = generator.toClass();
Proxy proxy = (Proxy) clazz.newInstance();
//生成代理实例
proxy = (Proxy) proxy.newInstance(handler);
//为代理实例的methods属性赋值
Field handlerField = clazz.getDeclaredField("methods");
handlerField.setAccessible(true);
handlerField.set(proxy, methods.toArray(new Method[0]));
CACHE.putIfAbsent(key, proxy);
return proxy;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e.getCause());
}
}
private static String getKey(Class<?>[] interfaces) {
return Arrays.stream(interfaces)
.map(Class::getName)
.collect(Collectors.joining());
}
/**
* 对方法返回值进行转换
*
* @param resultName 返回值名称
* @param returnType 返回类型
* @return
*/
private static String castResult(String resultName, Class<?> returnType) {
if (returnType.isPrimitive()) {
if (Byte.TYPE == returnType) {
return resultName + "==null? (byte)0:((Byte)" + resultName + ").byteValue();";
}
if (Short.TYPE == returnType) {
return resultName + "==null? (short)0:((Short)" + resultName + ").shortValue();";
}
if (Integer.TYPE == returnType) {
return resultName + "==null? (int)0:((Integer)" + resultName + ").intValue();";
}
if (Long.TYPE == returnType) {
return resultName + "==null? (long)0:((Long)" + resultName + ").longValue();";
}
if (Float.TYPE == returnType) {
return resultName + "==null? (float)0:((Float)" + resultName + ").floatValue();";
}
if (Double.TYPE == returnType) {
return resultName + "==null? (double)0:((Double)" + resultName + ").doubleValue();";
}
if (Character.TYPE == returnType) {
return resultName + "==null? (char)0:((Character)" + resultName + ").charValue();";
}
if (Boolean.TYPE == returnType) {
return resultName + "==null? false:((Boolean)" + resultName + ").booleanValue();";
}
throw new RuntimeException("Unknow primitive " + returnType.getCanonicalName() + " !");
}
return "(" + returnType.getCanonicalName() + ")" + resultName + ";";
}
}
注意到在扫描接口方法时,获取了方法签名进行去重。对于Java方法,方法的签名一般是由方法修饰符,方法名,方法参数作为签名。获取方法签名的代码如下:
public static String getSignature(Method method) {
StringBuilder signBuilder = new StringBuilder();
signBuilder.append(method.getName())
.append("(");
Class<?>[] parameters = method.getParameterTypes();
for (int i = 0; i < parameters.length; i++) {
if (i > 0) {
signBuilder.append(",");
}
signBuilder.append(parameters[i].getName());
}
signBuilder.append(")");
return signBuilder.toString();
}
至此,使用Javassist实现动态代理机制的代码介绍完毕,使用方式和JDK一模一样,区别只是Proxy的不同。
笔者将上述的代码使用在了本人自己写的IOC容器中,使用过程中暂时没有发现什么问题,完整代码可以查看本人的IOC容器代码。点击访问 https://github.com/bdqfork/spring-toy ,Proxy代码在spring-toy-core/src/main/java/cn/bdqfork/core/aop/proxy目录下。