场景
动态代理是一个比较常见的场景,广泛用于方法增强、AOP(面向切面编程)、测试、监控等场景。市面上常用的动态代理主要是 jdk动态代理和cglib动态代理,javaassist 和 byteBuddy同样可以做动态代理。一般使用jdk动态代理代理方法 适合于代理接口,它不需要引入额外依赖,一般cglib适合于代理没有实现接口的类,性能较好。Javaassist就更加高级了,可以自由生成类字节码,可以自由改变类的行为,Byte Buddy主要是比较灵活,和 javaassist 差不多。我们之前使用Byte Buddy 做了自动生成接口的操作。
jdk动态代理的原理
demo
java
interface MyInterface {
void performAction();
}
class MyInterfaceImpl implements MyInterface {
public void performAction() {
System.out.println("Action performed");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call");
Object result = method.invoke(target, args);
System.out.println("After method call");
return result;
}
}
public class JdkProxyDemo {
public static void main(String[] args) {
MyInterfaceImpl realObject = new MyInterfaceImpl();
MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance(
MyInterfaceImpl.class.getClassLoader(),
new Class[]{MyInterface.class},
new MyInvocationHandler(realObject)
);
proxyInstance.performAction();
}
}
jdk动态代理使用起来很简单,一个接口,n实现类 ,一个handler足够了,他的原理比较简单
java
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
@SuppressWarnings("removal")
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
/*
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
首先是检查handler和验证安全性,然后getProxyConstructor获取代理类的构造函数。
java
private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
// optimization for single interface
if (interfaces.length == 1) {
Class<?> intf = interfaces[0];
if (caller != null) {
checkProxyAccess(caller, loader, intf);
}
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
} else {
// interfaces cloned
final Class<?>[] intfsArray = interfaces.clone();
if (caller != null) {
checkProxyAccess(caller, loader, intfsArray);
}
final List<Class<?>> intfs = Arrays.asList(intfsArray);
return proxyCache.sub(intfs).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
}
}
这个过程也很简单,首先是 单个接口
当只有一个接口时,代码直接使用这个接口(Class<?> intf = interfaces[0];)。
在这种情况下,代理类只需要实现这一个接口,所以处理起来更直接、更简单。
代理缓存(proxyCache)的子映射使用这个单一的接口作为键。首先验证完该类是否可以被代理之后,先去缓存拿取,拿不到则构建
java
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
代理构造器
java
ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
if (!VM.isModuleSystemInited()) {
throw new InternalError("Proxy is not supported until "
+ "module system is fully initialized");
}
if (interfaces.size() > 65535) {
throw new IllegalArgumentException("interface limit exceeded: "
+ interfaces.size());
}
Set<Class<?>> refTypes = referencedTypes(loader, interfaces);
// IAE if violates any restrictions specified in newProxyInstance
validateProxyInterfaces(loader, interfaces, refTypes);
this.interfaces = interfaces;
this.module = mapToModule(loader, interfaces, refTypes);
assert getLoader(module) == loader;
}
首先检查模块是否已经完全初始化,这是j9引入的新特性,模块的概念,模块系统的初始化是启动过程的一个关键部分。它负责设置模块路径,确定哪些模块是可访问的,以及它们如何相互作用。
如果模块系统没有完全初始化,许多基于模块的操作,比如动态代理的创建,都不能正常进行。这是因为代理类可能需要跨越不同模块的边界,访问它们的类或接口。
其次检查接口数量是否大于65535,是否超出限制。
再来就是收集引用类型
java
/*
* Returns all types referenced by all public non-static method signatures of
* the proxy interfaces
*/
private static Set<Class<?>> referencedTypes(ClassLoader loader,
List<Class<?>> interfaces) {
var types = new HashSet<Class<?>>();
for (var intf : interfaces) {
for (Method m : intf.getMethods()) {
if (!Modifier.isStatic(m.getModifiers())) {
addElementType(types, m.getReturnType());
addElementTypes(types, m.getSharedParameterTypes());
addElementTypes(types, m.getSharedExceptionTypes());
}
}
}
return types;
}
其实就是收集接口中有哪些方法,方法中有哪些参数,返回值是什么,异常信息是什么这些云云。这个方法的目的是为动态代理类的创建准备必要的类型信息
再来就是验证代理接口:
java
validateProxyInterfaces(loader, interfaces, refTypes);
非常简单
java
private static void validateProxyInterfaces(ClassLoader loader,
List<Class<?>> interfaces,
Set<Class<?>> refTypes)
{
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.size());
for (Class<?> intf : interfaces) {
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!intf.isInterface()) {
throw new IllegalArgumentException(intf.getName() + " is not an interface");
}
if (intf.isHidden()) {
throw new IllegalArgumentException(intf.getName() + " is a hidden interface");
}
if (intf.isSealed()) {
throw new IllegalArgumentException(intf.getName() + " is a sealed interface");
}
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
ensureVisible(loader, intf);
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(intf, Boolean.TRUE) != null) {
throw new IllegalArgumentException("repeated interface: " + intf.getName());
}
}
for (Class<?> type : refTypes) {
ensureVisible(loader, type);
}
}
验证是不是一个接口,是不是隐藏和密封(15新特性)的接口,在15,引入了隐藏和密封的概念,随后验证接口和之前获取的类型能不能被获取加载。
再来就是完善构建器
java
this.interfaces = interfaces;
this.module = mapToModule(loader, interfaces, refTypes);
assert getLoader(module) == loader;
再来就是构建 构造方法了
java
Constructor<?> build() {
Class<?> proxyClass = defineProxyClass(module, interfaces);
assert !module.isNamed() || module.isOpen(proxyClass.getPackageName(), Proxy.class.getModule());
final Constructor<?> cons;
try {
cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
return cons;
}
java
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
boolean nonExported = false;
/*
* 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; // non-public, final
String pkg = intf.getPackageName();
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
} else {
if (!intf.getModule().isExported(intf.getPackageName())) {
// module-private types
nonExported = true;
}
}
}
if (proxyPkg == null) {
// all proxy interfaces are public and exported
if (!m.isNamed())
throw new InternalError("ununamed module: " + m);
proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName()
: m.getName();
} else if (proxyPkg.isEmpty() && m.isNamed()) {
throw new IllegalArgumentException(
"Unnamed package cannot be added to " + m);
}
if (m.isNamed()) {
if (!m.getDescriptor().packages().contains(proxyPkg)) {
throw new InternalError(proxyPkg + " not exist in " + m.getName());
}
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg.isEmpty()
? proxyClassNamePrefix + num
: proxyPkg + "." + proxyClassNamePrefix + num;
ClassLoader loader = getLoader(m);
trace(proxyName, m, loader, interfaces);
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
try {
Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
null, "__dynamic_proxy__");
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} 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());
}
}
这个方法意思就是创建一个代理类,方法比较长,但是无难度。
java
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
boolean nonExported = false;
首先定义包名访问符和接口是否有接口未被模块导出等变量 一个初始化的操作啊。
java
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL; // non-public, final
String pkg = intf.getPackageName();
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
} else {
if (!intf.getModule().isExported(intf.getPackageName())) {
// module-private types
nonExported = true;
}
}
}
if (proxyPkg == null) {
// all proxy interfaces are public and exported
if (!m.isNamed())
throw new InternalError("ununamed module: " + m);
proxyPkg = nonExported ? PROXY_PACKAGE_PREFIX + "." + m.getName()
: m.getName();
} else if (proxyPkg.isEmpty() && m.isNamed()) {
throw new IllegalArgumentException(
"Unnamed package cannot be added to " + m);
}
if (m.isNamed()) {
if (!m.getDescriptor().packages().contains(proxyPkg)) {
throw new InternalError(proxyPkg + " not exist in " + m.getName());
}
}
他把接口给遍历了啊,注意这里是有单一包名限制的。
遍历传入的接口列表interfaces。对于每个接口intf:
不是公共的,设置为FINAL,获取包名,如果接口的包名与proxyPkg不同,则抛出异常。
else:如果接口是公共的:
if (!intf.getModule().isExported(intf.getPackageName())):如果接口所在的模块没有导出该接口的包,则将nonExported设置为true。
上面的代码主打一个验证,就是杂七杂八的验证。
给代理类起名字
java
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg.isEmpty()
? proxyClassNamePrefix + num
: proxyPkg + "." + proxyClassNamePrefix + num;
获取类加载器并记录跟踪信息,随后生成代理类的字节码并定义类,然后定义代理
java
ClassLoader loader = getLoader(m);
trace(proxyName, m, loader, interfaces);
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
try {
Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
null, "__dynamic_proxy__");
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} 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());
}
重要代码
java
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);:使用ProxyGenerator生成代理类的字节码。
java
static byte[] generateProxyClass(ClassLoader loader,
final String name,
List<Class<?>> interfaces,
int accessFlags) {
ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Path.of(dotToSlash(name.substring(0, i)));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i + 1) + ".class");
} else {
path = Path.of(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); 创建代理生成器,生成字节码:
java
final byte[] classFile = gen.generateClassFile();:通过调用ProxyGenerator对象的generateClassFile方法,生成代理类的字节码,并将其存储在字节数组classFile中。
给字节码中注入方法,先注入基本方法 什么 hashCode 什么 equals 什么 toString ,然后再把接口方法注入进去,然后检查返回参数,随后生成构造方法,其实我们的关注点应该在generateMethod 方法上,这才是灵魂。
java
private byte[] generateClassFile() {
visit(V14, accessFlags, dotToSlash(className), null,
JLR_PROXY, typeNames(interfaces));
/*
* Add proxy methods for the hashCode, equals,
* and toString methods of java.lang.Object. This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
*/
addProxyMethod(hashCodeMethod);
addProxyMethod(equalsMethod);
addProxyMethod(toStringMethod);
/*
* Accumulate all of the methods from the proxy interfaces.
*/
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
if (!Modifier.isStatic(m.getModifiers())) {
addProxyMethod(m, intf);
}
}
}
/*
* For each set of proxy methods with the same signature,
* verify that the methods' return types are compatible.
*/
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
generateConstructor();
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for the Method object
visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
LJLR_METHOD, null, null);
// Generate code for proxy method
pm.generateMethod(this, className);
}
}
generateStaticInitializer();
generateLookupAccessor();
return toByteArray();
}
java
private void addProxyMethod(ProxyMethod pm) {
String sig = pm.shortSignature;
List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
(f) -> new ArrayList<>(3));
sigmethods.add(pm);
}
灵魂方法: 这个方法它实现了将方法调用转发到InvocationHandler的逻辑。
java
private void generateMethod(ClassWriter cw, String className) {
MethodType mt = MethodType.methodType(returnType, parameterTypes);
String desc = mt.toMethodDescriptorString();
int accessFlags = ACC_PUBLIC | ACC_FINAL;
if (method.isVarArgs()) accessFlags |= ACC_VARARGS;
MethodVisitor mv = cw.visitMethod(accessFlags,
method.getName(), desc, null,
typeNames(Arrays.asList(exceptionTypes)));
int[] parameterSlot = new int[parameterTypes.length];
int nextSlot = 1;
for (int i = 0; i < parameterSlot.length; i++) {
parameterSlot[i] = nextSlot;
nextSlot += getWordsPerType(parameterTypes[i]);
}
mv.visitCode();
Label L_startBlock = new Label();
Label L_endBlock = new Label();
Label L_RuntimeHandler = new Label();
Label L_ThrowableHandler = new Label();
List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes);
if (catchList.size() > 0) {
for (Class<?> ex : catchList) {
mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler,
dotToSlash(ex.getName()));
}
mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler,
JL_THROWABLE);
}
mv.visitLabel(L_startBlock);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,
LJLR_INVOCATION_HANDLER);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,
LJLR_METHOD);
if (parameterTypes.length > 0) {
// Create an array and fill with the parameters converting primitives to wrappers
emitIconstInsn(mv, parameterTypes.length);
mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT);
for (int i = 0; i < parameterTypes.length; i++) {
mv.visitInsn(DUP);
emitIconstInsn(mv, i);
codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]);
mv.visitInsn(Opcodes.AASTORE);
}
} else {
mv.visitInsn(Opcodes.ACONST_NULL);
}
mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
"invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
"[Ljava/lang/Object;)Ljava/lang/Object;", true);
if (returnType == void.class) {
mv.visitInsn(POP);
mv.visitInsn(RETURN);
} else {
codeUnwrapReturnValue(mv, returnType);
}
mv.visitLabel(L_endBlock);
// Generate exception handler
mv.visitLabel(L_RuntimeHandler);
mv.visitInsn(ATHROW); // just rethrow the exception
mv.visitLabel(L_ThrowableHandler);
mv.visitVarInsn(ASTORE, 1);
mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX,
"<init>", "(Ljava/lang/Throwable;)V", false);
mv.visitInsn(ATHROW);
// Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
mv.visitMaxs(-1, -1);
mv.visitEnd();
}
核心是 生成代理方法的主体:
加载this和InvocationHandler引用,以及被调用的方法对象。
创建参数对象数组,并将方法参数转换为对象,存储在数组中。
使用InvocationHandler的invoke方法来实现方法的动态代理调用。
加载this和InvocationHandler引用,以及被调用的方法对象:
mv.visitVarInsn(ALOAD, 0);:加载this引用到栈顶。在Java字节码中,ALOAD用于加载一个对象引用到栈顶,而这里的0表示加载位于局部变量表中索引为0的对象,即this引用。
mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName, LJLR_INVOCATION_HANDLER);:获取当前代理实例的InvocationHandler字段,将其压入操作数栈。这里GETFIELD是用于访问对象的字段。
mv.visitVarInsn(ALOAD, 0);:再次加载this引用到栈顶。
mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName, LJLR_METHOD);:获取静态字段,即当前正在执行的Method对象的引用。
我们回来
java
Constructor<?> build() {
Class<?> proxyClass = defineProxyClass(module, interfaces);
assert !module.isNamed() || module.isOpen(proxyClass.getPackageName(), Proxy.class.getModule());
final Constructor<?> cons;
try {
cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
return cons;
}
类都生成了 拿到构造简直是小菜一碟了,后面自然是绕过安全检查,设置构造可见。
java
private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
Constructor<?> cons,
InvocationHandler h) {
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (caller != null) {
checkNewProxyPermission(caller, cons.getDeclaringClass());
}
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);
}
}
然后就示例化了,最复杂的是生成代理类那一块。
生成后代理类伪代码应该是
java
import java.lang.reflect.*;
public class DynamicProxyClass implements HelloInterface {
private InvocationHandler handler;
public DynamicProxyClass(InvocationHandler handler) {
this.handler = handler;
}
@Override
public void performAction() {
try {
Method method = HelloInterface.class.getMethod("performAction");
this.handler.invoke(this, method, null);
} catch (Throwable throwable) {
// 处理异常
}
}
@Override
public String toString() {
try {
Method method = Object.class.getMethod("toString");
return (String) handler.invoke(this, method, null);
} catch (Throwable throwable) {
// 处理异常
return null;
}
}
@Override
public boolean equals(Object obj) {
try {
Method method = Object.class.getMethod("equals", Object.class);
return (Boolean) handler.invoke(this, method, new Object[]{obj});
} catch (Throwable throwable) {
// 处理异常
return false;
}
}
@Override
public int hashCode() {
try {
Method method = Object.class.getMethod("hashCode");
return (Integer) handler.invoke(this, method, null);
} catch (Throwable throwable) {
// 处理异常
return 0;
}
}
}
差不多就这样。
总结
差不多就是 先 接口定义->实现InvocationHandler -> 安全性检查 -> 生成代理类字节码 -> 实例化代理类 不复杂。 比起 javaaasit和bybtBuddy 还是很简单,弟中弟了 属于是。